GitHub Sale: sign up for any paid plan this week and pay nothing until January 1, 2009!  [ hide ]

public
Description: Prettier Benchmarking for Ruby
Clone URL: git://github.com/wycats/benchwarmer.git
nex3 (author)
Mon May 05 17:03:36 -0700 2008
commit  2fe5e93bcbe6b2b69332dcfd1404be7705f0745a
tree    92de673dfdeaf387d05d0728c81c0ab7eed13c08
parent  73abbc232b435a162698b8925818bff218c78bf2
benchwarmer / lib / benchwarmer.rb
100644 132 lines (104 sloc) 3.32 kb
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
132
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