#!/usr/bin/env ruby iter = Hash.new(100) options = Hash.new("") timings = {} gc_stats = {} keys = [] configs = [] narrow = ARGV.delete('-narrow') || ARGV.delete('-n') skip_urls = ARGV.delete('-skip_urls') || ARGV.delete('-s') if ARGV.length<2 print "perfcomp: not enough arguments!\n" print "usage: perf_comp [-n[arrow]] [-s[kip_urls]] file1 file2\n" exit 1 end ARGV.each do |arg| if /^(\d+)$/===arg iter = Hash.new($1.to_i) next end fn = arg fn = fn.sub(/^\/([cdefgh])(\/)/, '\1:\2') if RUBY_PLATFORM =~ /win32/ file = nil begin if File.stat(fn).readable? file = File.open(fn) else print "file #{fn} is unreadable\n" exit 1 end rescue print "file #{fn} does not exist\n" exit 1 end sysvers = fn.split('/').last.sub(/\.txt$/, '') configs << sysvers timings[sysvers] = {} gc_stats[sysvers] = [] file.each_line do |l| case l when /^(.*)\s+([\d\.]+)\s+([\d\.]+)\s+([\d\.]+)\s+\(\s*([\d\.]+)\s*\)$/ k = $1.strip keys << k if timings[sysvers][k] timings[sysvers][k] << $5.to_f else timings[sysvers][k] = [$5.to_f] end when /^.*perf_([a-zA-Z.]+)\s+(\d+)\s+(.*)$/ iter[sysvers] = $2.to_i options[sysvers] = $3 when /^GC.collections=(\d+), GC.time=([\d\.]+)$/ gc_stats[sysvers] << [$1.to_i,$2.to_f] end end file.close end def sum_a(a) a.inject(0.0){|r,v| r += v } end def time_avg(a) sum_a(a)/a.length end def truncate(text, length = 32, truncate_string = "...") return "" if text.nil? return text if text.length <= length n = length - truncate_string.length text[0..(n-1)] + truncate_string end keys.uniq! printf "perf data file 1: #{configs[0]}\n" printf " requests=#{iter[configs[0]]}, options=#{options[configs[0]]}\n\n" printf "perf data file 2: #{configs[1]}\n" printf " requests=#{iter[configs[1]]}, options=#{options[configs[1]]}\n\n" if narrow printf "%-3s %9s %9s %7s %7s %8s %8s %6s\n", 'page', 'c1 real', 'c2 real', 'c1 r/s', 'c2 r/s', 'c1 ms/r', 'c2 ms/r', 'c1/c2' else printf "%-32s %9s %9s %7s %7s %8s %8s %6s\n", 'page', 'c1 real', 'c2 real', 'c1 r/s', 'c2 r/s', 'c1 ms/r', 'c2 ms/r', 'c1/c2' end iter1 = iter[configs[0]] iter2 = iter[configs[1]] if iter10 collection_counts_1 = gc_stats[configs[0]].map{|p| p[0]} collection_times_1 = gc_stats[configs[0]].map{|p| p[1]} c1 = time_avg(collection_counts_1) t1 = time_avg(collection_times_1) collection_counts_2 = gc_stats[configs[1]].map{|p| p[0]} collection_times_2 = gc_stats[configs[1]].map{|p| p[1]} c2 = time_avg(collection_counts_2) t2 = time_avg(collection_times_2) factor = (c1-c2==0) ? 1 : c1/c2 if narrow printf "\n%-4s %9s %9s %7s %7s %8s %8s %6s\n", "GC:", "c1 real", "c2 real", "c1 #gc", "c2 #gc", "c1 gc%", "c2 gc%", "c1/c2" printf "%-4s %9.5f %9.5f %7.1f %7.1f %8.2f %8.2f %6.2f\n", "", t1, t2, c1, c2, (t1/total_time1)*100, (t2/total_time2)*100, factor else printf "\n%-32s %9s %9s %7s %7s %8s %8s %6s\n", "garbage collection stats", "c1 real", "c2 real", "c1 #gc", "c2 #gc", "c1 gc%", "c2 gc%", "c1/c2" printf "%-32s %9.5f %9.5f %7.1f %7.1f %8.2f %8.2f %6.2f\n", "", t1, t2, c1, c2, (t1/total_time1)*100, (t2/total_time2)*100, factor end end if narrow and !skip_urls puts "\nurls:" keys.each_with_index do |k, index| printf "%2d: %s\n", (index+1) , k end end __END__ ### Local Variables: *** ### mode:ruby *** ### End: *** # Copyright (C) 2005, 2006 Stefan Kaes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA