forked from wycats/benchwarmer
-
Notifications
You must be signed in to change notification settings - Fork 1
/
benchwarmer.rb
131 lines (104 loc) · 3.24 KB
/
benchwarmer.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
require "benchmark"
require File.join(File.expand_path(File.dirname(__FILE__)), "vendor", "dictionary")
module Enumerable
def max_by(&blk)
self.sort_by(&blk).last
end
end
module Benchmark
def self.warmer(times, &blk)
Warmer.new(times).run(&blk)
end
class Warmer
attr_reader :times, :columns, :groups
def initialize(times)
@times = times
end
def line!
size = @name_max + @group_max + 1
@columns.each do |name, val|
size += (@columns[name].size <= 5 ? 5 : @columns[name].size) + 3
end
puts "-" * size
end
def blanks(size)
print " " * size
end
def run(&blk)
puts "Running the benchmarks #{@times} times each..."
puts
self.instance_eval(&blk)
unless @columns
@columns = Dictionary.new
@columns[:results] = "Results"
end
@name_max = @groups.keys.max_by {|x| x.size}.size
@group_max = @groups.values.map {|x| x.keys }.flatten.max_by {|x| x.size}.size
print " " * (@name_max + @group_max + 2)
puts @columns.map {|col,val| "%5s" % val }.join(" | ") + " |"
line!
@groups.each do |group_name,runs|
# Print the group's name, left-justified and filling up as much space as the max
# group name
print "%-#{@name_max + 1}s" % group_name
# Go through the registered runs
runs.each_with_index do |(name, procs), i|
blanks(@name_max + 1) if i > 0
# The name has to take up all the space of the group name, and then some
print "%#{@group_max}s" % name
# Actually run the benchmarks
procs.each_with_index do |(column, proc), i|
head = @columns[column]
bench = Benchmark.measure { @times.times(&proc)}
print (" %#{[head.size, 5].max}.2f |" % bench.real)
end
puts
end
line!
end
end
def columns(*list)
@columns = list.inject(Dictionary.new) do |accum, col|
accum[col] = col.to_s.upcase
accum
end
end
def titles(titles)
@columns ||= Dictionary.new
@columns.merge!(titles)
end
def group(str, &blk)
@groups ||= Dictionary.new {|h,k| h[k] = Dictionary.new}
@current_group = str
self.instance_eval(&blk)
@current_group = nil
end
def report(str, &blk)
@groups ||= Dictionary.new {|h,k| h[k] = Dictionary.new}
if !@columns || @columns.size == 1
@groups[@current_group || ""][str] = {(@columns && @columns.order[0]) || :results => blk}
else
report = GroupReport.new(@columns.keys)
report.instance_eval(&blk)
@groups[@current_group || ""][str] = report.runs
end
end
end
class GroupReport
self.instance_methods.each do |meth|
send(:undef_method, meth) unless meth =~ /^(__|instance_eval)/
end
attr_accessor :runs, :cols
def initialize(cols)
new_self = (class << self; self end)
cols.each do |col|
new_self.class_eval <<-RUBY
def #{col}(&blk)
@runs ||= {}
@runs[#{col.to_sym.inspect}] = blk
end
RUBY
end
end
end
end