2007-11-16 04:30:29 +03:00
|
|
|
#
|
|
|
|
# Ruby Benchmark driver
|
|
|
|
#
|
|
|
|
|
2007-11-21 12:03:52 +03:00
|
|
|
first = true
|
|
|
|
|
|
|
|
begin
|
|
|
|
require 'optparse'
|
|
|
|
rescue LoadError
|
|
|
|
if first
|
|
|
|
first = false
|
|
|
|
$:.unshift File.join(File.dirname(__FILE__), '../lib')
|
|
|
|
retry
|
|
|
|
else
|
|
|
|
raise
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-11-16 04:30:29 +03:00
|
|
|
require 'benchmark'
|
|
|
|
require 'pp'
|
|
|
|
|
|
|
|
class BenchmarkDriver
|
|
|
|
def self.benchmark(opt)
|
|
|
|
driver = self.new(opt[:execs], opt[:dir], opt)
|
|
|
|
begin
|
|
|
|
driver.run
|
|
|
|
ensure
|
|
|
|
driver.show_results
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def output *args
|
|
|
|
puts(*args)
|
|
|
|
@output and @output.puts(*args)
|
|
|
|
end
|
|
|
|
|
|
|
|
def message *args
|
|
|
|
output(*args) if @verbose
|
|
|
|
end
|
|
|
|
|
|
|
|
def message_print *args
|
|
|
|
if @verbose
|
|
|
|
print(*args)
|
|
|
|
STDOUT.flush
|
|
|
|
@output and @output.print(*args)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def progress_message *args
|
|
|
|
unless STDOUT.tty?
|
2011-05-15 15:55:52 +04:00
|
|
|
STDERR.print(*args)
|
2007-11-16 04:30:29 +03:00
|
|
|
STDERR.flush
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize execs, dir, opt = {}
|
|
|
|
@execs = execs.map{|e|
|
|
|
|
e.strip!
|
|
|
|
next if e.empty?
|
|
|
|
|
|
|
|
if /(.+)::(.+)/ =~ e
|
|
|
|
# ex) ruby-a::/path/to/ruby-a
|
2012-11-26 13:19:00 +04:00
|
|
|
label = $1.strip
|
|
|
|
path = $2
|
|
|
|
version = `#{path} -v`.chomp
|
2007-11-16 04:30:29 +03:00
|
|
|
else
|
2012-11-26 13:19:00 +04:00
|
|
|
path = e
|
|
|
|
version = label = `#{path} -v`.chomp
|
2007-11-16 04:30:29 +03:00
|
|
|
end
|
2012-11-26 13:19:00 +04:00
|
|
|
[path, label, version]
|
2007-11-16 04:30:29 +03:00
|
|
|
}.compact
|
|
|
|
|
|
|
|
@dir = dir
|
|
|
|
@repeat = opt[:repeat] || 1
|
|
|
|
@repeat = 1 if @repeat < 1
|
|
|
|
@pattern = opt[:pattern] || nil
|
2012-10-24 12:46:01 +04:00
|
|
|
@exclude = opt[:exclude] || nil
|
2007-11-16 04:30:29 +03:00
|
|
|
@verbose = opt[:quiet] ? false : (opt[:verbose] || false)
|
|
|
|
@output = opt[:output] ? open(opt[:output], 'w') : nil
|
2014-05-07 06:57:11 +04:00
|
|
|
@rawdata_output = opt[:rawdata_output] ? open(opt[:rawdata_output], 'w') : nil
|
2007-11-16 04:30:29 +03:00
|
|
|
@loop_wl1 = @loop_wl2 = nil
|
2012-10-19 05:22:08 +04:00
|
|
|
@ruby_arg = opt[:ruby_arg] || nil
|
2007-11-16 04:30:29 +03:00
|
|
|
@opt = opt
|
2015-03-05 03:14:02 +03:00
|
|
|
@name_width = 4 # "name".size
|
2007-11-16 04:30:29 +03:00
|
|
|
|
|
|
|
# [[name, [[r-1-1, r-1-2, ...], [r-2-1, r-2-2, ...]]], ...]
|
|
|
|
@results = []
|
|
|
|
|
|
|
|
if @verbose
|
|
|
|
@start_time = Time.now
|
|
|
|
message @start_time
|
2012-11-26 13:19:00 +04:00
|
|
|
@execs.each_with_index{|(path, label, version), i|
|
2012-11-26 13:30:32 +04:00
|
|
|
message "target #{i}: " + (label == version ? "#{label}" : "#{label} (#{version})") + " at \"#{path}\""
|
2007-11-16 04:30:29 +03:00
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-10-16 17:26:46 +04:00
|
|
|
def adjusted_results name, results
|
|
|
|
s = nil
|
|
|
|
results.each_with_index{|e, i|
|
|
|
|
r = e.min
|
|
|
|
case name
|
|
|
|
when /^vm1_/
|
|
|
|
if @loop_wl1
|
|
|
|
r -= @loop_wl1[i]
|
|
|
|
r = 0 if r < 0
|
|
|
|
s = '*'
|
|
|
|
end
|
|
|
|
when /^vm2_/
|
|
|
|
if @loop_wl2
|
|
|
|
r -= @loop_wl2[i]
|
|
|
|
r = 0 if r < 0
|
|
|
|
s = '*'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
yield r
|
|
|
|
}
|
|
|
|
s
|
2011-07-07 01:47:56 +04:00
|
|
|
end
|
|
|
|
|
2007-11-16 04:30:29 +03:00
|
|
|
def show_results
|
2015-03-05 03:14:02 +03:00
|
|
|
case @opt[:format]
|
|
|
|
when :tsv
|
2015-03-05 05:39:09 +03:00
|
|
|
strformat = "\t%1$s"
|
|
|
|
numformat = "\t%1$*2$.3f"
|
|
|
|
minwidth = 0
|
|
|
|
name_width = 0
|
2015-03-05 03:14:02 +03:00
|
|
|
when :markdown
|
|
|
|
markdown = true
|
2015-03-05 05:39:09 +03:00
|
|
|
strformat = "|%1$-*2$s"
|
|
|
|
numformat = "|%1$*2$.3f"
|
|
|
|
when :plain
|
|
|
|
strformat = " %1$-*2$s"
|
|
|
|
numformat = " %1$*2$.3f"
|
2015-03-05 03:14:02 +03:00
|
|
|
end
|
|
|
|
|
2015-03-05 05:39:09 +03:00
|
|
|
name_width ||= @name_width
|
|
|
|
minwidth ||= 7
|
|
|
|
width = @execs.map{|(_, v)| [v.size, minwidth].max}
|
|
|
|
|
2007-11-16 04:30:29 +03:00
|
|
|
output
|
|
|
|
|
|
|
|
if @verbose
|
|
|
|
message '-----------------------------------------------------------'
|
|
|
|
message 'raw data:'
|
|
|
|
message
|
|
|
|
message PP.pp(@results, "", 79)
|
|
|
|
message
|
2012-08-11 19:45:54 +04:00
|
|
|
message "Elapsed time: #{Time.now - @start_time} (sec)"
|
2007-11-16 04:30:29 +03:00
|
|
|
end
|
|
|
|
|
2014-05-07 06:57:11 +04:00
|
|
|
if @rawdata_output
|
|
|
|
h = {}
|
|
|
|
h[:cpuinfo] = File.read('/proc/cpuinfo') if File.exist?('/proc/cpuinfo')
|
|
|
|
h[:executables] = @execs
|
|
|
|
h[:results] = @results
|
2014-05-07 07:07:10 +04:00
|
|
|
@rawdata_output.puts h.inspect
|
2014-05-07 06:57:11 +04:00
|
|
|
end
|
|
|
|
|
2007-11-16 04:30:29 +03:00
|
|
|
output '-----------------------------------------------------------'
|
|
|
|
output 'benchmark results:'
|
|
|
|
|
|
|
|
if @verbose and @repeat > 1
|
|
|
|
output "minimum results in each #{@repeat} measurements."
|
|
|
|
end
|
|
|
|
|
2012-10-16 17:26:46 +04:00
|
|
|
output "Execution time (sec)"
|
2015-03-05 03:14:02 +03:00
|
|
|
output if markdown
|
2015-03-05 05:39:09 +03:00
|
|
|
output ["name".ljust(name_width), @execs.map.with_index{|(_, v), i| sprintf(strformat, v, width[i])}].join("").rstrip
|
|
|
|
output ["-"*name_width, width.map{|n|":".rjust(n, "-")}].join("|") if markdown
|
|
|
|
@results.each_with_index{|(v, result), i|
|
2007-11-16 04:30:29 +03:00
|
|
|
rets = []
|
2012-10-16 17:26:46 +04:00
|
|
|
s = adjusted_results(v, result){|r|
|
2015-03-05 05:39:09 +03:00
|
|
|
rets << sprintf(numformat, r, width[i])
|
2007-11-16 04:30:29 +03:00
|
|
|
}
|
2015-03-05 03:14:02 +03:00
|
|
|
v += s if s
|
2015-03-05 05:39:09 +03:00
|
|
|
output [v.ljust(name_width), rets].join("")
|
2007-11-16 04:30:29 +03:00
|
|
|
}
|
2011-07-07 01:47:56 +04:00
|
|
|
|
2012-10-16 17:26:46 +04:00
|
|
|
if @execs.size > 1
|
|
|
|
output
|
2012-11-17 15:07:05 +04:00
|
|
|
output "Speedup ratio: compare with the result of `#{@execs[0][1]}' (greater is better)"
|
2015-03-05 03:14:02 +03:00
|
|
|
output if markdown
|
2015-03-05 05:39:09 +03:00
|
|
|
output ["name".ljust(name_width), @execs[1..-1].map.with_index{|(_, v), i| sprintf(strformat, v, width[i])}].join("").rstrip
|
|
|
|
output ["-"*name_width, width[1..-1].map{|n|":".rjust(n, "-")}].join("|") if markdown
|
|
|
|
@results.each_with_index{|(v, result), i|
|
2012-10-16 17:26:46 +04:00
|
|
|
rets = []
|
|
|
|
first_value = nil
|
|
|
|
s = adjusted_results(v, result){|r|
|
|
|
|
if first_value
|
|
|
|
if r == 0
|
2012-11-17 15:07:05 +04:00
|
|
|
rets << "Error"
|
2012-10-16 17:26:46 +04:00
|
|
|
else
|
2015-03-05 05:39:09 +03:00
|
|
|
rets << sprintf(numformat, first_value/r, width[i])
|
2012-10-16 17:26:46 +04:00
|
|
|
end
|
|
|
|
else
|
|
|
|
first_value = r
|
|
|
|
end
|
|
|
|
}
|
2015-03-05 03:14:02 +03:00
|
|
|
v += s if s
|
2015-03-05 05:39:09 +03:00
|
|
|
output [v.ljust(name_width), rets].join("")
|
2012-10-16 17:26:46 +04:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2012-10-16 17:07:44 +04:00
|
|
|
if @opt[:output]
|
|
|
|
output
|
|
|
|
output "Log file: #{@opt[:output]}"
|
2011-07-07 01:47:56 +04:00
|
|
|
end
|
2007-11-16 04:30:29 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
def files
|
|
|
|
flag = {}
|
|
|
|
@files = Dir.glob(File.join(@dir, 'bm*.rb')).map{|file|
|
|
|
|
next if @pattern && /#{@pattern}/ !~ File.basename(file)
|
2012-10-24 12:46:01 +04:00
|
|
|
next if @exclude && /#{@exclude}/ =~ File.basename(file)
|
2007-11-16 04:30:29 +03:00
|
|
|
case file
|
|
|
|
when /bm_(vm[12])_/, /bm_loop_(whileloop2?).rb/
|
|
|
|
flag[$1] = true
|
|
|
|
end
|
|
|
|
file
|
|
|
|
}.compact
|
|
|
|
|
|
|
|
if flag['vm1'] && !flag['whileloop']
|
|
|
|
@files << File.join(@dir, 'bm_loop_whileloop.rb')
|
|
|
|
elsif flag['vm2'] && !flag['whileloop2']
|
|
|
|
@files << File.join(@dir, 'bm_loop_whileloop2.rb')
|
|
|
|
end
|
|
|
|
|
|
|
|
@files.sort!
|
|
|
|
progress_message "total: #{@files.size * @repeat} trial(s) (#{@repeat} trial(s) for #{@files.size} benchmark(s))\n"
|
|
|
|
@files
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
files.each_with_index{|file, i|
|
|
|
|
@i = i
|
|
|
|
r = measure_file(file)
|
|
|
|
|
|
|
|
if /bm_loop_whileloop.rb/ =~ file
|
|
|
|
@loop_wl1 = r[1].map{|e| e.min}
|
|
|
|
elsif /bm_loop_whileloop2.rb/ =~ file
|
|
|
|
@loop_wl2 = r[1].map{|e| e.min}
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def measure_file file
|
|
|
|
name = File.basename(file, '.rb').sub(/^bm_/, '')
|
|
|
|
prepare_file = File.join(File.dirname(file), "prepare_#{name}.rb")
|
|
|
|
load prepare_file if FileTest.exist?(prepare_file)
|
|
|
|
|
|
|
|
if @verbose
|
|
|
|
output
|
|
|
|
output '-----------------------------------------------------------'
|
|
|
|
output name
|
|
|
|
output
|
|
|
|
output File.read(file)
|
|
|
|
output
|
|
|
|
end
|
|
|
|
|
2015-03-05 03:14:02 +03:00
|
|
|
@name_width = name.size if name.size > @name_width
|
2007-11-16 04:30:29 +03:00
|
|
|
result = [name]
|
|
|
|
result << @execs.map{|(e, v)|
|
|
|
|
(0...@repeat).map{
|
|
|
|
message_print "#{v}\t"
|
|
|
|
progress_message '.'
|
|
|
|
|
|
|
|
m = measure(e, file)
|
|
|
|
message "#{m}"
|
|
|
|
m
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@results << result
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
2014-05-05 20:11:00 +04:00
|
|
|
unless defined?(File::NULL)
|
|
|
|
if File.exist?('/dev/null')
|
|
|
|
File::NULL = '/dev/null'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-11-16 04:30:29 +03:00
|
|
|
def measure executable, file
|
2012-10-19 05:22:08 +04:00
|
|
|
cmd = "#{executable} #{@ruby_arg} #{file}"
|
|
|
|
|
2007-11-16 04:30:29 +03:00
|
|
|
m = Benchmark.measure{
|
2014-01-30 22:48:40 +04:00
|
|
|
system(cmd, out: File::NULL)
|
2007-11-16 04:30:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if $? != 0
|
2012-05-27 08:35:11 +04:00
|
|
|
output "\`#{cmd}\' exited with abnormal status (#{$?})"
|
|
|
|
0
|
|
|
|
else
|
|
|
|
m.real
|
2007-11-16 04:30:29 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if __FILE__ == $0
|
|
|
|
opt = {
|
2012-11-26 13:19:00 +04:00
|
|
|
:execs => [],
|
2012-10-16 17:07:44 +04:00
|
|
|
:dir => File.dirname(__FILE__),
|
2007-11-16 04:30:29 +03:00
|
|
|
:repeat => 1,
|
2015-03-05 05:38:54 +03:00
|
|
|
:output => nil,
|
2015-03-05 03:14:02 +03:00
|
|
|
:raw_output => nil,
|
|
|
|
:format => :tsv,
|
2007-11-16 04:30:29 +03:00
|
|
|
}
|
2015-03-05 05:38:54 +03:00
|
|
|
formats = {
|
|
|
|
:tsv => ".tsv",
|
|
|
|
:markdown => ".md",
|
2015-03-05 05:39:09 +03:00
|
|
|
:plain => ".txt",
|
2015-03-05 05:38:54 +03:00
|
|
|
}
|
2007-11-16 04:30:29 +03:00
|
|
|
|
|
|
|
parser = OptionParser.new{|o|
|
|
|
|
o.on('-e', '--executables [EXECS]',
|
2012-11-26 13:19:00 +04:00
|
|
|
"Specify benchmark one or more targets (e1::path1; e2::path2; e3::path3;...)"){|e|
|
|
|
|
e.split(/;/).each{|path|
|
|
|
|
opt[:execs] << path
|
|
|
|
}
|
2007-11-16 04:30:29 +03:00
|
|
|
}
|
|
|
|
o.on('-d', '--directory [DIRECTORY]', "Benchmark suites directory"){|d|
|
|
|
|
opt[:dir] = d
|
|
|
|
}
|
|
|
|
o.on('-p', '--pattern [PATTERN]', "Benchmark name pattern"){|p|
|
|
|
|
opt[:pattern] = p
|
|
|
|
}
|
2012-10-24 12:46:01 +04:00
|
|
|
o.on('-x', '--exclude [PATTERN]', "Benchmark exclude pattern"){|e|
|
|
|
|
opt[:exclude] = e
|
|
|
|
}
|
2007-11-16 04:30:29 +03:00
|
|
|
o.on('-r', '--repeat-count [NUM]', "Repeat count"){|n|
|
|
|
|
opt[:repeat] = n.to_i
|
|
|
|
}
|
2012-02-12 10:17:25 +04:00
|
|
|
o.on('-o', '--output-file [FILE]', "Output file"){|f|
|
|
|
|
opt[:output] = f
|
2007-11-16 04:30:29 +03:00
|
|
|
}
|
2012-10-19 05:22:08 +04:00
|
|
|
o.on('--ruby-arg [ARG]', "Optional argument for ruby"){|a|
|
|
|
|
opt[:ruby_arg] = a
|
|
|
|
}
|
2014-05-07 06:57:11 +04:00
|
|
|
o.on('--rawdata-output [FILE]', 'output rawdata'){|r|
|
|
|
|
opt[:rawdata_output] = r
|
2007-11-16 04:30:29 +03:00
|
|
|
}
|
2015-03-05 05:38:54 +03:00
|
|
|
o.on('-f', "--format=FORMAT", "output format (#{formats.keys.join(",")})", formats.keys){|r|
|
2015-03-05 03:14:02 +03:00
|
|
|
opt[:format] = r
|
|
|
|
}
|
2007-11-16 04:30:29 +03:00
|
|
|
o.on('-v', '--verbose'){|v|
|
|
|
|
opt[:verbose] = v
|
|
|
|
}
|
2014-05-07 06:57:11 +04:00
|
|
|
o.on('-q', '--quiet', "Run without notify information except result table."){|q|
|
|
|
|
opt[:quiet] = q
|
|
|
|
opt[:verbose] = false
|
|
|
|
}
|
2007-11-16 04:30:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
parser.parse!(ARGV)
|
2015-03-05 05:38:54 +03:00
|
|
|
opt[:output] ||= "bmlog-#{Time.now.strftime('%Y%m%d-%H%M%S')}.#{$$}#{formats[opt[:format]]}"
|
2007-11-16 04:30:29 +03:00
|
|
|
BenchmarkDriver.benchmark(opt)
|
|
|
|
end
|
|
|
|
|