rbench 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Yehuda Katz & Sindre Aarsaether
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,103 @@
1
+ rbench
2
+ ======
3
+
4
+ == What is RBench?
5
+
6
+ Library for generating nice ruby-benchmarks in several formats.
7
+ Only text-output is available atm.
8
+
9
+ Heavily inspired by benchwarmer. Much love.
10
+
11
+ == Usage
12
+
13
+ require "rubygems"
14
+ require "rbench"
15
+
16
+ # Choose how many times you want to repeat each benchmark.
17
+ # This can be overridden on specific reports, if needed.
18
+ TIMES = 100_000
19
+
20
+ # A relatively simple benchmark:
21
+ RBench.run(TIMES) do
22
+
23
+ column :one
24
+ column :two
25
+
26
+ report "Squeezing with #squeeze" do
27
+ one { "abc//def//ghi//jkl".squeeze("/") }
28
+ two { "abc///def///ghi///jkl".squeeze("/") }
29
+ end
30
+
31
+ report "Squeezing with #gsub" do
32
+ one { "abc//def//ghi//jkl".gsub(/\/+/, "/") }
33
+ two { "abc///def///ghi///jkl".gsub(/\/+/, "/") }
34
+ end
35
+
36
+ report "Splitting with #split" do
37
+ one { "aaa/aaa/aaa.bbb.ccc.ddd".split(".") }
38
+ two { "aaa//aaa//aaa.bbb.ccc.ddd.eee".split(".") }
39
+ end
40
+
41
+ report "Splitting with #match" do
42
+ one { "aaa/aaa/aaa.bbb.ccc.ddd".match(/\.([^\.]*)$/) }
43
+ two { "aaa//aaa//aaa.bbb.ccc.ddd.eee".match(/\.([^\.]*)$/) }
44
+ end
45
+
46
+ end
47
+
48
+ # The benchmark above will output the following:
49
+ ONE | TWO |
50
+ --------------------------------------------------------------------------------
51
+ Squeezing with #squeeze 0.122 | 0.118 |
52
+ Squeezing with #gsub 0.274 | 0.271 |
53
+ Splitting with #split 0.349 | 0.394 |
54
+ Splitting with #match 0.238 | 0.291 |
55
+
56
+
57
+ # Now onto a benchmark that utilizes a some more stiff.
58
+ RBench.run(TIMES) do
59
+
60
+ format :width => 65
61
+
62
+ column :times
63
+ column :one, :title => "#1"
64
+ column :two, :title => "#2"
65
+ column :diff, :title => "#1/#2", :compare => [:one,:two]
66
+
67
+ group "Squeezing" do
68
+ report "with #squeeze" do
69
+ one { "abc//def//ghi//jkl".squeeze("/") }
70
+ two { "abc///def///ghi///jkl".squeeze("/") }
71
+ end
72
+ report "with #gsub" do
73
+ one { "abc//def//ghi//jkl".gsub(/\/+/, "/") }
74
+ two { "abc///def///ghi///jkl".gsub(/\/+/, "/") }
75
+ end
76
+
77
+ summary "all methods (totals)"
78
+ end
79
+
80
+ group "Splitting" do
81
+ report "with #split" do
82
+ one { "aaa/aaa/aaa.bbb.ccc.ddd".split(".") }
83
+ two { "aaa//aaa//aaa.bbb.ccc.ddd.eee".split(".") }
84
+ end
85
+ report "with #match", TIMES / 100 do
86
+ one { "aaa/aaa/aaa.bbb.ccc.ddd".match(/\.([^\.]*)$/) }
87
+ two { "aaa//aaa//aaa.bbb.ccc.ddd.eee".match(/\.([^\.]*)$/) }
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ # The benchmark above will output the following:
94
+ | #1 | #2 | #1/#2 |
95
+ --Squeezing------------------------------------------------------
96
+ with #squeeze x100000 | 0.122 | 0.117 | 1.04x |
97
+ with #gsub x100000 | 0.267 | 0.279 | 0.96x |
98
+ all methods (totals) | 0.390 | 0.396 | 0.98x |
99
+ --Splitting------------------------------------------------------
100
+ with #split x100000 | 0.341 | 0.394 | 0.87x |
101
+ with #match x1000 | 0.002 | 0.003 | 0.82x |
102
+
103
+
data/TODO ADDED
@@ -0,0 +1,6 @@
1
+ TODO
2
+ ====
3
+ * Add #to_html and #to_xml output
4
+ * Write general documentation
5
+ * Make example in readme with a boolean-column
6
+ * Write specs
@@ -0,0 +1,16 @@
1
+ require 'pathname'
2
+ require 'benchmark'
3
+
4
+ dir = Pathname(__FILE__).dirname.expand_path + 'rbench/'
5
+
6
+ require dir + 'runner'
7
+ require dir + 'column'
8
+ require dir + 'group'
9
+ require dir + 'report'
10
+ require dir + 'summary'
11
+
12
+ module RBench
13
+ def self.run(times=1, &block)
14
+ Runner.new(times).run(&block)
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ module RBench
2
+ class Column
3
+ attr_accessor :name, :title, :width, :compare, :default
4
+
5
+ def initialize(runner, name, options = {})
6
+ @runner = runner
7
+ @name = name
8
+ @title = options[:title] || (@name == :times ? "" : @name.to_s.upcase)
9
+ @width = options[:width] || [@title.length,7].max
10
+ @compare = options[:compare]
11
+ @default = @compare ? @compare : options[:default]
12
+ end
13
+
14
+ def to_s(val=title)
15
+ str = case val
16
+ when Array then "%#{width-1}.2f" % (val[0] / val[1]) + "x"
17
+ when Float then "%#{width}.3f" % val
18
+ when Integer then "%#{width}.0f" % val
19
+ when TrueClass then " "*(width/2.0).floor + "X" + " "*(width/2.0).floor
20
+ when String then "%#{width}s" % (val)[0,width]
21
+ when Object then " " * width
22
+ end
23
+ return " #{(str.to_s+" "*width)[0,width]} |"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ module RBench
2
+ class Group
3
+ self.instance_methods.each do |m|
4
+ send(:undef_method, m) unless m =~ /^(__|is_a?|kind_of?|respond_to?|hash|eql?|inspect|instance_eval)/
5
+ end
6
+
7
+ attr_reader :name, :items, :block
8
+
9
+ def initialize(runner, name, &block)
10
+ @runner = runner
11
+ @name = name
12
+ @items = []
13
+ @block = block
14
+ end
15
+
16
+ def prepare
17
+ # This just loops through and spawns the reports, and the summary (if exists)
18
+ self.instance_eval(&@block) if @block
19
+ # Now we want to make sure that the summary is
20
+ @items << @items.shift if @items.first.is_a?(Summary)
21
+ end
22
+
23
+ def run
24
+ # Put a separator with the group-name at the top. puts?
25
+ puts @runner.separator(@name)
26
+ # Now loop through the items in this group, and run them
27
+ @items.each{|item| item.run}
28
+ end
29
+
30
+ def report(name,times=nil,&block)
31
+ @items << Report.new(@runner,self,name,times,&block)
32
+ end
33
+
34
+ def summary(name)
35
+ @items.unshift(Summary.new(@runner,self,name)) unless @items.detect{|i| i.is_a?(Summary)}
36
+ end
37
+
38
+ def to_s
39
+ @runner.separator(@name) << @items.map { |item| item.to_s }.join
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,53 @@
1
+ module RBench
2
+ class Report
3
+ self.instance_methods.each do |m|
4
+ send(:undef_method, m) unless m =~ /^(__|is_a?|kind_of?|respond_to?|hash|inspect|instance_eval|eql?)/
5
+ end
6
+
7
+ attr_reader :name, :cells
8
+
9
+ def initialize(runner, group, name, times=nil,&block)
10
+ @runner = runner
11
+ @group = group
12
+ @name = name
13
+ @times = (times || runner.times).ceil
14
+ @cells = {}
15
+ @block = block
16
+
17
+ # Setting the default for all cells
18
+ runner.columns.each {|c| @cells[c.name] = c.name == :times ? "x#{@times}" : c.default }
19
+
20
+ new_self = (class << self; self end)
21
+ @runner.columns.each do |column|
22
+ new_self.class_eval <<-CLASS
23
+ def #{column.name}(val=nil,&block)
24
+ @cells[#{column.name.inspect}] = block ? Benchmark.measure { @times.times(&block) }.real : val
25
+ end
26
+ CLASS
27
+ end
28
+ end
29
+
30
+ def run
31
+ # runs the actual benchmarks. If there is only one column, just evaluate the block itself.
32
+ if @runner.columns.length == 1
33
+ @cells[@runner.columns.first.name] = Benchmark.measure { @times.times(&@block) }.real
34
+ else
35
+ self.instance_eval(&@block)
36
+ end
37
+ # puts its row now that it is complete
38
+ puts to_s
39
+ end
40
+
41
+ def to_s
42
+ out = "%-#{@runner.desc_width}s" % name
43
+ @runner.columns.each do |column|
44
+ value = @cells[column.name]
45
+ value = @cells.values_at(*value) if value.is_a?(Array)
46
+ value = nil if value.is_a?(Array) && value.nitems != 2
47
+
48
+ out << column.to_s(value)
49
+ end
50
+ out << @runner.newline
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,109 @@
1
+ module RBench
2
+ class Runner
3
+ attr_accessor :columns, :items, :times, :width
4
+
5
+ def initialize(times)
6
+ @width = 0
7
+ @times = times
8
+ @columns = []
9
+ @items = []
10
+ end
11
+
12
+ def run(&block)
13
+
14
+ # initiate all the columns, groups, reports, and summaries on top level.
15
+ self.instance_eval(&block)
16
+
17
+ # the groups has not run just yet, but when they do, we really only want
18
+ # to make them initiate their reports, not run them (just yet)
19
+ # when we only have two levels, _every_ report should now be initialized.
20
+ @items.each{|item| item.prepare if item.is_a?(Group)}
21
+
22
+ # We are putting the summary to the back if its there.
23
+ @items << @items.shift if @items.first.is_a?(Summary)
24
+
25
+ # if on columns were set, create a default column
26
+ column(:results, :title => "Results") if @columns.empty?
27
+
28
+ # since we are about to start rendering, we put out the column-header
29
+ puts header
30
+
31
+ # now we are ready to loop through our items and run!
32
+ items.each { |item| item.run if item.respond_to?(:run) }
33
+
34
+ # returning self so people can output it in different formats.
35
+ self
36
+ end
37
+
38
+ def format(options={})
39
+ @width = options.delete(:width) || @width
40
+ end
41
+
42
+ def column(name,options={})
43
+ @columns << Column.new(self,name,options)
44
+ end
45
+
46
+ def group(name,&block)
47
+ @items << Group.new(self,name,&block)
48
+ end
49
+
50
+ def report(name,times=nil,&block)
51
+ # create an anonymous group, or add it to the last open group.
52
+ group(nil) unless @items.last.is_a?(Group) && !@items.last.block
53
+ # now create the report on the last group
54
+ @items.last.report(name,times,&block)
55
+ end
56
+
57
+ def summary(name)
58
+ # adding the summary to the front, so it is easier to put it last later.
59
+ @items.unshift(Summary.new(self,nil,name)) unless @items.detect{|i| i.is_a?(Summary)}
60
+ end
61
+
62
+ ##
63
+ # convenience-methods
64
+ ##
65
+
66
+ def groups
67
+ @items.select{|item| item.is_a?(Group) }
68
+ end
69
+
70
+ def reports
71
+ # we now want _all_ reports, also those that are part of subgroups
72
+ groups.map{|g| g.items.select{|item| item.is_a?(Report) } }.flatten
73
+ end
74
+
75
+ ##
76
+ # for rendering text. pull out in separate module when to_xml and to_html is in place
77
+ ##
78
+
79
+ def newline
80
+ "\n"
81
+ end
82
+
83
+ def header
84
+ " " * desc_width + @columns.map {|c| c.to_s }.join + newline
85
+ end
86
+
87
+ def desc_width
88
+ @desc_width ||= [items.map{|i| (i.items.map{|r| r.name} << i.name) }.flatten.map{|i| i.to_s.length}.max+8,@width-columns_width].max
89
+ end
90
+
91
+ def columns_width
92
+ @columns.inject(0){ |tot,c| tot += (c.to_s.length) }
93
+ end
94
+
95
+ def width(value=nil)
96
+ header.length-1
97
+ end
98
+
99
+ def separator(title=nil,chr="-",length=width)
100
+ title ? chr*2 + title + chr * (width - title.length - 2) : chr * length
101
+ end
102
+
103
+ def to_s
104
+ out = " " * desc_width + @columns.map {|c| c.to_s }.join + newline
105
+ out << @items.map {|item| item.to_s}.join
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,51 @@
1
+ module RBench
2
+ class Summary
3
+ attr_reader :name, :runner, :cells, :items
4
+ attr_accessor :lines
5
+
6
+ def initialize(runner, group, name)
7
+ @runner = runner
8
+ @group = group
9
+ @name = name
10
+ @cells = {} # A hash with keys as columns, and values being the result
11
+ @items = []
12
+ end
13
+
14
+ def run
15
+ # maybe add convenience-method to group to. group == runner really.
16
+ items = (@group ? @group.items & @runner.reports : @runner.reports)
17
+
18
+ rows = items.map{|item| item.cells.values_at(*@runner.columns.map{|c|c.name}) }
19
+ rows = rows.pop.zip(*rows)
20
+
21
+ @runner.columns.each_with_index do |c,i|
22
+ if c.compare
23
+ value,comparisons = 0,0
24
+ items.each do |item|
25
+ v1,v2 = *item.cells.values_at(*c.compare)
26
+ if v1.kind_of?(Numeric) && v2.kind_of?(Numeric) && v1 != 0 && v2 != 0
27
+ value += v1 / v2
28
+ comparisons += 1
29
+ end
30
+ end
31
+ @cells[c.name] = [value,comparisons] if comparisons > 0
32
+ elsif c.name != :times
33
+ @cells[c.name] = rows[i].compact.select{|r| r.kind_of?(Numeric)}.inject(0){|tot,v| tot += v.to_f }
34
+ end
35
+ end
36
+
37
+ puts to_s
38
+ end
39
+
40
+ def to_s
41
+ out = ""
42
+ out << @runner.separator(nil,"=") + @runner.newline unless @group
43
+ out << "%-#{@runner.desc_width}s" % name
44
+ @runner.columns.each do |column|
45
+ value = @cells[column.name]
46
+ out << column.to_s( value )
47
+ end
48
+ out << @runner.newline
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,141 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
3
+
4
+ TIMES = 100_000
5
+
6
+ RBench.run(TIMES) do
7
+
8
+ column :one
9
+ column :two
10
+
11
+ report "Squeezing with #squeeze" do
12
+ one { "abc//def//ghi//jkl".squeeze("/") }
13
+ two { "abc///def///ghi///jkl".squeeze("/") }
14
+ end
15
+
16
+ report "Squeezing with #gsub" do
17
+ one { "abc//def//ghi//jkl".gsub(/\/+/, "/") }
18
+ two { "abc///def///ghi///jkl".gsub(/\/+/, "/") }
19
+ end
20
+
21
+ report "Splitting with #split" do
22
+ one { "aaa/aaa/aaa.bbb.ccc.ddd".split(".") }
23
+ two { "aaa//aaa//aaa.bbb.ccc.ddd.eee".split(".") }
24
+ end
25
+
26
+ report "Splitting with #match" do
27
+ one { "aaa/aaa/aaa.bbb.ccc.ddd".match(/\.([^\.]*)$/) }
28
+ two { "aaa//aaa//aaa.bbb.ccc.ddd.eee".match(/\.([^\.]*)$/) }
29
+ end
30
+
31
+ end
32
+
33
+ puts
34
+ puts
35
+
36
+ RBench.run(TIMES) do
37
+
38
+ format :width => 80
39
+
40
+ report "Squeezing with #squeeze" do
41
+ "abc//def//ghi//jkl".squeeze("/")
42
+ end
43
+
44
+ report "Squeezing with #gsub" do
45
+ "abc//def//ghi//jkl".gsub(/\/+/, "/")
46
+ end
47
+
48
+ report "Splitting with #split" do
49
+ "aaa/aaa/aaa.bbb.ccc.ddd".split(".")
50
+ end
51
+
52
+ report "Splitting with #match" do
53
+ "aaa/aaa/aaa.bbb.ccc.ddd".match(/\.([^\.]*)$/)
54
+ end
55
+
56
+ end
57
+
58
+ puts
59
+ puts
60
+
61
+ bench = RBench.run(TIMES) do
62
+
63
+ column :times
64
+ column :dm
65
+ column :ar
66
+ column :diff, :compare => [:dm,:ar]
67
+
68
+ group "This is a group" do
69
+
70
+ report "split", TIMES / 10 do
71
+ dm { "aaa/aaa/aaa.bbb.ccc.ddd".split(".") }
72
+ ar { "aaa//aaa//aaa.bbb.ccc.ddd.eee".split(".") }
73
+ end
74
+
75
+ report "match" do
76
+ dm { "aaa/aaa/aaa.bbb.ccc.ddd".match(/\.([^\.]*)$/) }
77
+ ar { "aaa//aaa//aaa.bbb.ccc.ddd.eee".match(/\.([^\.]*)$/) }
78
+ end
79
+
80
+ summary "methods (totals)" # should display total for all preceding rows within this group / scope
81
+
82
+ end
83
+
84
+ group "This is a group" do
85
+
86
+ report "split", TIMES / 10 do
87
+ dm { "aaa/aaa/aaa.bbb.ccc.ddd".split(".") }
88
+ ar { "aaa//aaa//aaa.bbb.ccc.ddd.eee".split(".") }
89
+ end
90
+
91
+ report "match" do
92
+ dm { "aaa/aaa/aaa.bbb.ccc.ddd".match(/\.([^\.]*)$/) }
93
+ #ar { "aaa//aaa//aaa.bbb.ccc.ddd.eee".match(/\.([^\.]*)$/) }
94
+ end
95
+
96
+ end
97
+
98
+ report "testing" do
99
+ dm { "aaa/aaa/aaa.bbb.ccc.ddd".match(/\.([^\.]*)$/) }
100
+ ar { "aaa//aaa//aaa.bbb.ccc.ddd.eee".match(/\.([^\.]*)$/) }
101
+ end
102
+
103
+ summary "Overall"
104
+
105
+ end
106
+
107
+ puts
108
+ puts
109
+
110
+ RBench.run(TIMES) do
111
+
112
+ column :times
113
+ column :one, :title => "#1"
114
+ column :two, :title => "#2"
115
+ column :diff, :title => "#1/#2", :compare => [:one,:two]
116
+
117
+ group("Squeezing") do
118
+ report "with #squeeze" do
119
+ one { "abc//def//ghi//jkl".squeeze("/") }
120
+ two { "abc///def///ghi///jkl".squeeze("/") }
121
+ end
122
+ report "with #gsub" do
123
+ one { "abc//def//ghi//jkl".gsub(/\/+/, "/") }
124
+ two { "abc///def///ghi///jkl".gsub(/\/+/, "/") }
125
+ end
126
+
127
+ summary "all methods (totals)"
128
+ end
129
+
130
+ group("Splitting") do
131
+ report "with #split" do
132
+ one { "aaa/aaa/aaa.bbb.ccc.ddd".split(".") }
133
+ two { "aaa//aaa//aaa.bbb.ccc.ddd.eee".split(".") }
134
+ end
135
+ report "with #match", TIMES / 100 do
136
+ one { "aaa/aaa/aaa.bbb.ccc.ddd".match(/\.([^\.]*)$/) }
137
+ two { "aaa//aaa//aaa.bbb.ccc.ddd.eee".match(/\.([^\.]*)$/) }
138
+ end
139
+ end
140
+
141
+ end
@@ -0,0 +1,2 @@
1
+ --format specdoc
2
+ --colour
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ gem 'rspec', '>=1.1.3'
3
+ require 'spec'
4
+ require 'pathname'
5
+ require Pathname(__FILE__).dirname.expand_path.parent + 'lib/rbench'
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rbench
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Yehuda Katz & Sindre Aarsaether
8
+ autorequire: rbench
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-06-16 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Library for generating nice ruby-benchmarks
17
+ email: post [a] rbench [d] org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - LICENSE
25
+ - TODO
26
+ files:
27
+ - README
28
+ - LICENSE
29
+ - TODO
30
+ - spec/rbench_spec.rb
31
+ - spec/spec.opts
32
+ - spec/spec_helper.rb
33
+ - lib/rbench.rb
34
+ - lib/rbench/summary.rb
35
+ - lib/rbench/runner.rb
36
+ - lib/rbench/report.rb
37
+ - lib/rbench/group.rb
38
+ - lib/rbench/column.rb
39
+ has_rdoc: true
40
+ homepage: http://www.rbench.org
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.1.1
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: Library for generating nice ruby-benchmarks
65
+ test_files: []
66
+