1 $: << File.expand_path(File.dirname(__FILE__) + '/..')
16 alias :kilobyte :kilobytes
21 alias :megabyte :megabytes
26 alias :gigabyte :gigabytes
31 alias :terabyte :terabytes
36 alias :petabyte :petabytes
41 alias :exabyte :exabytes
48 def number_to_human_size(size, precision=1)
49 size = Kernel.Float(size)
51 when size.to_i == 1; "1 Byte"
52 when size < 1.kilobyte; "%d Bytes" % size
53 when size < 1.megabyte; "%.#{precision}f KB" % (size / 1.0.kilobyte)
54 when size < 1.gigabyte; "%.#{precision}f MB" % (size / 1.0.megabyte)
55 when size < 1.terabyte; "%.#{precision}f GB" % (size / 1.0.gigabyte)
56 else "%.#{precision}f TB" % (size / 1.0.terabyte)
57 end.sub(/([0-9])\.?0+ /, '\1 ' )
68 inject(0) { |i, s| s += i }
72 sort_by{ rand }.each &block
76 class ServerTestResults
77 def self.open(filename)
78 if File.readable?(filename)
79 new(Marshal.load(File.read(filename)))
85 def initialize(results = [])
89 def write(filename='results.dump')
90 puts "writing dump file to #{filename}"
91 File.open(filename, 'w+') do |f|
92 f.write Marshal.dump(@results)
105 @results.map {|r| r[:server] }.uniq.sort
108 def data(server, what=:size)
109 server_data = @results.find_all { |r| r[:server] == server }
110 ticks = server_data.map { |d| d[what] }.uniq
113 measurements = server_data.find_all { |d| d[what] == c }.map { |d| d[:rps] }
114 datas << [c, measurements.avg]
122 attr_reader :name, :port, :app, :pid
123 def initialize(name, port, &start_block)
126 @start_block = start_block
134 Process.kill('KILL', @pid)
142 puts "Starting #{name}"
143 @pid = fork { @start_block.call }
146 def trial(options = {})
147 concurrency = options[:concurrency] || 50
148 size = options[:size] || 20 * 1.kilobyte
149 requests = options[:requests] || 500
151 print "#{@name} (c=#{concurrency},s=#{size}) "
153 r = %x{ab -t 3 -q -c #{concurrency} http://0.0.0.0:#{@port}/bytes/#{size}}
154 # Complete requests: 1000
156 return nil unless r =~ /Requests per second:\s*(\d+\.\d\d)/
158 if r =~ /Complete requests:\s*(\d+)/
159 completed_requests = $1.to_i
161 puts "#{rps} req/sec (#{completed_requests} completed)"
165 :concurrency => concurrency,
168 :requests => requests,
169 :requests_completed => completed_requests,
174 def wait_trial(wait, concurrency = 50)
176 print "#{@name} (c=#{concurrency},wait=#{wait}) "
178 r = %x{ab -t #{wait*3} -q -c #{concurrency} http://0.0.0.0:#{@port}/periodical_activity/fibonacci/#{wait}}
179 # Complete requests: 1000
181 return nil unless r =~ /Requests per second:\s*(\d+\.\d\d)/
183 if r =~ /Complete requests:\s*(\d+)/
184 completed_requests = $1.to_i
186 puts "#{rps} req/sec (#{completed_requests} completed)"
190 :concurrency => concurrency,
193 :requests_completed => completed_requests,
199 def post_trial(size = 1, concurrency = 10)
201 print "#{@name} (c=#{concurrency},posting=#{size}) "
204 fn = "/tmp/ebb_post_trial_#{size}"
205 unless FileTest.exists?(fn)
206 File.open(fn, 'w+') { |f| f.write("C"*size) }
209 r = %x{ab -t 6 -q -c #{concurrency} -p #{fn} http://0.0.0.0:#{@port}/test_post_length}
211 return nil unless r =~ /Requests per second:\s*(\d+\.\d\d)/
213 if r =~ /Complete requests:\s*(\d+)/
214 completed_requests = $1.to_i
216 puts "#{rps} req/sec (#{completed_requests} completed)"
220 :concurrency => concurrency,
223 :requests_completed => completed_requests,
232 $servers << ServerTest.new('evented mongrel', 4001) do
234 require 'swiftcore/evented_mongrel'
236 Rack::Handler::Mongrel.run(app, :Port => 4001)
239 $servers << ServerTest.new('ebb', 4002) do
240 require File.dirname(__FILE__) + '/../ruby_lib/ebb'
241 server = Ebb::Server.new(app, :port => 4002)
245 $servers << ServerTest.new('mongrel', 4003) do
247 Rack::Handler::Mongrel.run(app, :Port => 4003)
250 $servers << ServerTest.new('thin', 4004) do
252 Rack::Handler::Thin.run(app, :Port => 4004)