Skip to content

Commit

Permalink
More benchmark tools
Browse files Browse the repository at this point in the history
* bench_atomic_1 is used to measure 1 combination
* graph_atomic is used to run series, using bench_atomic_1

The idea is to calculate series by varying one parameter
which gives much more meaninful results.
  • Loading branch information
Jonas Pfenniger committed Sep 9, 2011
1 parent 8171865 commit 3477f07
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 0 deletions.
138 changes: 138 additions & 0 deletions examples/bench_atomic_1.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env ruby

$: << File.expand_path('../../lib', __FILE__)

require 'optparse'
require 'thread'
require 'benchmark'

require 'atomic'

Thread.abort_on_exception = true

$conf = {
:lock => "atomic",
:num_threads => 100,
:count => 100_000,
:count_per_thread => nil,
:slow => nil,
}

OptionParser.new do |opts|
opts.on("-c", "--count NUM") do |n|
$conf[:count] = n.to_i
end
opts.on("-p", "--count-per-thread") do |n|
$conf[:count_per_thread] = n.to_i
end
opts.on("-t", "--num-threads NUM") do |n|
$conf[:num_threads] = n.to_i
end
opts.on("-s", "--slow NUM") do |n|
$conf[:slow] = n.to_i
end
opts.on("-l", "--lock atomic|mutex") do |x|
$conf[:lock] = x
end
opts.on("-h", "--help"){ puts opts; exit }
end.parse!(ARGV)

unless $conf[:count_per_thread]
$conf[:count_per_thread] = $conf[:count] / $conf[:num_threads]
end
$conf.delete(:count)

if $conf[:slow].to_i > 0
require 'digest/md5'
def slow_down
$conf[:slow].times do |i|
Digest::MD5.hexdigest(i.to_s)
end
end

ret = []
10.times do
m = Benchmark.measure{ slow_down }
ret << m.real
end

$conf[:slow_time] = [ret.min, ret.max]
else
def slow_down; end
end

$stderr.puts $conf.inspect

def para_prepare(&block)
num_threads = $conf[:num_threads]
count = $conf[:count_per_thread]

if num_threads % 2 > 0
raise ArgumentError, "num_threads must be a multiple of two"
end

# Keep those threads together
tg = ThreadGroup.new

num_threads.times do |i|
diff = (i % 2 == 0) ? 1 : -1

t = Thread.new do
nil until $go
count.times do
yield diff
end
end

tg.add(t)
end

# Make sure all threads are started
while tg.list.find{|t| t.status != "run"}
Thread.pass
end

# For good measure
GC.start

$go = false

tg
end



$tg = nil
if $conf[:lock] == "atomic"
$atom = Atomic.new(0)
$tg = para_prepare do |diff|
$atom.update do |x|
slow_down
x + diff
end
end
else
$lock = Mutex.new
$value = 0
$tg = para_prepare do |diff|
$lock.synchronize do
slow_down
$value += diff
end
end
end


# Run !
#
# NOTE: It seems to me that this measurement method
# is sensible to how the system dispatches his resources.
#
# More precise caluclation could be done using
# getrusage's times
ret = Benchmark.measure do
$go = true
$tg.list.each{|t| t.join}
$go = false
end
puts ret.real
68 changes: 68 additions & 0 deletions examples/graph_atomic_bench.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env ruby
require 'optparse'

conf = {
:vary => "threads",
:lock => "atomic"
}

OptionParser.new do |opts|
opts.on("-l", "--lock atomic|mutex") do |l|
conf[:lock] = l
end
opts.on("-v", "--vary threads|speed") do |v|
conf[:vary] = v
end
opts.on("-h", "--help"){ puts opts; exit }
end.parse!(ARGV)

result = File.open("results_#{conf[:lock]}_#{conf[:vary]}.csv", "w")


if conf[:vary] == "threads"
# Vary the number of concurrent threads that update the value.
#
# There is a total count of 1mio updates that is distributed
# between the number of threads.
#
# A pair number of threads is used so that even add and odd substract 1.
# This avoid creating instances for Bignum since the number should
# stay in the Fixnum range.
#
(1..100).each do |i|
i = i * 2

ret = []
10.times do
ret << `ruby ./bench_atomic_1.rb -l #{conf[:lock]} -t #{i}`.to_f
end

line = ([i] + ret).join(', ')

puts line
result.puts line
end
elsif conf[:vary] == "speed"
# Varies the execution time of the update block
# by using long calulation (MD5)
#
# NOTE: Thread.pass and sleep() are not usable by the atomic
# lock. It needs to run the whole block without hitting
# another atomic update otherwise it has to retry
#
# The expected result is that the atomic lock's performance
# will hit a certain threshold where it will be worse than mutexes.
#
(1..30).each do |i|

ret = []
10.times do
ret << `ruby ./bench_atomic_1.rb -l #{conf[:lock]} -s #{i}`.to_f
end

line = ([i] + ret).join(', ')

puts line
result.puts line
end
end

0 comments on commit 3477f07

Please sign in to comment.