benchmark-ips 2.7.1 → 2.8.4

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
- SHA1:
3
- metadata.gz: dbf6e5f13ed635a69ab099631340497b0305df4e
4
- data.tar.gz: 010de92f654e0dc25d1770b9ce83944540d71920
2
+ SHA256:
3
+ metadata.gz: 574d0cb84279e02316df55c8d96f396b2a10301b15639ede54198eb278fd6ba2
4
+ data.tar.gz: d4ddabd9c093fd2c9559870818543d105c6fb2bbf3d38ad397b3bdf0df691397
5
5
  SHA512:
6
- metadata.gz: 97c18c2d2bf77dd41320fdd5336b2aa562fc0db2b46f8a38866d087491c3d3a52b0e732b2169eaf330bb30e14822f01ff5d4f2f670d99a05624e30721c32bf53
7
- data.tar.gz: 3cc170d620547e78039a1e007a2fe17aaa6dd0ac6c06d571b0e26aaab0933db69cde62cc63042d3fb6e2c0d2fb54135ea5f5b6c519923a0787284583f7564b4f
6
+ metadata.gz: 701868e1d0e33101daaf7e5005245b327dbf3b3d02fb19a8e89eb39563540b5ca5b1ff91c4e3cd4bd9dd54f97537ca48e668608742ee0569a13336818ddfef23
7
+ data.tar.gz: a032682bd26691ad5d4a837ba956b9acf50e45c69036e07bdda347791f78bf388cb3339873fd6d5ca9cbf6edeec5090c15ae6fa56a712f6d0f0340729ce8e792
@@ -1,3 +1,41 @@
1
+ === 2.8.4 / 2020-12-03
2
+
3
+ * Bug fix
4
+ * Fixed hold! when results file does not exist.
5
+
6
+ === 2.8.3 / 2020-08-28
7
+
8
+ * Bug fix
9
+ * Fixed inaccuracy caused by integer overflows.
10
+
11
+ === 2.8.2 / 2020-05-04
12
+
13
+ * Bug fix
14
+ * Fixed problems with Manifest.txt.
15
+ * Empty interim results files are ignored.
16
+
17
+ === 2.8.0 / 2020-05-01
18
+
19
+ * Feature
20
+ * Allow running with empty ips block.
21
+ * Added save! method for saving interim results.
22
+ * Run more than just 1 cycle during warmup to reduce overhead.
23
+ * Optimized Job::Entry hot-path for fairer results on JRuby/TruffleRuby.
24
+
25
+ * Bug fix
26
+ * Removed the warmup section if set to 0.
27
+ * Added some RDoc docs.
28
+ * Added some examples in examples/
29
+
30
+ === 2.7.2 / 2016-08-18
31
+
32
+ * 1 bug fix:
33
+ * Restore old accessors. Fixes #76
34
+
35
+ === 2.7.1 / 2016-08-08
36
+
37
+ Add missing files
38
+
1
39
  === 2.7.0 / 2016-08-05
2
40
 
3
41
  * 1 minor features:
@@ -1,5 +1,4 @@
1
1
  .autotest
2
- Gemfile.lock
3
2
  History.txt
4
3
  Manifest.txt
5
4
  README.md
@@ -13,5 +12,6 @@ lib/benchmark/ips/report.rb
13
12
  lib/benchmark/ips/share.rb
14
13
  lib/benchmark/ips/stats/bootstrap.rb
15
14
  lib/benchmark/ips/stats/sd.rb
15
+ lib/benchmark/ips/stats/stats_metric.rb
16
16
  lib/benchmark/timing.rb
17
17
  test/test_benchmark_ips.rb
data/README.md CHANGED
@@ -1,13 +1,14 @@
1
+ # benchmark-ips
2
+
3
+ * rdoc :: http://rubydoc.info/gems/benchmark-ips
4
+ * home :: https://github.com/evanphx/benchmark-ips
5
+
1
6
  [![Gem Version](https://badge.fury.io/rb/benchmark-ips.svg)](http://badge.fury.io/rb/benchmark-ips)
2
7
  [![Build Status](https://secure.travis-ci.org/evanphx/benchmark-ips.svg)](http://travis-ci.org/evanphx/benchmark-ips)
3
8
  [![Inline docs](http://inch-ci.org/github/evanphx/benchmark-ips.svg)](http://inch-ci.org/github/evanphx/benchmark-ips)
4
9
 
5
- # benchmark-ips
6
-
7
10
  * https://github.com/evanphx/benchmark-ips
8
11
 
9
- * [documentation](http://rubydoc.info/gems/benchmark-ips)
10
-
11
12
  ## DESCRIPTION:
12
13
 
13
14
  An iterations per second enhancement to Benchmark.
@@ -155,6 +156,11 @@ This will run only one benchmarks each time you run the command, storing
155
156
  results in the specified file. The file is deleted when all results have been
156
157
  gathered and the report is shown.
157
158
 
159
+ Alternatively, if you prefer a different approach, the `save!` command is
160
+ available. Examples for [hold!](examples/hold.rb) and [save!](examples/save.rb) are available in
161
+ the `examples/` directory.
162
+
163
+
158
164
  ### Multiple iterations
159
165
 
160
166
  In some cases you may want to run multiple iterations of the warmup and
@@ -40,18 +40,14 @@ module Benchmark
40
40
 
41
41
  $stdout.puts "\nComparison:"
42
42
 
43
- $stdout.printf "%20s: %10.1f i/s\n", best.label, best.stats.central_tendency
43
+ $stdout.printf "%20s: %10.1f i/s\n", best.label.to_s, best.stats.central_tendency
44
44
 
45
45
  sorted.each do |report|
46
46
  name = report.label.to_s
47
-
47
+
48
48
  $stdout.printf "%20s: %10.1f i/s - ", name, report.stats.central_tendency
49
-
50
- best_low = best.stats.central_tendency - best.stats.error
51
- report_high = report.stats.central_tendency + report.stats.error
52
- overlaps = report_high > best_low
53
-
54
- if overlaps
49
+
50
+ if report.stats.overlaps?(best.stats)
55
51
  $stdout.print "same-ish: difference falls within error"
56
52
  else
57
53
  slowdown, error = report.stats.slowdown(best.stats)
@@ -61,7 +57,7 @@ module Benchmark
61
57
  end
62
58
  $stdout.print " slower"
63
59
  end
64
-
60
+
65
61
  $stdout.puts
66
62
  end
67
63
 
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require 'benchmark/timing'
3
3
  require 'benchmark/compare'
4
+ require 'benchmark/ips/stats/stats_metric'
4
5
  require 'benchmark/ips/stats/sd'
5
6
  require 'benchmark/ips/stats/bootstrap'
6
7
  require 'benchmark/ips/report'
@@ -15,10 +16,10 @@ module Benchmark
15
16
  module IPS
16
17
 
17
18
  # Benchmark-ips Gem version.
18
- VERSION = "2.7.1"
19
+ VERSION = "2.8.4"
19
20
 
20
21
  # CODENAME of current version.
21
- CODENAME = "Cultivating Confidence"
22
+ CODENAME = "Tardy Turtle"
22
23
 
23
24
  # Measure code in block, each code's benchmarked result will display in
24
25
  # iteration per second with standard deviation in given time.
@@ -54,10 +55,17 @@ module Benchmark
54
55
 
55
56
  yield job
56
57
 
57
- job.load_held_results if job.hold? && job.held_results?
58
+ job.load_held_results
58
59
 
59
60
  job.run
60
61
 
62
+ if job.run_single? && job.all_results_have_been_run?
63
+ job.clear_held_results
64
+ else
65
+ job.save_held_results
66
+ puts '', 'Pausing here -- run Ruby again to measure the next benchmark...' if job.run_single?
67
+ end
68
+
61
69
  $stdout.sync = sync
62
70
  job.run_comparison
63
71
  job.generate_json
@@ -102,4 +110,68 @@ module Benchmark
102
110
  end
103
111
 
104
112
  extend Benchmark::IPS # make ips available as module-level method
113
+
114
+ ##
115
+ # :singleton-method: ips
116
+ #
117
+ # require 'benchmark/ips'
118
+ #
119
+ # Benchmark.ips do |x|
120
+ # # Configure the number of seconds used during
121
+ # # the warmup phase (default 2) and calculation phase (default 5)
122
+ # x.config(:time => 5, :warmup => 2)
123
+ #
124
+ # # These parameters can also be configured this way
125
+ # x.time = 5
126
+ # x.warmup = 2
127
+ #
128
+ # # Typical mode, runs the block as many times as it can
129
+ # x.report("addition") { 1 + 2 }
130
+ #
131
+ # # To reduce overhead, the number of iterations is passed in
132
+ # # and the block must run the code the specific number of times.
133
+ # # Used for when the workload is very small and any overhead
134
+ # # introduces incorrectable errors.
135
+ # x.report("addition2") do |times|
136
+ # i = 0
137
+ # while i < times
138
+ # 1 + 2
139
+ # i += 1
140
+ # end
141
+ # end
142
+ #
143
+ # # To reduce overhead even more, grafts the code given into
144
+ # # the loop that performs the iterations internally to reduce
145
+ # # overhead. Typically not needed, use the |times| form instead.
146
+ # x.report("addition3", "1 + 2")
147
+ #
148
+ # # Really long labels should be formatted correctly
149
+ # x.report("addition-test-long-label") { 1 + 2 }
150
+ #
151
+ # # Compare the iterations per second of the various reports!
152
+ # x.compare!
153
+ # end
154
+ #
155
+ # This will generate the following report:
156
+ #
157
+ # Calculating -------------------------------------
158
+ # addition 71.254k i/100ms
159
+ # addition2 68.658k i/100ms
160
+ # addition3 83.079k i/100ms
161
+ # addition-test-long-label
162
+ # 70.129k i/100ms
163
+ # -------------------------------------------------
164
+ # addition 4.955M (± 8.7%) i/s - 24.155M
165
+ # addition2 24.011M (± 9.5%) i/s - 114.246M
166
+ # addition3 23.958M (±10.1%) i/s - 115.064M
167
+ # addition-test-long-label
168
+ # 5.014M (± 9.1%) i/s - 24.545M
169
+ #
170
+ # Comparison:
171
+ # addition2: 24011974.8 i/s
172
+ # addition3: 23958619.8 i/s - 1.00x slower
173
+ # addition-test-long-label: 5014756.0 i/s - 4.79x slower
174
+ # addition: 4955278.9 i/s - 4.85x slower
175
+ #
176
+ # See also Benchmark::IPS
105
177
  end
@@ -9,6 +9,7 @@ module Benchmark
9
9
  # The percentage of the expected runtime to allow
10
10
  # before reporting a weird runtime
11
11
  MAX_TIME_SKEW = 0.05
12
+ POW_2_30 = 1 << 30
12
13
 
13
14
  # Two-element arrays, consisting of label and block pairs.
14
15
  # @return [Array<Entry>] list of entries
@@ -58,11 +59,12 @@ module Benchmark
58
59
  @stdout = opts[:quiet] ? nil : StdoutReport.new
59
60
  @list = []
60
61
  @compare = false
62
+ @run_single = false
61
63
  @json_path = false
62
64
  @held_path = nil
63
65
  @held_results = nil
64
66
 
65
- @timing = {}
67
+ @timing = Hash.new 1 # default to 1 in case warmup isn't run
66
68
  @full_report = Report.new
67
69
 
68
70
  # Default warmup and calculation time in seconds.
@@ -94,7 +96,7 @@ module Benchmark
94
96
  @compare
95
97
  end
96
98
 
97
- # Set @compare to true.
99
+ # Run comparison utility.
98
100
  def compare!
99
101
  @compare = true
100
102
  end
@@ -105,9 +107,27 @@ module Benchmark
105
107
  !!@held_path
106
108
  end
107
109
 
108
- # Set @hold to true.
110
+ # Hold after each iteration.
111
+ # @param held_path [String] File name to store hold file.
109
112
  def hold!(held_path)
110
113
  @held_path = held_path
114
+ @run_single = true
115
+ end
116
+
117
+ # Save interim results. Similar to hold, but all reports are run
118
+ # The report label must change for each invocation.
119
+ # One way to achieve this is to include the version in the label.
120
+ # @param held_path [String] File name to store hold file.
121
+ def save!(held_path)
122
+ @held_path = held_path
123
+ @run_single = false
124
+ end
125
+
126
+ # Return true if items are to be run one at a time.
127
+ # For the traditional hold, this is true
128
+ # @return [Boolean] Run just a single item?
129
+ def run_single?
130
+ @run_single
111
131
  end
112
132
 
113
133
  # Return true if job needs to generate json.
@@ -116,7 +136,7 @@ module Benchmark
116
136
  !!@json_path
117
137
  end
118
138
 
119
- # Set @json_path to given path, defaults to "data.json".
139
+ # Generate json to given path, defaults to "data.json".
120
140
  def json!(path="data.json")
121
141
  @json_path = path
122
142
  end
@@ -166,84 +186,112 @@ module Benchmark
166
186
  def iterations_per_sec cycles, time_us
167
187
  MICROSECONDS_PER_SECOND * (cycles.to_f / time_us.to_f)
168
188
  end
169
-
170
- def held_results?
171
- File.exist?(@held_path)
172
- end
173
-
189
+
174
190
  def load_held_results
191
+ return unless @held_path && File.exist?(@held_path) && !File.zero?(@held_path)
192
+ require "json"
193
+ @held_results = {}
194
+ JSON.load(IO.read(@held_path)).each do |result|
195
+ @held_results[result['item']] = result
196
+ create_report(result['item'], result['measured_us'], result['iter'],
197
+ create_stats(result['samples']), result['cycles'])
198
+ end
199
+ end
200
+
201
+ def save_held_results
202
+ return unless @held_path
175
203
  require "json"
176
- @held_results = Hash[File.open(@held_path).map { |line|
177
- result = JSON.parse(line)
178
- [result['item'], result]
179
- }]
204
+ data = full_report.entries.map { |e|
205
+ {
206
+ 'item' => e.label,
207
+ 'measured_us' => e.microseconds,
208
+ 'iter' => e.iterations,
209
+ 'samples' => e.samples,
210
+ 'cycles' => e.measurement_cycle
211
+ }
212
+ }
213
+ IO.write(@held_path, JSON.generate(data) << "\n")
214
+ end
215
+
216
+ def all_results_have_been_run?
217
+ @full_report.entries.size == @list.size
180
218
  end
181
-
219
+
220
+ def clear_held_results
221
+ File.delete @held_path if File.exist?(@held_path)
222
+ end
223
+
182
224
  def run
183
- @stdout.start_warming if @stdout
184
- @iterations.times do
185
- run_warmup
225
+ if @warmup && @warmup != 0 then
226
+ @stdout.start_warming if @stdout
227
+ @iterations.times do
228
+ run_warmup
229
+ end
186
230
  end
187
-
231
+
188
232
  @stdout.start_running if @stdout
189
-
190
- held = nil
191
-
233
+
192
234
  @iterations.times do |n|
193
- held = run_benchmark
235
+ run_benchmark
194
236
  end
195
237
 
196
238
  @stdout.footer if @stdout
197
-
198
- if held
199
- puts
200
- puts 'Pausing here -- run Ruby again to measure the next benchmark...'
201
- end
202
239
  end
203
240
 
204
241
  # Run warmup.
205
242
  def run_warmup
206
243
  @list.each do |item|
207
- next if hold? && @held_results && @held_results.key?(item.label)
208
-
244
+ next if run_single? && @held_results && @held_results.key?(item.label)
245
+
209
246
  @suite.warming item.label, @warmup if @suite
210
247
  @stdout.warming item.label, @warmup if @stdout
211
248
 
212
249
  Timing.clean_env
213
250
 
251
+ # Run for up to half of the configured warmup time with an increasing
252
+ # number of cycles to reduce overhead and improve accuracy.
253
+ # This also avoids running with a constant number of cycles, which a
254
+ # JIT might speculate on and then have to recompile in #run_benchmark.
214
255
  before = Timing.now
215
- target = Timing.add_second before, @warmup
256
+ target = Timing.add_second before, @warmup / 2.0
216
257
 
217
- warmup_iter = 0
218
-
219
- while Timing.now < target
220
- item.call_times(1)
221
- warmup_iter += 1
258
+ cycles = 1
259
+ warmup_iter = 1
260
+ warmup_time_us = 0.0
261
+ while Timing.now + warmup_time_us * 2 < target
262
+ t0 = Timing.now
263
+ item.call_times cycles
264
+ t1 = Timing.now
265
+ warmup_iter = cycles
266
+ warmup_time_us = Timing.time_us(t0, t1)
267
+
268
+ # If the number of cycles would go outside the 32-bit signed integers range
269
+ # then exit the loop to avoid overflows and start the 100ms warmup runs
270
+ break if cycles >= POW_2_30
271
+ cycles *= 2
222
272
  end
223
273
 
224
- after = Timing.now
274
+ cycles = cycles_per_100ms warmup_time_us, warmup_iter
275
+ @timing[item] = cycles
225
276
 
226
- warmup_time_us = Timing.time_us(before, after)
227
-
228
- @timing[item] = cycles_per_100ms warmup_time_us, warmup_iter
277
+ # Run for the remaining of warmup in a similar way as #run_benchmark.
278
+ target = Timing.add_second before, @warmup
279
+ while Timing.now + MICROSECONDS_PER_100MS < target
280
+ item.call_times cycles
281
+ end
229
282
 
230
283
  @stdout.warmup_stats warmup_time_us, @timing[item] if @stdout
231
284
  @suite.warmup_stats warmup_time_us, @timing[item] if @suite
232
-
233
- break if hold?
285
+
286
+ break if run_single?
234
287
  end
235
288
  end
236
289
 
237
290
  # Run calculation.
238
291
  def run_benchmark
239
292
  @list.each do |item|
240
- if hold? && @held_results && @held_results.key?(item.label)
241
- result = @held_results[item.label]
242
- create_report(item.label, result['measured_us'], result['iter'],
243
- create_stats(result['samples']), result['cycles'])
244
- next
245
- end
246
-
293
+ next if run_single? && @held_results && @held_results.key?(item.label)
294
+
247
295
  @suite.running item.label, @time if @suite
248
296
  @stdout.running item.label, @time if @stdout
249
297
 
@@ -257,7 +305,7 @@ module Benchmark
257
305
  cycles = @timing[item]
258
306
 
259
307
  target = Timing.add_second Timing.now, @time
260
-
308
+
261
309
  while (before = Timing.now) < target
262
310
  item.call_times cycles
263
311
  after = Timing.now
@@ -288,29 +336,9 @@ module Benchmark
288
336
 
289
337
  @stdout.add_report rep, caller(1).first if @stdout
290
338
  @suite.add_report rep, caller(1).first if @suite
291
-
292
- if hold? && item != @list.last
293
- File.open @held_path, "a" do |f|
294
- require "json"
295
- f.write JSON.generate({
296
- :item => item.label,
297
- :measured_us => measured_us,
298
- :iter => iter,
299
- :samples => samples,
300
- :cycles => cycles
301
- })
302
- f.write "\n"
303
- end
304
-
305
- return true
306
- end
307
- end
308
-
309
- if hold? && @full_report.entries.size == @list.size
310
- File.delete @held_path if File.exist?(@held_path)
339
+
340
+ break if run_single?
311
341
  end
312
-
313
- false
314
342
  end
315
343
 
316
344
  def create_stats(samples)
@@ -11,10 +11,12 @@ module Benchmark
11
11
  def initialize(label, action)
12
12
  @label = label
13
13
 
14
+ # We define #call_times on the singleton class of each Entry instance.
15
+ # That way, there is no polymorphism for `@action.call` inside #call_times.
16
+
14
17
  if action.kind_of? String
15
- compile action
18
+ compile_string action
16
19
  @action = self
17
- @as_action = true
18
20
  else
19
21
  unless action.respond_to? :call
20
22
  raise ArgumentError, "invalid action, must respond to #call"
@@ -23,12 +25,10 @@ module Benchmark
23
25
  @action = action
24
26
 
25
27
  if action.respond_to? :arity and action.arity > 0
26
- @call_loop = true
28
+ compile_block_with_manual_loop
27
29
  else
28
- @call_loop = false
30
+ compile_block
29
31
  end
30
-
31
- @as_action = false
32
32
  end
33
33
  end
34
34
 
@@ -40,25 +40,43 @@ module Benchmark
40
40
  # @return [String, Proc] Code to be called, could be String / Proc.
41
41
  attr_reader :action
42
42
 
43
- # Call action by given times, return if +@call_loop+ is present.
43
+ # Call action by given times.
44
44
  # @param times [Integer] Times to call +@action+.
45
45
  # @return [Integer] Number of times the +@action+ has been called.
46
46
  def call_times(times)
47
- return @action.call(times) if @call_loop
47
+ raise '#call_times should be redefined per Benchmark::IPS::Job::Entry instance'
48
+ end
48
49
 
49
- act = @action
50
+ def compile_block
51
+ m = (class << self; self; end)
52
+ code = <<-CODE
53
+ def call_times(times)
54
+ act = @action
50
55
 
51
- i = 0
52
- while i < times
53
- act.call
54
- i += 1
55
- end
56
+ i = 0
57
+ while i < times
58
+ act.call
59
+ i += 1
60
+ end
61
+ end
62
+ CODE
63
+ m.class_eval code
64
+ end
65
+
66
+ def compile_block_with_manual_loop
67
+ m = (class << self; self; end)
68
+ code = <<-CODE
69
+ def call_times(times)
70
+ @action.call(times)
71
+ end
72
+ CODE
73
+ m.class_eval code
56
74
  end
57
75
 
58
76
  # Compile code into +call_times+ method.
59
77
  # @param str [String] Code to be compiled.
60
78
  # @return [Symbol] :call_times.
61
- def compile(str)
79
+ def compile_string(str)
62
80
  m = (class << self; self; end)
63
81
  code = <<-CODE
64
82
  def call_times(__total);
@@ -2,10 +2,14 @@ module Benchmark
2
2
  module IPS
3
3
  class Job
4
4
  class StdoutReport
5
+ def initialize
6
+ @last_item = nil
7
+ end
8
+
5
9
  def start_warming
6
10
  $stdout.puts "Warming up --------------------------------------"
7
11
  end
8
-
12
+
9
13
  def start_running
10
14
  $stdout.puts "Calculating -------------------------------------"
11
15
  end
@@ -31,6 +35,7 @@ module Benchmark
31
35
  end
32
36
 
33
37
  def footer
38
+ return unless @last_item
34
39
  footer = @last_item.stats.footer
35
40
  $stdout.puts footer.rjust(40) if footer
36
41
  end
@@ -40,6 +40,22 @@ module Benchmark
40
40
  # @return [Object] statisical summary.
41
41
  attr_reader :stats
42
42
 
43
+ # LEGACY: Iterations per second.
44
+ # @return [Float] number of iterations per second.
45
+ def ips
46
+ @stats.central_tendency
47
+ end
48
+
49
+ # LEGACY: Standard deviation of iteration per second.
50
+ # @return [Float] standard deviation of iteration per second.
51
+ def ips_sd
52
+ @stats.error
53
+ end
54
+
55
+ def samples
56
+ @stats.samples
57
+ end
58
+
43
59
  # Number of Cycles.
44
60
  # @return [Integer] number of cycles.
45
61
  attr_reader :measurement_cycle
@@ -60,7 +76,7 @@ module Benchmark
60
76
  # Return entry's standard deviation of iteration per second in percentage.
61
77
  # @return [Float] +@ips_sd+ in percentage.
62
78
  def error_percentage
63
- 100.0 * (@stats.error.to_f / @stats.central_tendency)
79
+ @stats.error_percentage
64
80
  end
65
81
 
66
82
  alias_method :runtime, :seconds
@@ -72,7 +88,7 @@ module Benchmark
72
88
  def body
73
89
  case Benchmark::IPS.options[:format]
74
90
  when :human
75
- left = "%s (±%4.1f%%) i/s" % [Helpers.scale(@stats.central_tendency), error_percentage]
91
+ left = "%s (±%4.1f%%) i/s" % [Helpers.scale(@stats.central_tendency), @stats.error_percentage]
76
92
  iters = Helpers.scale(@iterations)
77
93
 
78
94
  if @show_total_time
@@ -81,7 +97,7 @@ module Benchmark
81
97
  left.ljust(20) + (" - %s" % iters)
82
98
  end
83
99
  else
84
- left = "%10.1f (±%.1f%%) i/s" % [@stats.central_tendency, error_percentage]
100
+ left = "%10.1f (±%.1f%%) i/s" % [@stats.central_tendency, @stats.error_percentage]
85
101
 
86
102
  if @show_total_time
87
103
  left.ljust(20) + (" - %10d in %10.6fs" % [@iterations, runtime])
@@ -3,27 +3,30 @@ module Benchmark
3
3
  module Stats
4
4
 
5
5
  class Bootstrap
6
-
7
- attr_reader :data
6
+ include StatsMetric
7
+ attr_reader :data, :error, :samples
8
8
 
9
9
  def initialize(samples, confidence)
10
10
  dependencies
11
11
  @iterations = 10_000
12
12
  @confidence = (confidence / 100.0).to_s
13
+ @samples = samples
13
14
  @data = Kalibera::Data.new({[0] => samples}, [1, samples.size])
14
15
  interval = @data.bootstrap_confidence_interval(@iterations, @confidence)
15
16
  @median = interval.median
16
17
  @error = interval.error
17
18
  end
18
19
 
20
+ # Average stat value
21
+ # @return [Float] central_tendency
19
22
  def central_tendency
20
23
  @median
21
24
  end
22
25
 
23
- def error
24
- @error
25
- end
26
-
26
+ # Determines how much slower this stat is than the baseline stat
27
+ # if this average is lower than the faster baseline, higher average is better (e.g. ips) (calculate accordingly)
28
+ # @param baseline [SD|Bootstrap] faster baseline
29
+ # @returns [Array<Float, nil>] the slowdown and the error (not calculated for standard deviation)
27
30
  def slowdown(baseline)
28
31
  low, slowdown, high = baseline.data.bootstrap_quotient(@data, @iterations, @confidence)
29
32
  error = Timing.mean([slowdown - low, high - slowdown])
@@ -1,33 +1,41 @@
1
1
  module Benchmark
2
2
  module IPS
3
3
  module Stats
4
-
4
+
5
5
  class SD
6
-
6
+ include StatsMetric
7
+ attr_reader :error, :samples
8
+
7
9
  def initialize(samples)
10
+ @samples = samples
8
11
  @mean = Timing.mean(samples)
9
12
  @error = Timing.stddev(samples, @mean).round
10
13
  end
11
-
14
+
15
+ # Average stat value
16
+ # @return [Float] central_tendency
12
17
  def central_tendency
13
18
  @mean
14
19
  end
15
-
16
- def error
17
- @error
18
- end
19
20
 
21
+ # Determines how much slower this stat is than the baseline stat
22
+ # if this average is lower than the faster baseline, higher average is better (e.g. ips) (calculate accordingly)
23
+ # @param baseline [SD|Bootstrap] faster baseline
24
+ # @returns [Array<Float, nil>] the slowdown and the error (not calculated for standard deviation)
20
25
  def slowdown(baseline)
21
- slowdown = baseline.central_tendency.to_f / central_tendency
22
- [slowdown, nil]
26
+ if baseline.central_tendency > central_tendency
27
+ [baseline.central_tendency.to_f / central_tendency, 0]
28
+ else
29
+ [central_tendency.to_f / baseline.central_tendency, 0]
30
+ end
23
31
  end
24
32
 
25
33
  def footer
26
34
  nil
27
35
  end
28
-
36
+
29
37
  end
30
-
38
+
31
39
  end
32
40
  end
33
41
  end
@@ -0,0 +1,21 @@
1
+ module Benchmark
2
+ module IPS
3
+ module Stats
4
+ module StatsMetric
5
+ # Return entry's standard deviation of iteration per second in percentage.
6
+ # @return [Float] +@ips_sd+ in percentage.
7
+ def error_percentage
8
+ 100.0 * (error.to_f / central_tendency)
9
+ end
10
+
11
+ def overlaps?(baseline)
12
+ baseline_low = baseline.central_tendency - baseline.error
13
+ baseline_high = baseline.central_tendency + baseline.error
14
+ my_high = central_tendency + error
15
+ my_low = central_tendency - error
16
+ my_high > baseline_low && my_low < baseline_high
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,6 +1,7 @@
1
1
  require "minitest/autorun"
2
2
  require "benchmark/ips"
3
3
  require "stringio"
4
+ require "tmpdir"
4
5
 
5
6
  class TestBenchmarkIPS < Minitest::Test
6
7
  def setup
@@ -20,6 +21,19 @@ class TestBenchmarkIPS < Minitest::Test
20
21
  assert $stdout.string.size > 0
21
22
  end
22
23
 
24
+ def test_warmup0
25
+ $stdout = @old_stdout
26
+
27
+ out, err = capture_io do
28
+ Benchmark.ips(:time => 1, :warmup => 0, :quiet => false) do |x|
29
+ x.report("sleep 0.25") { sleep(0.25) }
30
+ end
31
+ end
32
+
33
+ refute_match(/Warming up -+/, out)
34
+ assert_empty err
35
+ end
36
+
23
37
  def test_output
24
38
  Benchmark.ips(1) do |x|
25
39
  x.report("operation") { 100 * 100 }
@@ -55,11 +69,11 @@ class TestBenchmarkIPS < Minitest::Test
55
69
 
56
70
  assert_equal "sleep 0.25", rep1.label
57
71
  assert_equal 4, rep1.iterations
58
- assert_in_delta 4.0, rep1.stats.central_tendency, 0.2
72
+ assert_in_delta 4.0, rep1.ips, 0.2
59
73
 
60
74
  assert_equal "sleep 0.05", rep2.label
61
75
  assert_in_delta 20.0, rep2.iterations.to_f, 1.0
62
- assert_in_delta 20.0, rep2.stats.central_tendency, 2.0
76
+ assert_in_delta 20.0, rep2.ips, 2.0
63
77
  end
64
78
 
65
79
  def test_ips_alternate_config
@@ -73,7 +87,7 @@ class TestBenchmarkIPS < Minitest::Test
73
87
 
74
88
  assert_equal "sleep 0.25", rep.label
75
89
  assert_equal 4, rep.iterations
76
- assert_in_delta 4.0, rep.stats.central_tendency, 0.4
90
+ assert_in_delta 4.0, rep.ips, 0.4
77
91
  end
78
92
 
79
93
  def test_ips_old_config
@@ -85,7 +99,7 @@ class TestBenchmarkIPS < Minitest::Test
85
99
 
86
100
  assert_equal "sleep 0.25", rep.label
87
101
  assert_equal 4, rep.iterations
88
- assert_in_delta 4.0, rep.stats.central_tendency, 0.2
102
+ assert_in_delta 4.0, rep.ips, 0.2
89
103
  end
90
104
 
91
105
  def test_ips_config_suite
@@ -112,7 +126,7 @@ class TestBenchmarkIPS < Minitest::Test
112
126
 
113
127
  assert_equal "sleep 0.25", rep.label
114
128
  assert_equal 4*5, rep.iterations
115
- assert_in_delta 4.0, rep.stats.central_tendency, 0.2
129
+ assert_in_delta 4.0, rep.ips, 0.2
116
130
  end
117
131
 
118
132
  def test_ips_report_using_symbol
@@ -124,7 +138,7 @@ class TestBenchmarkIPS < Minitest::Test
124
138
 
125
139
  assert_equal :sleep_a_quarter_second, rep.label
126
140
  assert_equal 4*5, rep.iterations
127
- assert_in_delta 4.0, rep.stats.central_tendency, 0.2
141
+ assert_in_delta 4.0, rep.ips, 0.2
128
142
  end
129
143
 
130
144
  def test_ips_default_data
@@ -140,6 +154,17 @@ class TestBenchmarkIPS < Minitest::Test
140
154
  assert all_data[0][:stddev]
141
155
  end
142
156
 
157
+ def test_ips_empty
158
+ report = Benchmark.ips do |_x|
159
+
160
+ end
161
+
162
+ all_data = report.data
163
+
164
+ assert all_data
165
+ assert_equal [], all_data
166
+ end
167
+
143
168
  def test_json_output
144
169
  json_file = Tempfile.new("data.json")
145
170
 
@@ -158,4 +183,17 @@ class TestBenchmarkIPS < Minitest::Test
158
183
  assert data[0]["ips"]
159
184
  assert data[0]["stddev"]
160
185
  end
186
+
187
+ def test_hold!
188
+ temp_file_name = Dir::Tmpname.create(["benchmark-ips", ".tmp"]) { }
189
+
190
+ Benchmark.ips(:time => 0.001, :warmup => 0.001) do |x|
191
+ x.report("operation") { 100 * 100 }
192
+ x.report("operation2") { 100 * 100 }
193
+ x.hold! temp_file_name
194
+ end
195
+
196
+ assert File.exist?(temp_file_name)
197
+ File.unlink(temp_file_name)
198
+ end
161
199
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: benchmark-ips
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.1
4
+ version: 2.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-09 00:00:00.000000000 Z
11
+ date: 2020-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -16,42 +16,48 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.9'
19
+ version: '5.14'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5.9'
26
+ version: '5.14'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rdoc
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '4.0'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '7'
34
37
  type: :development
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
39
42
  - !ruby/object:Gem::Version
40
43
  version: '4.0'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '7'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: hoe
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
51
  - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: '3.15'
53
+ version: '3.22'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
58
  - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: '3.15'
60
+ version: '3.22'
55
61
  description: An iterations per second enhancement to Benchmark.
56
62
  email:
57
63
  - evan@phx.io
@@ -63,7 +69,6 @@ extra_rdoc_files:
63
69
  - README.md
64
70
  files:
65
71
  - ".autotest"
66
- - Gemfile.lock
67
72
  - History.txt
68
73
  - Manifest.txt
69
74
  - README.md
@@ -77,13 +82,15 @@ files:
77
82
  - lib/benchmark/ips/share.rb
78
83
  - lib/benchmark/ips/stats/bootstrap.rb
79
84
  - lib/benchmark/ips/stats/sd.rb
85
+ - lib/benchmark/ips/stats/stats_metric.rb
80
86
  - lib/benchmark/timing.rb
81
87
  - test/test_benchmark_ips.rb
82
88
  homepage: https://github.com/evanphx/benchmark-ips
83
89
  licenses:
84
90
  - MIT
85
- metadata: {}
86
- post_install_message:
91
+ metadata:
92
+ homepage_uri: https://github.com/evanphx/benchmark-ips
93
+ post_install_message:
87
94
  rdoc_options:
88
95
  - "--main"
89
96
  - README.md
@@ -100,9 +107,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
107
  - !ruby/object:Gem::Version
101
108
  version: '0'
102
109
  requirements: []
103
- rubyforge_project:
104
- rubygems_version: 2.5.1
105
- signing_key:
110
+ rubygems_version: 3.1.4
111
+ signing_key:
106
112
  specification_version: 4
107
113
  summary: An iterations per second enhancement to Benchmark.
108
114
  test_files: []
@@ -1,18 +0,0 @@
1
- GEM
2
- remote: https://rubygems.org/
3
- specs:
4
- hoe (3.15.0)
5
- rake (>= 0.8, < 12.0)
6
- minitest (5.8.4)
7
- rake (10.5.0)
8
-
9
- PLATFORMS
10
- ruby
11
-
12
- DEPENDENCIES
13
- hoe (~> 3.14)
14
- minitest
15
- rake (~> 10.5)
16
-
17
- BUNDLED WITH
18
- 1.11.2