somebee-rbench 0.1 → 0.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.
@@ -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(value=title)
15
+ str = case value
16
+ when Array then "%#{width-1}.2f" % (value[0] / value[1]) + "x"
17
+ when Float then "%#{width}.3f" % value
18
+ when Integer then "%#{width}.0f" % value
19
+ when TrueClass then " "*(width/2.0).floor + "X" + " "*(width/2.0).floor
20
+ when String then "%#{width}s" % (value)[0,width]
21
+ when Object then " " * width
22
+ end
23
+ return " #{(str+" "*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?|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,50 @@
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?|inspect|instance_eval)/
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
+ out << column.to_s(value.is_a?(Array) ? @cells.values_at(*value) : value )
46
+ end
47
+ out << @runner.newline
48
+ end
49
+ end
50
+ 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,27 @@
1
+ module RBench
2
+ class Summary < Report
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.select{|i| i.is_a?(Report)} : @runner.reports
17
+
18
+ @runner.columns.each do |c|
19
+ # TODO: Get number of fields that was counted in, and /
20
+ value = items.inject(0){|tot,i| v = i.cells[c.name]; tot += v.kind_of?(Numeric) ? v : 0 }
21
+ @cells[c.name] = value == 0 ? "" : value
22
+ end
23
+ puts @runner.separator(nil,"=") unless @group
24
+ puts to_s
25
+ end
26
+ end
27
+ end
data/lib/rbench.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'pathname'
2
2
  require 'benchmark'
3
3
 
4
- dir = Pathname(__FILE__).dirname.expand_path + 'rbench'
4
+ dir = Pathname(__FILE__).dirname.expand_path + 'rbench/'
5
5
 
6
6
  require dir + 'runner'
7
7
  require dir + 'column'
@@ -10,7 +10,7 @@ require dir + 'report'
10
10
  require dir + 'summary'
11
11
 
12
12
  module RBench
13
- def self.run(times, &block)
13
+ def self.run(times=1, &block)
14
14
  Runner.new(times).run(&block)
15
15
  end
16
16
  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
data/spec/spec.opts ADDED
@@ -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 CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: somebee-rbench
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.1"
4
+ version: "0.2"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yehuda Katz & Sindre Aarsaether
@@ -24,11 +24,18 @@ extra_rdoc_files:
24
24
  - LICENSE
25
25
  - TODO
26
26
  files:
27
- - LICENSE
28
27
  - README
29
- - Rakefile
30
- - lib/rbench.rb
28
+ - LICENSE
31
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
32
39
  has_rdoc: true
33
40
  homepage: http://www.rbench.org
34
41
  post_install_message:
data/Rakefile DELETED
@@ -1,9 +0,0 @@
1
- require 'rubygems'
2
- require 'rake/gempackagetask'
3
- load File.join(File.dirname(__FILE__), 'rbench.gemspec')
4
-
5
- Rake::GemPackageTask.new(GEMSPEC).define
6
-
7
- task :install => [:package] do
8
- sh %{sudo gem install pkg/#{GEM}-#{VERSION}}
9
- end