$: << File.expand_path(File.dirname(__FILE__)) require 'rubygems' require 'rack' require 'application' class Array def mean @mean ||= sum / length end def variance @variance ||= map { |x| (mean - x)**2 }.sum / length end def stdd @stdd ||= Math.sqrt(variance) end def errors map { |x| (mean - x).abs } end def center_avg return mean if stdd < 10 acceptable_error = 0 good_points = [] while good_points.empty? acceptable_error += stdd good_points = select { |x| (mean - x).abs < acceptable_error } end good_points.mean end def sum inject(0.to_f) { |i, s| s += i } end def rand_each(&block) sort_by{ rand }.each &block end end class ServerTestResults def self.open(filename) if File.readable?(filename) new(Marshal.load(File.read(filename))) else new end end attr_reader :results def initialize(results = []) @results = results end def benchmark @results.first[:benchmark] end def write(filename='results.dump') puts "writing dump file to #{filename}" File.open(filename, 'w+') do |f| f.write Marshal.dump(@results) end end def <<(r) @results << r end def length @results.length end def servers @results.map {|r| r[:server] }.uniq.sort end def data(server) server_data = @results.find_all { |r| r[:server] == server } ticks = server_data.map { |d| d[:input] }.uniq datas = [] ticks.each do |c| measurements = server_data.find_all { |d| d[:input] == c }.map { |d| d[:rps] } datas << [c, measurements.mean] unless measurements.empty? end datas end end class ServerTest attr_reader :name, :port, :app, :pid def initialize(name, port, &start_block) @name = name @port = port.to_i end def <=>(a) @name <=> a.name end def kill Process.kill('KILL', @pid) if @pid end def running? !@pid.nil? end def start puts "Starting #{name}" case name when 'emongrel' @pid = fork { start_emongrel } when 'ebb_threaded' @pid = fork { start_ebb_threaded } when 'ebb_sequential' @pid = fork { start_ebb_sequential } when 'mongrel' @pid = fork { start_mongrel } when 'thin' @pid = fork { start_thin } when 'fcgi' @pid = fork { start_fcgi } end end def app SimpleApp.new end def start_emongrel require 'mongrel' require 'swiftcore/evented_mongrel' ENV['EVENT'] = "1" Rack::Handler::Mongrel.run(app, :Host => '0.0.0.0', :Port => @port.to_i) end def start_ebb_threaded require File.dirname(__FILE__) + '/../ruby_lib/ebb' server = Ebb::start_server(app, :port => @port, :threaded_processing => true) end def start_ebb_sequential require File.dirname(__FILE__) + '/../ruby_lib/ebb' server = Ebb::start_server(app, :port => @port, :threaded_processing => false) end def start_mongrel require 'mongrel' ENV.delete('EVENT') Rack::Handler::Mongrel.run(app, :Port => @port) end def start_thin require 'thin' Rack::Handler::Thin.run(app, :Port => @port) end def start_fcgi Rack::Handler::FastCGI.run(app, :Port => @port) end def trial(ab_cmd) cmd = ab_cmd.sub('PORT', @port.to_s) puts "#{@name} (#{cmd})" r = %x{#{cmd}} return nil unless r =~ /Requests per second:\s*(\d+\.\d\d)/ rps = $1.to_f if r =~ /Complete requests:\s*(\d+)/ requests_completed = $1.to_i end if r =~ /Failed requests:\s*(\d+)/ failed_requests = $1.to_i else raise "didn't get how many failed requests from ab" end puts " #{rps} req/sec (#{requests_completed} completed, #{failed_requests} failed)" { :server => @name, :rps => rps, :requests_completed => requests_completed, :requests_failed => failed_requests, :ab_cmd => cmd } end end