#!/usr/bin/ruby require 'pp' Dir.chdir(ARGV.shift || raise(ArgumentError, "Need a directory")) ROOTDIR=Dir.pwd LANGUAGES = %w{apache erlang python ruby} REQUESTS = %w{1kb 10kb 100kb 1mb grab1kb grab10kb grab100kb grab1mb ackermann} NUM_REQUESTS = [100, 1_000, 10_000, 100_000] NUM_TRIALS = (1..10).to_a CONCURRENCY = ["serial"] + (1..5).to_a def typical_regexp(str) /#{str}:\s+([[:digit:].]+)/ end # shorter name alias :t :typical_regexp class Symbol def <=>(a) # fuuuuugly hack self.to_s.<=>(a.to_s) end end # Helpful methods for statistical analysis class Array def sum self.inject(0) { |a,b| a += b } end def avg if self.size == 0 0 else self.sum/self.size end end def stddev m = self.avg var = self.inject(0) { |v, x| v += (x-m)**2 } if self.size == 0 0 else Math.sqrt(var/self.size) end end end SUMMARY_DATA = { :time => t("Time taken for tests"), :complete => t("Complete requests"), :failed => t("Failed requests"), :werr => t("Write errors"), :rps => t("Requests per second:"), :tpr => /Time per request:\s+([[:digit:].]+) \[ms\] \(mean\)/, :tpr_mean => /Time per request:\s+([[:digit:].]+) \[ms\] \(mean, across all concurrent requests\)/, :rate => t("Transfer rate"), } SUMMARY = SUMMARY_DATA.keys.sort + [:valid_trials, :std_dev] SUMMARY_AVG = [:tpr, :tpr_mean, :rate] summary = { =begin Intended to structure somewhat like this: :erlang => { :time => (average of all 10 results) (etc) } :ruby => (etc) =end } OVERALL = [:time, :complete, :failed, :werr].sort overall = { =begin Intended to be structured like this: :time => (cumulative over all, cleared per lang) (etc) =end } def result_filename(lang, file, con, reqs) "#{lang}_#{file}_c#{con}_n#{reqs}" end def log_filename(lang, file, con, reqs, trial) result_filename(lang, file, con, reqs) + "_t#{trial}.log" end process_file = lambda do |fn, lang| time = 0 File.open(fn) do |io| io.each do |line| SUMMARY_DATA.each do |k, reg| next unless reg if line =~ reg summary[lang][k] += Float($1) time = Float($1) if k == :time # return the time end end end end time end def output_results(fn, key, result) File.open(fn, "w") do |f| f.puts key.join("\t") f.puts key.collect{|k| "#{result[k]}"}.join("\t") end end LANGUAGES.each do |lang| # This is different than in test.rb because I did analysis on a different system Dir.chdir("#{lang}-rawdata") overall[lang] = Hash.new(0.0) REQUESTS.each do |request| req = Hash.new(0.0) req[:valid_trials] = 0 NUM_REQUESTS.each do |numreqs| CONCURRENCY.each do |con| con = (con*numreqs)/5 if con != "serial" #puts "= new hash =" time = [] summary[lang] = Hash.new(0.0) summary[lang][:valid_trials] = 0 rfn = result_filename(lang, request, con, numreqs) + ".summary" NUM_TRIALS.each do |trial| fn = log_filename(lang, request, con, numreqs, trial) next unless File.exists? fn summary[lang][:valid_trials] += 1 # puts "About to open #{log_filename(lang, request, con, numreqs, trial)}" time << process_file.call(fn,lang) # useful things, those side effects end OVERALL.each do |k| overall[lang][k] += summary[lang][k] req[k] += summary[lang][k] end #average things out for display purposes SUMMARY_AVG.each{|n| summary[lang][n] /= summary[lang][:valid_trials]} summary[lang][:std_dev] = time.stddev output_results(rfn, SUMMARY, summary[lang]) unless summary[lang][:complete] == 0 req[:valid_trials] += summary[lang][:complete] end end # average request time req[:time] /= req[:valid_trials] output_results("#{lang}-#{request}.overall", OVERALL, req) unless req[:complete] == 0 end output_results("#{lang}.overall", OVERALL, overall[lang]) Dir.chdir(ROOTDIR) end