require 'benchmark' require 'thread' require_relative 'atomic_linked_queue' require_relative 'two_lock_linked_queue' thread_count = 50 iterations = 10_000 queue_klasses = [Queue, TwoLockLinkedQueue, AtomicLinkedQueue] Thread.abort_on_exception = true # this one tells all the threads when to start $go = false def setup(queue, writer_thread_count, reader_thread_count, iterations) tg = ThreadGroup.new # spawn writer threads writer_thread_count.times do t = Thread.new do # wait until the bm starts to do the work. This should # minimize variance. nil until $go iterations.times do queue.push('item') end end tg.add(t) end # spawn reader threads if queue.class == Queue # the Queue class gets special behaviour because its #pop # method is blocking by default. reader_thread_count.times do t = Thread.new do nil until $go iterations.times do begin queue.pop(:nonblocking) rescue end end end tg.add(t) end else reader_thread_count.times do t = Thread.new do nil until $go iterations.times do queue.pop end end tg.add(t) end end tg end def exercise(tg) $go = true tg.list.each(&:join) $go = false end 3.times do queue_klasses.each do |klass| Benchmark.bm(50) do |bm| queue = klass.new tg = setup(queue, thread_count, (thread_count * 0.6).to_i, iterations) bm.report("#{klass} - more writers than readers") { exercise(tg) } queue = klass.new tg = setup(queue, (thread_count * 0.6).to_i, thread_count, iterations) bm.report("#{klass} - more readers than writers") { exercise(tg) } queue = klass.new tg = setup(queue, thread_count, thread_count, iterations) bm.report("#{klass} - equal writers and readers") { exercise(tg) } end end end