bnchmrkr 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 81543d9e532bbc61a99896b8019287a145dcd15b
4
- data.tar.gz: 9272a8d8e1764d176f4e5b8d8e049a6e78f6c181
3
+ metadata.gz: 2f9a56d97f5184f42f18cd757cd07e102a6858a8
4
+ data.tar.gz: eb6d17fe537b8e9a1ab56a5f2999e6d49572acc3
5
5
  SHA512:
6
- metadata.gz: a8ac397da4feaa130d033aa0d014faef00cc256bbbb5646f8f3b503a9fad1ac71b0f1c5e58f22c180e9009c25aadeda5e8e80c142a74455eeec0ab243b13a176
7
- data.tar.gz: 717623a9113bb53a82ad04fdfd10271034cf13643eaae0b713405c3e47cfb8519dc471baeed90f9befa39939994c1dc224ae55c48cc6cbb5c266aaf820eddb53
6
+ metadata.gz: a8f9736fa5621e2663b6c84ba286c501bedf312eb9ec1bb06af97844c07ac580152672747733f7c04947674e37a52942e40d3b71aed53c2ea2d36b52aa8d78ed
7
+ data.tar.gz: 84dac7131ef08ec8512c57f6fc476558ddb5c20d947ebe960ea99011f8294095af90e9a43be3afd767a9ac2488062da9d3f1891024c65fc3239a2754bdb1c3eb
data/.reek CHANGED
@@ -10,19 +10,42 @@ BooleanParameter:
10
10
  ControlParameter:
11
11
  enabled: false
12
12
 
13
+ DataClump:
14
+ min_clump_size: 4
15
+
13
16
  DuplicateMethodCall:
14
17
  enabled: false
15
18
 
19
+ # this is not a real thing
20
+ FeatureEnvy:
21
+ enabled: false
22
+
23
+ # this should have understanding of non-required parameters
24
+ LongParameterList:
25
+ max_params: 5
26
+
16
27
  NestedIterators:
17
28
  max_allowed_nesting: 3
18
29
 
30
+ # you don't know my life
31
+ NilCheck:
32
+ enabled: false
33
+
19
34
  # more lacking context judgement - when _would_ you be ok with '!' ?
20
35
  PrimaDonnaMethod:
21
36
  enabled: false
22
37
 
23
38
  # this feels a bit arbitrary.. maybe would make more sense if was an average
24
39
  TooManyStatements:
25
- max_statements: 15
40
+ max_statements: 30
41
+
42
+ # see above
43
+ TooManyInstanceVariables:
44
+ max_instance_variables: 20
45
+
46
+ # see above
47
+ TooManyMethods:
48
+ max_methods: 20
26
49
 
27
50
  # i don't name my exceptions specifically. that doesn't make me a bad person.
28
51
  UncommunicativeVariableName:
@@ -7,4 +7,4 @@ notifications:
7
7
  email: false
8
8
 
9
9
  bundler_args: --without test --jobs 3 --retry 3
10
- script: bundle exec rake test reek build
10
+ script: bundle exec rake test reek build install
data/Gemfile CHANGED
@@ -2,9 +2,8 @@ ruby '2.0.0'
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- gem 'rake', '~> 10.5.0', '>= 10.5.0'
6
-
7
5
  group :development do
6
+ gem 'rake', '~> 10.5.0', '>= 10.5.0'
8
7
  gem 'jeweler', '~> 2.0.1', '>= 2.0.1'
9
8
  gem 'reek', '~> 3.11', '>= 3.11'
10
9
  gem 'test-unit', '~> 3.0.0', '>= 3.0.0'
data/README.md CHANGED
@@ -1,17 +1,28 @@
1
1
  # bnchmrkr
2
2
 
3
- i hate the name too, [but..](https://github.com/chorankates/bnchmrkr/issues/1)
3
+ [![build status](https://travis-ci.org/chorankates/bnchmrkr.svg)](https://travis-ci.org/chorankates/bnchmrkr) [![Gem Version](https://badge.fury.io/rb/bnchmrkr.png)](https://rubygems.org/gems/bnchmrkr)
4
4
 
5
- Bnchmrkr (Benchmarker) is a tool to help benchmark different method implementations
5
+ Bnchmrkr (Benchmarker) is a tool to help benchmark different method implementations in Ruby
6
6
 
7
7
  it is driven by [Benchmark](http://ruby-doc.org/stdlib-2.0.0/libdoc/benchmark/rdoc/Benchmark.html)
8
8
 
9
+ i hate the name too, [but..](https://github.com/chorankates/bnchmrkr/issues/1)
10
+
11
+ - [usage](#usage)
12
+ - [pre-built gem installation (stable)](#pre-built-gem-installation-stable)
13
+ - [from-source installation (latest)](#from-source-installation-latest)
14
+ - [examples](#examples)
15
+ - [code](#code)
16
+ - [output](#output)
17
+ - [converting from < 0.1.1](#upgrading)
18
+ - [methods](#methods)
19
+ - [Bnchmrkr](#bnchmrkr)
20
+ - [Bnchmrkr::Mark](#bnchmrkrmark)
21
+
9
22
  ## usage
10
23
 
11
24
  ### pre-built gem installation (stable)
12
25
 
13
- [![Gem Version](https://badge.fury.io/rb/bnchmrkr.png)](https://rubygems.org/gems/bnchmrkr)
14
-
15
26
  ```sh
16
27
  gem install bnchmrkr
17
28
  irb
@@ -22,8 +33,6 @@ irb(main):001:0> require 'bnchmrkr'
22
33
 
23
34
  ### from-source installation (latest)
24
35
 
25
- [![build status](https://travis-ci.org/chorankates/bnchmrkr.svg)](https://travis-ci.org/chorankates/bnchmrkr)
26
-
27
36
  ```sh
28
37
  git clone https://github.com/chorankates/bnchmrkr.git
29
38
  cd bnchmrkr
@@ -37,6 +46,8 @@ irb(main):001:0> require 'bnchmrkr'
37
46
 
38
47
  ## examples
39
48
 
49
+ ### code
50
+
40
51
  ```rb
41
52
  tester = Bnchmrkr.new({
42
53
  :count_to_1k => lambda { 1.upto(1000).each { |i| i } },
@@ -51,6 +62,8 @@ tester.benchmark!
51
62
  puts tester
52
63
  ```
53
64
 
65
+ ### output
66
+
54
67
  ```
55
68
  $ ruby examples/ls_vs_stat.rb
56
69
  fastest by type(ls) => 0.005704
@@ -76,16 +89,60 @@ overall:
76
89
  slowest => stat [0.076243]
77
90
  ```
78
91
 
79
- ## instance methods
92
+ ### upgrading
93
+
94
+ `Bnchmrkr` follows semantic versioning, which allowed a breaking change from versions `0.1.1` to `0.2.0`
95
+
96
+ `0.2.0` significantly improves the API and implementation for `Bnchmrkr`, and while upgrading will require some client side changes, they shouldn't be too onerous:
97
+
98
+ for the most part, you should be able to update the Hash lookup key with a method call of the same name,
99
+
100
+ #### < 0.1.1
101
+ ```rb
102
+ assert_equal(:iterative, tester.fastest_overall[:name])
103
+ assert_equal(:recursive, tester.slowest_overall[:name])
104
+ ```
105
+
106
+ #### >= 0.2.0
107
+ ```rb
108
+ assert_equal(:iterative, tester.fastest_overall.name)
109
+ assert_equal(:recursive, tester.slowest_overall.name)
110
+ ```
111
+
112
+ ## methods
113
+
114
+ ### `Bnchmrkr`
115
+ ```rb
116
+ attr_reader :executions, :marks, :fastest, :slowest
117
+ ...
118
+ def initialize(lambdas, executions = 100)
119
+ def types
120
+ def benchmark!
121
+ def inspect
122
+ def to_s
123
+ def fastest_by_type(type, mode = :real)
124
+ def slowest_by_type(type, mode = :real)
125
+ def fastest_overall
126
+ def slowest_overall
127
+ def is_faster?(a, b, mode = :real)
128
+ def is_slower?(a, b, mode = :real)
129
+ def faster_by_result(a, b, percent = true, mode = :real)
130
+ def faster_by_type(a, b, percent = true, mode = :real)
131
+ def slower_by_type(a, b, percent = true)
132
+ def slower_by_result(a, b, percent = true)
133
+ def calculate_overall(mode = :real)
134
+ ```
135
+
136
+ ### `Bnchmrkr::Mark`
137
+ ```rb
138
+ attr_reader :computed, :lambda, :name, :mode_precision
139
+ attr_reader :fastest, :slowest, :mean, :median, :mode, :total
140
+ ...
141
+ def initialize(name, lambda, mode_precision = 0)
142
+ def add_measure(measure)
143
+ def each(&block)
144
+ def compute
145
+ def inspect
146
+ def reset_computations
147
+ ```
80
148
  ```
81
- benchmark!
82
- count
83
- faster_by
84
- fastest_by_type
85
- fastest_overall
86
- is_faster?
87
- is_slower?
88
- results
89
- slowest_by_type
90
- slowest_overall
91
- ```
data/Rakefile CHANGED
@@ -61,8 +61,13 @@ end
61
61
 
62
62
  Reek::Rake::Task.new do |t|
63
63
  t.config_file = File.join(BASEDIR, '.reek')
64
- t.source_files = './**/*.rb'
64
+ t.source_files = FileList.new('lib/**/*.rb', 'test/**/*.rb')
65
65
  t.reek_opts = '--no-wiki-links'
66
66
  t.fail_on_error = false
67
67
  t.verbose = true
68
+ end
69
+
70
+ desc 'install the gem'
71
+ task :install do
72
+ sh 'gem install log4r-sequel'
68
73
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.0
@@ -1,100 +1,105 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
+ $LOAD_PATH << sprintf('%s/../lib', File.dirname(__FILE__))
4
+ require 'bnchmrkr/mark'
5
+
3
6
  require 'benchmark'
4
7
 
5
8
  # Bnchmrkr is a tool to help Benchmark.measure {} and compare different method implementations
6
9
  class Bnchmrkr
7
10
 
8
- attr_reader :count, :results
9
-
10
- def initialize(lambdas, count = 100)
11
+ UNCOMPUTED = :uncomputed
11
12
 
12
- @count = count
13
- @results = Hash.new
14
- @lambdas = lambdas
13
+ attr_reader :executions, :marks, :fastest, :slowest
15
14
 
16
- @fastest = { :name => :unknown, :measure => 2 ** 10 } # TODO really need to find a better max int
17
- @slowest = { :name => :unknown, :measure => 0 }
15
+ def initialize(lambdas, executions = 100)
16
+ @executions = executions
17
+ @marks = Hash.new
18
18
 
19
- @cache = Hash.new
19
+ # TODO need to cache these computations and allow reseting similar to how Bnchmrkr::Mark works
20
+ @fastest = UNCOMPUTED
21
+ @slowest = UNCOMPUTED
20
22
 
21
- @lambdas.each_pair do |name, l|
23
+ lambdas.each_pair do |name, l|
22
24
  unless name.class.eql?(Symbol) and l.class.eql?(Proc)
23
25
  raise ArgumentError.new(sprintf('expecting[Symbol,Proc], got[%s,%s]', name.class, l.class))
24
26
  end
27
+
28
+ @marks[name] = Bnchmrkr::Mark.new(name, l)
25
29
  end
26
30
 
27
- raise ArgumentError.new(sprintf('expecting[Fixnum], got[%s]', count.class)) unless count.class.eql?(Fixnum)
31
+ raise ArgumentError.new(sprintf('expecting[Fixnum], got[%s]', executions.class)) unless executions.class.eql?(Fixnum)
28
32
 
29
33
  end
30
34
 
31
- # return list of named lambdas known
35
+ # return list of named Bnchmrkr::Marks
32
36
  def types
33
- @lambdas.keys
37
+ @marks.keys
34
38
  end
35
39
 
36
- # 10 lines to actually do the work..
40
+ # < 10 lines to actually do the work..
37
41
  def benchmark!
38
- @lambdas.each_pair do |name, l|
39
- 1.upto(@count).each do |round|
40
- begin
41
- measure = Benchmark.measure {
42
- l.call
43
- }
44
- add_measure(name, measure)
45
- rescue => e
46
- add_measure(sprintf('%s-failed', name).to_sym, Benchmark.measure {})
47
- end
42
+ @marks.each_pair do |_name, mark|
43
+ 1.upto(@executions).each do |_execution|
44
+ measure = Benchmark.measure { mark.lambda.call }
45
+ mark.add_measure(measure)
48
46
  end
47
+
48
+ mark.compute # this is a safer place to do it than by computing on each measure, but still should consider putting this behind a flag
49
49
  end
50
50
 
51
+ calculate_overall
51
52
  end
52
53
 
53
- ## :specific => fastest, slowest, mean, median, total per lambda
54
- ## :overall => fastest, slowest for all
55
54
  def inspect
56
- { :overall => calculate_overall, :specific => calculate_per_lambda }
55
+ {
56
+ :fastest => {
57
+ :name => @fastest.name,
58
+ :fastest => @fastest.fastest.to_s.chomp,
59
+ :by => self.faster_by_result(@fastest.fastest, @slowest.slowest),
60
+ },
61
+ :slowest => {
62
+ :name => @slowest.name,
63
+ :slowest => @slowest.fastest.to_s.chomp,
64
+ },
65
+ :meta => {
66
+ :marks => @marks.keys,
67
+ :executions => @executions,
68
+ },
69
+ }
57
70
  end
58
71
 
59
72
  # overly intricate output formatting of overall and specific results
60
73
  def to_s
61
74
  string = String.new
62
75
  inspection = self.inspect
63
- return string unless inspection.nil? or inspection.has_key?(:overall)
64
76
 
65
- longest_key = inspection[:specific].keys.each { |i| i.length }.max.length + 5
77
+ longest_key = inspection.keys.each { |i| i.length }.max.length + 5
66
78
 
67
- inspection[:specific].keys.each do |i|
79
+ inspection.keys.each do |i|
68
80
  string << sprintf('%s:%s', i, "\n")
69
- inspection[:specific][i].keys.sort.each do |k|
70
- string << sprintf(" %#{longest_key}s => %s%s", k, inspection[:specific][i][k], "\n")
81
+ inspection[i].keys.each do |k|
82
+ string << sprintf(" %#{longest_key}s => %s%s", k, inspection[i][k], "\n")
71
83
  end
72
84
  end
73
85
 
74
- string << sprintf('overall:%s', "\n")
75
- inspection[:overall].each_pair do |type, measure|
76
- string << sprintf(" %#{longest_key}s => %s [%s]%s%s",
77
- type,
78
- measure[:name],
79
- measure[:measure],
80
- measure.has_key?(:faster_by) ? sprintf(' [faster by %s]', measure[:faster_by]) : '',
81
- "\n"
82
- )
83
- end
84
-
85
86
  string
86
87
  end
87
88
 
88
89
  # +type+ name of a lambda that is known
89
- # find and return the fastest execution per lambda of +type+
90
- def fastest_by_type(type)
91
- results = @results
92
- measures = Array.new
93
-
94
- return nil unless results.has_key?(type)
95
- results[type].collect { |r| measures << r.real}
90
+ # +mode+ method to use on the Bnchmrkr::Mark object ot compare (:real, :cstime, :cutime, :stime, :utime, :total)
91
+ # find and return the fastest Bnchrmrkr::Mark runtime per lambda of +type+
92
+ def fastest_by_type(type, mode = :real)
93
+ return UNCOMPUTED unless @marks.has_key?(type)
94
+ @marks[type].fastest.__send__(mode)
95
+ end
96
96
 
97
- measures.sort.first
97
+ # +type+ name of a lambda that is known
98
+ # +mode+ method to use on the Bnchmrkr::Mark object ot compare (:real, :cstime, :cutime, :stime, :utime, :total)
99
+ # find and return the slowest Bnchrmrkr::Mark runtime per lambda of +type+
100
+ def slowest_by_type(type, mode = :real)
101
+ return UNCOMPUTED unless @marks.has_key?(type)
102
+ @marks[type].slowest.__send__(mode)
98
103
  end
99
104
 
100
105
  # find and return the fastest overall execution (regardless of lambda type)
@@ -103,18 +108,6 @@ class Bnchmrkr
103
108
  @fastest
104
109
  end
105
110
 
106
- # +type+ name of a lambda that is known
107
- # find and return the slowest execution per lambda of +type+
108
- def slowest_by_type(type)
109
- results = @results
110
- measures = Array.new
111
-
112
- return nil unless results.has_key?(type)
113
- results[type].collect { |r| measures << r.real }
114
-
115
- measures.sort.last
116
- end
117
-
118
111
  # find and return the slowest overall execution (regardless of lambda type)
119
112
  def slowest_overall
120
113
  calculate_overall
@@ -125,28 +118,31 @@ class Bnchmrkr
125
118
  # +b+ Symbol that represents a known lambda
126
119
  # +mode+ :fastest, :slowest, :mean, :median, :total
127
120
  # return boolean if a is faster than b, false if invalid
128
- def is_faster?(a, b, mode = :total)
129
- result = calculate_per_lambda
130
- return false unless result.has_key?(a) and result.has_key?(b)
131
- result[a][mode] < result[b][mode]
121
+ def is_faster?(a, b, mode = :real)
122
+ return false unless @marks.has_key?(a) and @marks.has_key?(b)
123
+ # TODO not sure that we're doing the right thing here.. the fastest fast run should always be faster than the slowest slow run.. but should we be comparing fastest fast with slowest fast?
124
+ @marks[a].fastest.__send__(mode) < @marks[b].fastest.__send__(mode)
132
125
  end
133
126
 
134
- # +a+ {:name => name, :measure => measure}
135
- # +b+ {:name => name, :measure => measure}
127
+ # +a+ Bnchmrkr::Mark
128
+ # +b+ Bnchmrkr::Mark
136
129
  # +mode+ :fastest, :slowest, :mean, :median, :total
137
130
  # return boolean if a is faster than b, false if invalid
138
- def is_slower?(a, b, mode = :total)
131
+ def is_slower?(a, b, mode = :real)
139
132
  ! is_faster?(a, b, mode)
140
133
  end
141
134
 
142
- # +a+ {:name => name, :measure => measure}
143
- # +b+ {:name => name, :measure => measure}
135
+ # +a+ Bnchmrkr::Mark
136
+ # +b+ Bnchmrkr::Mark
144
137
  # +percent+ Boolean representing percent (String) or Float difference
145
138
  # return Float representing difference in measures, or false, if b is slower than a
146
- def faster_by_result(a, b, percent = true)
147
- return false if b[:measure] < a[:measure]
139
+ def faster_by_result(a, b, percent = true, mode = :real)
140
+ measure_a = a.__send__(mode)
141
+ measure_b = b.__send__(mode)
142
+
143
+ return false if measure_b < measure_a
148
144
 
149
- faster = (b[:measure] - a[:measure]) / a[:measure]
145
+ faster = (measure_b - measure_a) / measure_a
150
146
  percent ? sprintf('%4f%', faster * 100) : faster
151
147
  end
152
148
 
@@ -154,9 +150,9 @@ class Bnchmrkr
154
150
  # +b+ Symbol representing name of known lambda type
155
151
  # +percent+ Boolean representing percent (String) or Float difference
156
152
  # return Float representing difference in measures, or false, if b is slower than a
157
- def faster_by_type(a, b, percent = true)
158
- fastest_a = fastest_by_type(a)
159
- fastest_b = fastest_by_type(b)
153
+ def faster_by_type(a, b, percent = true, mode = :real)
154
+ fastest_a = fastest_by_type(a).__send__(mode)
155
+ fastest_b = fastest_by_type(b).__send__(mode)
160
156
 
161
157
  return false if fastest_b < fastest_a
162
158
 
@@ -174,63 +170,27 @@ class Bnchmrkr
174
170
 
175
171
  private
176
172
 
177
- def add_measure(name, measure)
178
- self.results[name] = Array.new unless self.results.has_key?(name)
179
- self.results[name] << measure
180
- end
181
-
182
- # +mode_precision+ Fixnum indicating number of digits to consider during mode calculation, default to 0, which will use all signal
183
- # from existing results, generate some statistics per lambda type
184
- def calculate_per_lambda(mode_precision = 0)
185
- hash = Hash.new
186
-
187
- # TODO come up with way to not recompute unless contents have changed -- https://github.com/chorankates/bnchmrkr/issues/3
188
-
189
- @results.each_pair do |name, measures|
190
- hash[name] = Hash.new
191
- frequency_hash = Hash.new(0)
192
- mode_candidate = { :frequency => 1, :operand => nil }
193
- total = 0
194
-
195
- sorted = measures.sort { |a,b| a.real <=> b.real }
196
- measures.each do |measure|
197
- operand = mode_precision.equal?(0) ? measure.real : measure.real.round(mode_precision)
198
- frequency_hash[operand] += 1
173
+ # update fastest/slowest, return in a named Hash
174
+ def calculate_overall(mode = :real)
199
175
 
200
- # i hate maths
201
- if frequency_hash[operand] > mode_candidate[:frequency] and frequency_hash[operand] > 1
202
- mode_candidate[:frequency] = frequency_hash[operand]
203
- mode_candidate[:operand] = operand
204
- end
176
+ @marks.each_pair do |_name, mark|
177
+ if @fastest.eql?(:uncomputed) or mark.fastest.__send__(mode) < @fastest.fastest.__send__(mode)
178
+ @fastest = mark
205
179
  end
206
180
 
207
- measures.collect {|m| total += m.real }
208
- hash[name][:fastest] = sorted.first.real
209
- hash[name][:slowest] = sorted.last.real
210
- hash[name][:mean] = sprintf('%5f', total / sorted.size).to_f
211
- hash[name][:median] = sorted[(sorted.size / 2)].real
212
- # TODO need to handle the rare case that we have multiple modes -- https://github.com/chorankates/bnchmrkr/issues/4
213
- hash[name][:mode] = mode_candidate[:operand] # collect key name with highest value
214
- hash[name][:total] = sprintf('%5f', total).to_f
215
- end
216
-
217
- hash
218
- end
219
-
220
- # update fastest/slowest, return in a named Hash
221
- def calculate_overall
222
- calculate_per_lambda.each_pair do |name,results|
223
- if results[:fastest] < @fastest[:measure]
224
- @fastest = { :name => name, :measure => results[:fastest] }
181
+ if @slowest.eql?(:uncomputed) or mark.slowest.__send__(mode) > @slowest.slowest.__send__(mode)
182
+ @slowest = mark
225
183
  end
226
184
 
227
- if results[:slowest] > @slowest[:measure]
228
- @slowest = { :name => name, :measure => results[:slowest] }
229
- end
230
185
  end
231
186
 
232
- @fastest[:faster_by] = self.faster_by_result(@fastest, @slowest)
233
- { :fastest => @fastest, :slowest => @slowest }
187
+ {
188
+ :fastest => @fastest.fastest.__send__(mode),
189
+ :fastest_name => @fastest.name,
190
+ :slowest => @slowest.slowest.__send__(mode),
191
+ :slowest_name => @slowest.name,
192
+ :faster_by => self.faster_by_result(@fastest.fastest, @slowest.slowest)
193
+ }
234
194
  end
235
195
 
236
- end
196
+ end
@@ -0,0 +1,95 @@
1
+ # oh ruby
2
+ class Bnchmrkr; end
3
+
4
+ # Bnchmrkr::Mark represents individual lambda runs within a Bnchmrkr run
5
+ class Bnchmrkr::Mark
6
+ include Enumerable
7
+
8
+ attr_reader :computed, :lambda, :name, :mode_precision
9
+ attr_reader :fastest, :slowest, :mean, :median, :mode, :total
10
+
11
+ def initialize(name, lambda, mode_precision = 0)
12
+ @name = name
13
+ @lambda = lambda # TODO this name is going to cause problems
14
+ @measures = Array.new
15
+
16
+ @mode_precision = mode_precision
17
+
18
+ reset_computations # initialize to known values
19
+ end
20
+
21
+ # +measure+ Benchmark.measure{} result
22
+ def add_measure(measure)
23
+ @measures << measure
24
+ reset_computations if @computed # as soon as a measure is added, previous computations are invalid
25
+ end
26
+
27
+ # allows Array-like behavior on the Array of measurements contained in a Bnchmrkr::Mark
28
+ def each(&block)
29
+ @measures.each(&block)
30
+ end
31
+
32
+ def compute
33
+ frequency_hash = Hash.new(0)
34
+ total = 0
35
+
36
+ sorted = @measures.sort { |a,b| a.real <=> b.real }
37
+
38
+ @measures.each do |measure|
39
+ operand = @mode_precision.equal?(0) ? measure.real : measure.real.round(@mode_precision)
40
+ frequency_hash[operand] += 1
41
+ end
42
+
43
+ max_frequency = frequency_hash.values.max
44
+ mode_candidate = frequency_hash.select{ |_operand, frequency| frequency.equal?(max_frequency) }.keys
45
+
46
+ if max_frequency.equal?(1)
47
+ mode = nil
48
+ else
49
+ mode = mode_candidate.size > 1 ? mode_candidate : mode_candidate.first
50
+ end
51
+
52
+ sorted.collect { |r| total += r.real }
53
+ @fastest = sorted.first
54
+ @slowest = sorted.last
55
+ @mean = sprintf('%5f', total / sorted.size).to_f
56
+ @median = sorted[(sorted.size / 2)]
57
+ @mode = mode
58
+ @total = sprintf('%5f', total).to_f
59
+
60
+ @computed = true
61
+ end
62
+
63
+ # a semi-fancy and somewhat fragile inspect method
64
+ ## try to compute if we haven't been, ensuring we always return a hash
65
+ def inspect
66
+ begin
67
+ self.compute unless @computed
68
+ {
69
+ :name => @name,
70
+ :fastest => @fastest.real,
71
+ :slowest => @slowest.real,
72
+ :mean => @mean,
73
+ :median => @median,
74
+ :mode => @mode,
75
+ :total => @total,
76
+ }
77
+ rescue => e
78
+ { :name => @name, :computed => @computed }
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ # reset internal computed values, used when a new measure is added
85
+ def reset_computations
86
+ @computed = false
87
+ @fastest = :uncomputed
88
+ @slowest = :uncomputed
89
+ @mean = :uncomputed
90
+ @median = :uncomputed
91
+ @mode = :uncomputed
92
+ @total = :uncomputed
93
+ end
94
+
95
+ end
@@ -12,8 +12,10 @@ class TestExamples < Test::Unit::TestCase
12
12
  @files.each do |file|
13
13
  raw = `ruby #{file}`
14
14
 
15
- assert_true($?.success?)
16
- assert_not_nil(raw)
15
+ generic_failure_message = sprintf('[%s] returned [%s]: [%s]', File.basename(file), $?, raw)
16
+
17
+ assert_true($?.success?, generic_failure_message)
18
+ assert_not_nil(raw, generic_failure_message)
17
19
  end
18
20
  end
19
21
 
@@ -17,74 +17,85 @@ class TestContrived < Test::Unit::TestCase
17
17
  end
18
18
 
19
19
  def test_fastest
20
- assert_equal(:count_to_1k, @tester.fastest_overall[:name])
21
- assert_true(@tester.fastest_overall[:measure] < @tester.slowest_overall[:measure])
20
+ assert_equal(:count_to_1k, @tester.fastest_overall.name)
21
+ assert_true(@tester.fastest_overall.fastest.real < @tester.slowest_overall.slowest.real)
22
22
  end
23
23
 
24
24
  def test_slowest
25
25
  slowest_overall = @tester.slowest_overall
26
26
  fastest_overall = @tester.fastest_overall
27
27
 
28
- assert_equal(:count_to_100k, slowest_overall[:name])
29
- assert_true(slowest_overall[:measure] > fastest_overall[:measure])
28
+ generic_failure_message = sprintf('fast[%s] slow[%s]', fastest_overall, slowest_overall)
29
+
30
+ assert_equal(:count_to_100k, slowest_overall.name, generic_failure_message)
31
+ assert_true(slowest_overall.slowest.real > fastest_overall.fastest.real, generic_failure_message)
30
32
  end
31
33
 
32
34
  def test_speed_by_type
33
35
  @tester.types.each do |type|
34
- slowest = @tester.slowest_by_type(type)
35
- fastest = @tester.fastest_by_type(type)
36
+ slowest = @tester.slowest_by_type(type).real
37
+ fastest = @tester.fastest_by_type(type).real
38
+
39
+ generic_failure_message = sprintf('fast[%s] slow[%s]', fastest, slowest)
36
40
 
37
- assert_true(slowest > fastest)
41
+ assert_true(slowest > fastest, generic_failure_message)
38
42
  end
39
43
  end
40
44
 
41
45
  def test_is_faster?
42
- fast = @tester.fastest_overall[:name]
43
- slow = @tester.slowest_overall[:name]
46
+ fast = @tester.fastest_overall.name
47
+ slow = @tester.slowest_overall.name
48
+
49
+ generic_failure_message = sprintf('fast[%s] slow[%s]', fast, slow)
44
50
 
45
51
  forward = @tester.is_faster?(fast, slow)
46
52
  reverse = @tester.is_faster?(slow, fast)
47
53
  equal = @tester.is_faster?(fast, fast)
48
54
 
49
- assert_not_equal(forward, reverse)
50
- assert_true(forward)
51
- assert_false(reverse)
52
- assert_false(equal)
55
+ assert_not_equal(forward, reverse, generic_failure_message)
56
+ assert_true(forward, generic_failure_message)
57
+ assert_false(reverse, generic_failure_message)
58
+ assert_false(equal, generic_failure_message)
53
59
  end
54
60
 
55
61
  def test_is_slower?
56
- fast = @tester.fastest_overall[:name]
57
- slow = @tester.slowest_overall[:name]
62
+ fast = @tester.fastest_overall.name
63
+ slow = @tester.slowest_overall.name
64
+
65
+ generic_failure_message = sprintf('fast[%s] slow[%s]', fast, slow)
58
66
 
59
67
  forward = @tester.is_slower?(slow, fast)
60
68
  reverse = @tester.is_slower?(fast, slow)
61
69
  equal = @tester.is_slower?(slow, slow)
62
70
 
63
- assert_not_equal(forward, reverse)
64
- assert_true(forward)
65
- assert_false(reverse)
66
- assert_true(equal) # ugh, this is misleading.. but is_slower? is boolean opposite of is_slower?
71
+ assert_not_equal(forward, reverse, generic_failure_message)
72
+ assert_true(forward, generic_failure_message)
73
+ assert_false(reverse, generic_failure_message)
74
+ assert_true(equal, sprintf('fast[%s] slow[%s]', fast, slow))
67
75
  end
68
76
 
69
77
  def test_faster_by_result
70
- fast = @tester.fastest_overall
71
- slow = @tester.slowest_overall
78
+ fast = @tester.fastest_overall.fastest
79
+ slow = @tester.slowest_overall.slowest
80
+
81
+ generic_failure_message = sprintf('fast[%s] slow[%s]', fast, slow)
72
82
 
73
- assert_not_nil(@tester.faster_by_result(fast, slow, true))
74
- assert_match(/\d+\.\d+%/, @tester.faster_by_result(fast, slow, true))
75
- assert_true(@tester.faster_by_result(fast, slow, false).is_a?(Float))
76
- assert_false(@tester.faster_by_result(slow, fast))
83
+ assert_not_nil(@tester.faster_by_result(fast, slow, true), generic_failure_message)
84
+ assert_match(/\d+\.\d+%/, @tester.faster_by_result(fast, slow, true), generic_failure_message)
85
+ assert_true(@tester.faster_by_result(fast, slow, false).is_a?(Float), generic_failure_message)
86
+ assert_false(@tester.faster_by_result(slow, fast), generic_failure_message)
77
87
  end
78
88
 
79
89
  def test_faster_by_type
80
- fastest = @tester.fastest_overall[:name]
81
- slowest = @tester.slowest_overall[:name]
90
+ fastest = @tester.fastest_overall.name
91
+ slowest = @tester.slowest_overall.name
82
92
 
83
- assert_not_nil(@tester.faster_by_type(fastest, slowest, true))
84
- assert_match(/\d+\.\d+%/, @tester.faster_by_type(fastest, slowest, true))
85
- assert_true(@tester.faster_by_type(fastest, slowest, false).is_a?(Float))
86
- assert_false(@tester.faster_by_type(slowest, fastest))
93
+ generic_failure_message = sprintf('fast[%s] slow[%s]', fastest, slowest)
87
94
 
95
+ assert_not_nil(@tester.faster_by_type(fastest, slowest, true), generic_failure_message)
96
+ assert_match(/\d+\.\d+%/, @tester.faster_by_type(fastest, slowest, true), generic_failure_message)
97
+ assert_true(@tester.faster_by_type(fastest, slowest, false).is_a?(Float), generic_failure_message)
98
+ assert_false(@tester.faster_by_type(slowest, fastest), generic_failure_message)
88
99
  end
89
100
 
90
101
  end
@@ -6,17 +6,21 @@ class TestExceptions < Test::Unit::TestCase
6
6
 
7
7
  def setup; end
8
8
 
9
- def test_lambdas_will_raise
9
+ def test_lambdas_will_raise # but we will catch them
10
+ symbol = :divbyzero
11
+
10
12
  tester = Bnchmrkr.new({
11
- :divbyzero => lambda {
13
+ symbol => lambda {
12
14
  10 / 0
13
15
  },
14
16
  })
15
17
 
16
- assert_nothing_raised do
18
+ e = assert_raise do
17
19
  tester.benchmark!
18
- assert_true(tester.results.has_key?('divbyzero-failed'.to_sym))
19
20
  end
21
+
22
+ assert_true(tester.marks.has_key?(symbol))
23
+ assert_equal(ZeroDivisionError, e.class)
20
24
  end
21
25
 
22
26
  def test_lambdas_wont_raise_anyway
@@ -26,7 +30,7 @@ class TestExceptions < Test::Unit::TestCase
26
30
 
27
31
  assert_nothing_raised do
28
32
  tester.benchmark!
29
- assert_true(tester.results.has_key?(:foo))
33
+ assert_true(tester.marks.has_key?(:foo))
30
34
  end
31
35
 
32
36
  end
@@ -19,6 +19,19 @@ class TestInitialize < Test::Unit::TestCase
19
19
 
20
20
  end
21
21
 
22
+ # TODO this is really testing Bnchmrkr::Mark objects
23
+ def test_initial_values
24
+
25
+ bnchmrkr = Bnchmrkr.new({:foo => lambda {}})
26
+
27
+ bnchmrkr.marks.each_pair do |name, m|
28
+ [ :fastest, :slowest, :mean, :median, :mode, :total ].each do |a|
29
+ assert_equal(:uncomputed, m.__send__(a), sprintf('failed[%s] for[%s]', a, name))
30
+ end
31
+ end
32
+
33
+ end
34
+
22
35
  def test_invalid
23
36
 
24
37
  # not a proc
@@ -49,8 +62,6 @@ class TestInitialize < Test::Unit::TestCase
49
62
  Bnchmrkr.new({:foo => :bar}, 1.1)
50
63
  end
51
64
 
52
-
53
-
54
65
  end
55
66
 
56
67
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bnchmrkr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Conor Horan-Kates
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-23 00:00:00.000000000 Z
11
+ date: 2016-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -20,7 +20,7 @@ dependencies:
20
20
  - - ~>
21
21
  - !ruby/object:Gem::Version
22
22
  version: 10.5.0
23
- type: :runtime
23
+ type: :development
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
@@ -106,6 +106,7 @@ files:
106
106
  - Rakefile
107
107
  - VERSION
108
108
  - lib/bnchmrkr.rb
109
+ - lib/bnchmrkr/mark.rb
109
110
  - test/examples/test_examples.rb
110
111
  - test/unit/test_contrived.rb
111
112
  - test/unit/test_exceptions.rb
@@ -130,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
131
  version: '0'
131
132
  requirements: []
132
133
  rubyforge_project:
133
- rubygems_version: 2.2.2
134
+ rubygems_version: 2.4.8
134
135
  signing_key:
135
136
  specification_version: 4
136
137
  summary: compare execution time