$:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..','ext','revdispatch')) $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..','lib')) $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '.')) require 'evdispatch' require 'test_server' require 'net/http' require 'open-uri' # test the currency load class LoadTest TEST_HOST='http://127.0.0.1' TEST_PATH='/delay/0.1' EXPECTED_RESPONSE='delayed 0.1 seconds' def initialize(trials) @trials = trials @trial = 0 @reports = {} @d = Evdispatch::Loop.new @d.start # start up a few of these to load balance for concurrency @ports = [4044,4045,4046,4047,4048,4049] @ports.each {|p| TestServer.setup(p,false) } sleep(0.5) # wait for the server to start up @port = 0 # warm up the test servers @ports.each do|p| assert_equal( EXPECTED_RESPONSE, open(request_url).read ) end end def port p = @ports[@port] if( (@port + 1) < @ports.size ) @port += 1 else @port = 0 end p end # round robin requests def request_url TEST_HOST + ":#{port}" + TEST_PATH end # see: http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/ def variance(population) n = 0 mean = 0.0 s = 0.0 population.each { |x| n = n + 1 delta = x - mean mean = mean + (delta / n) s = s + delta * (x - mean) } # if you want to calculate std deviation # of a sample change this to "s / (n-1)" return s / (n-1) end # calculate the standard deviation of a population # accepts: an array, the population # returns: the standard deviation def standard_deviation(population) Math.sqrt(variance(population)) end def report( image_path = "benchmark100.png", font_path="/usr/share/fonts/bitstream-vera/Vera.ttf" ) require 'gruff' extras = {} g = Gruff::Line.new(1024) g.title = "Parallel HTTP Request Benchmark" g.font = font_path if font_path # my linux system's font @reports.each do|key,values| total = 0 values.each {|t| total += t } average = total / values.size std = standard_deviation(values) g.data(key,values) puts "#{key}: #{average} seconds (#{std})" extras[key] = {:std => std, :average => average} end labels = {} @trials.each_with_index do|t,index| labels[index] = t.to_s end g.labels = labels g.write(image_path) end def assert_equal( s1, s2 ) raise "Error response not matching expected library failure" if s1 != s2 end def assert_not_nil( v ) raise "Error: value should not be nil!" if v.nil? end def test_curb_multiple_ruby_threads(trials) require 'rubygems' require 'curb' timer = Time.now trials.times do threads = [] threads << Thread.new do c = Curl::Easy.new(request_url) c.perform assert_equal( EXPECTED_RESPONSE, c.body_str ) end threads.each {|t| t.join } end duration = Time.now - timer puts "Running curb( multi threaded ): #{trials} in #{duration} seconds" @reports[:curb_multiple] ||= [] @reports[:curb_multiple] << duration rescue LoadError => e puts "install gem curb: #{e.message}" end def test_nethttp_single(trials) require 'net/http' require 'open-uri' timer = Time.now trials.times do assert_equal( EXPECTED_RESPONSE, open(request_url).read ) end duration = Time.now - timer puts "Running net/http( single threaded ): #{trials} in #{duration} seconds" @reports[:net_http_single] ||= [] @reports[:net_http_single] << duration end def test_nethttp_multithreaded(trials) require 'net/http' require 'open-uri' timer = Time.now threads = [] queue = Queue.new trials.times do|i| threads << Thread.new(i) do|ci| queue << [ci,open(request_url).read] end end threads.each do |t| t.join assert_equal( EXPECTED_RESPONSE, queue.pop.last ) end duration = Time.now - timer puts "Running net/http( multi threaded ): #{trials} in #{duration} seconds" @reports[:net_http_muli] ||= [] @reports[:net_http_muli] << duration end def test_evdispatch_polling(trials) timer = Time.now requests = [] trials.times do requests << @d.request( request_url ) end requests.each do|id| response = nil while response.nil? response = @d.response( id ) end assert_not_nil( response ) assert_equal( EXPECTED_RESPONSE, response[:body] ) end duration = Time.now - timer puts "Running evdispatch: #{trials} in #{duration} seconds" @reports[:evdispatch] ||= [] @reports[:evdispatch] << duration end def run @trials.each do|trial| test_nethttp_multithreaded(trial) end @trials.each do|trial| test_evdispatch_polling(trial) end @trials.each do|trial| test_curb_multiple_ruby_threads(trial) end end end test = LoadTest.new([10,20,50])#,80,100,200,300,400]) test.run test.report('benchmark10.png',nil)