rbench 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+