benchmark-ips 2.3.0 → 2.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac378dafa55fa25f223374c352d9fce265a73a1d
4
- data.tar.gz: edcbad0a3ed40b69793e18b52b40b4d2d73b72fd
3
+ metadata.gz: 8ae132eff282ec75647b3d3d1301cf42372dcdaf
4
+ data.tar.gz: cc8d76ab64858a0682f7fb767fa8e958973cdb3d
5
5
  SHA512:
6
- metadata.gz: 427e884910dc92a0e50ceb825ff36770b70c0dcee9088f56d3e090867986f6c8d12d176c7a671d7b964f15c12c2ae6edd4c87b532c6c68bdd351c0508c03fd90
7
- data.tar.gz: 3db5f1f4445eb0b2c4985633a197abf58be95f9a2bb01282bda329c2880916e1c25c5cd0ec94b2a17adbdc667e3e8829dd46fa428ef13dc41cf3327136564d89
6
+ metadata.gz: 8a79e55f2f50dfda13b04fd852bfa122a41918e2e74a105cf42dc01f7d65042a089c392f5fe6964c7784e57c7d7ef73fa6fb43546ec5e7f881e9b945a9c9cf26
7
+ data.tar.gz: 31bcb112be6fa82299708cc4dfccbe9ac421774ff7ff53da85d6bac1d61cacdc9ef28ec24e07efd96ffb59fbff0e3aee0eb8f180ce4aa90468e223e87de7537e
@@ -1,3 +1,38 @@
1
+ === 2.4.0 / 2016-02-12
2
+
3
+ * 1 minor features
4
+ * Add support for hold! and independent invocations.
5
+
6
+ * 6 bug fixes
7
+ * Separate messages for warming up and calculating.
8
+ * Tighten timing loop.
9
+ * Pass simple types into Job#create_report
10
+ * More concise sorting
11
+ * Fix runtime comparison
12
+ * Use runtime if ips is not available
13
+
14
+ * 5 doc fixes
15
+ * Fix typo unsed --> used
16
+ * Better document Report::Entry
17
+ * Fix some typos in docs
18
+ * Don't calculate mean 2 times
19
+ * Add more tolerance to tests
20
+
21
+ * 13 merged PRs
22
+ * Merge pull request #44 from kbrock/job_extract
23
+ * Merge pull request #45 from kbrock/runtime_only
24
+ * Merge pull request #47 from kbrock/use_avg
25
+ * Merge pull request #46 from kbrock/report_stdout
26
+ * Merge pull request #48 from bquorning/fix-label-for-runtime-comparison
27
+ * Merge pull request #50 from tjschuck/fix_typo
28
+ * Merge pull request #51 from bquorning/all-reports-respond-to-ips
29
+ * Merge pull request #52 from kbrock/document_reports
30
+ * Merge pull request #53 from kbrock/interface_create_report
31
+ * Merge pull request #54 from PragTob/patch-2
32
+ * Merge pull request #55 from chrisseaton/messages
33
+ * Merge pull request #56 from chrisseaton/independence
34
+ * Merge pull request #57 from chrisseaton/tighten-loop
35
+
1
36
  === 2.3.0 / 2015-07-20
2
37
 
3
38
  * 2 minor features:
data/README.md CHANGED
@@ -136,6 +136,27 @@ Benchmark.ips do |x|
136
136
  end
137
137
  ```
138
138
 
139
+ ### Independent benchmarking
140
+
141
+ If you are comparing multiple implementations of a piece of code you may want
142
+ to benchmark them in separate invocations of Ruby so that the measurements
143
+ are independent of each other. You can do this with the `hold!` command.
144
+
145
+ ```ruby
146
+ Benchmark.ips do |x|
147
+
148
+ ...
149
+
150
+ # Hold results between multiple invocations of Ruby
151
+ x.hold! 'filename'
152
+
153
+ end
154
+ ```
155
+
156
+ This will run only one benchmarks each time you run the command, storing
157
+ results in the specified file. The file is deleted when all results have been
158
+ gathered and the report is shown.
159
+
139
160
  ## REQUIREMENTS:
140
161
 
141
162
  * None!
@@ -28,40 +28,23 @@ module Benchmark
28
28
 
29
29
  # Compare between reports, prints out facts of each report:
30
30
  # runtime, comparative speed difference.
31
- # @param reports [Array<Report>] Reports to compare.
32
- def compare(*reports)
33
- return if reports.size < 2
31
+ # @param entries [Array<Report::Entry>] Reports to compare.
32
+ def compare(*entries)
33
+ return if entries.size < 2
34
34
 
35
- iter = false
36
- sorted = reports.sort do |a,b|
37
- if a.respond_to? :ips
38
- iter = true
39
- b.ips <=> a.ips
40
- else
41
- a.runtime <=> b.runtime
42
- end
43
- end
35
+ sorted = entries.sort_by(&:ips).reverse
44
36
 
45
37
  best = sorted.shift
46
38
 
47
39
  $stdout.puts "\nComparison:"
48
40
 
49
- if iter
50
- $stdout.printf "%20s: %10.1f i/s\n", best.label, best.ips
51
- else
52
- $stdout.puts "#{best.rjust(20)}: #{best.runtime}s"
53
- end
41
+ $stdout.printf "%20s: %10.1f i/s\n", best.label, best.ips
54
42
 
55
43
  sorted.each do |report|
56
44
  name = report.label.to_s
57
45
 
58
- if iter
59
- x = (best.ips.to_f / report.ips.to_f)
60
- $stdout.printf "%20s: %10.1f i/s - %.2fx slower\n", name, report.ips, x
61
- else
62
- x = "%.2f" % (report.ips.to_f / best.ips.to_f)
63
- $stdout.puts "#{name.rjust(20)}: #{report.runtime}s - #{x}x slower"
64
- end
46
+ x = (best.ips.to_f / report.ips.to_f)
47
+ $stdout.printf "%20s: %10.1f i/s - %.2fx slower\n", name, report.ips, x
65
48
  end
66
49
 
67
50
  $stdout.puts
@@ -2,6 +2,8 @@
2
2
  require 'benchmark/timing'
3
3
  require 'benchmark/compare'
4
4
  require 'benchmark/ips/report'
5
+ require 'benchmark/ips/job/entry'
6
+ require 'benchmark/ips/job/stdout_report'
5
7
  require 'benchmark/ips/job'
6
8
 
7
9
  # Performance benchmarking library
@@ -11,10 +13,10 @@ module Benchmark
11
13
  module IPS
12
14
 
13
15
  # Benchmark-ips Gem version.
14
- VERSION = "2.3.0"
16
+ VERSION = "2.4.0"
15
17
 
16
18
  # CODENAME of current version.
17
- CODENAME = "Monsoon BBQ"
19
+ CODENAME = "Nonlinear Rollercoaster"
18
20
 
19
21
  # Measure code in block, each code's benchmarked result will display in
20
22
  # iteration per second with standard deviation in given time.
@@ -49,26 +51,17 @@ module Benchmark
49
51
  job.config job_opts
50
52
 
51
53
  yield job
52
-
53
- $stdout.puts "Calculating -------------------------------------" unless quiet
54
+
55
+ job.load_held_results if job.hold? && job.held_results?
54
56
 
55
57
  job.run_warmup
56
-
57
- $stdout.puts "-------------------------------------------------" unless quiet
58
-
59
58
  job.run
60
59
 
61
60
  $stdout.sync = sync
61
+ job.run_comparison
62
+ job.generate_json
62
63
 
63
- if job.compare?
64
- job.run_comparison
65
- end
66
-
67
- if job.json?
68
- job.generate_json
69
- end
70
-
71
- return job.full_report
64
+ job.full_report
72
65
  end
73
66
 
74
67
  # Set options for running the benchmarks.
@@ -10,91 +10,6 @@ module Benchmark
10
10
  # before reporting a weird runtime
11
11
  MAX_TIME_SKEW = 0.05
12
12
 
13
- # Entries in Benchmark Jobs.
14
- class Entry
15
- # Instantiate the Benchmark::IPS::Job::Entry.
16
- # @param label [#to_s] Label of Benchmarked code.
17
- # @param action [String, Proc] Code to be benchmarked.
18
- # @raise [ArgumentError] Raises when action is not String or not responding to +call+.
19
- def initialize(label, action)
20
- @label = label
21
-
22
- if action.kind_of? String
23
- compile action
24
- @action = self
25
- @as_action = true
26
- else
27
- unless action.respond_to? :call
28
- raise ArgumentError, "invalid action, must respond to #call"
29
- end
30
-
31
- @action = action
32
-
33
- if action.respond_to? :arity and action.arity > 0
34
- @call_loop = true
35
- else
36
- @call_loop = false
37
- end
38
-
39
- @as_action = false
40
- end
41
- end
42
-
43
- # The label of benchmarking action.
44
- # @return [#to_s] Label of action.
45
- attr_reader :label
46
-
47
- # The benchmarking action.
48
- # @return [String, Proc] Code to be called, could be String / Proc.
49
- attr_reader :action
50
-
51
- # Add padding to label's right if label's length < 20,
52
- # Otherwise add a new line and 20 whitespaces.
53
- # @return [String] Right justified label.
54
- def label_rjust
55
- label = @label.to_s
56
- if label.size > 20
57
- "#{label}\n#{' ' * 20}"
58
- else
59
- label.rjust(20)
60
- end
61
- end
62
-
63
- # Call action by given times, return if +@call_loop+ is present.
64
- # @param times [Integer] Times to call +@action+.
65
- # @return [Integer] Number of times the +@action+ has been called.
66
- def call_times(times)
67
- return @action.call(times) if @call_loop
68
-
69
- act = @action
70
-
71
- i = 0
72
- while i < times
73
- act.call
74
- i += 1
75
- end
76
- end
77
-
78
- # Compile code into +call_times+ method.
79
- # @param str [String] Code to be compiled.
80
- # @return [Symbol] :call_times.
81
- def compile(str)
82
- m = (class << self; self; end)
83
- code = <<-CODE
84
- def call_times(__total);
85
- __i = 0
86
- while __i < __total
87
- #{str};
88
- __i += 1
89
- end
90
- end
91
- CODE
92
- m.class_eval code
93
- end
94
- end # End of Entry
95
-
96
- # class Job
97
-
98
13
  # Two-element arrays, consisting of label and block pairs.
99
14
  # @return [Array<Entry>] list of entries
100
15
  attr_reader :list
@@ -103,6 +18,10 @@ module Benchmark
103
18
  # @return [Boolean] true if needs to run compare.
104
19
  attr_reader :compare
105
20
 
21
+ # Determining whether to hold results between Ruby invocations
22
+ # @return [Boolean]
23
+ attr_accessor :hold
24
+
106
25
  # Report object containing information about the run.
107
26
  # @return [Report] the report object.
108
27
  attr_reader :full_report
@@ -124,10 +43,12 @@ module Benchmark
124
43
  # @option opts [Boolean] (false) :quiet Suppress the printing of information.
125
44
  def initialize opts={}
126
45
  @suite = opts[:suite] || nil
127
- @quiet = opts[:quiet] || false
46
+ @stdout = opts[:quiet] ? nil : StdoutReport.new
128
47
  @list = []
129
48
  @compare = false
130
49
  @json_path = false
50
+ @held_path = nil
51
+ @held_results = nil
131
52
 
132
53
  @timing = {}
133
54
  @full_report = Report.new
@@ -157,6 +78,16 @@ module Benchmark
157
78
  @compare = true
158
79
  end
159
80
 
81
+ # Return true if results are held while multiple Ruby invocations
82
+ # @return [Boolean] Need to hold results between multiple Ruby invocations?
83
+ def hold?
84
+ !!@held_path
85
+ end
86
+
87
+ # Set @hold to true.
88
+ def hold!(held_path)
89
+ @held_path = held_path
90
+ end
160
91
 
161
92
  # Return true if job needs to generate json.
162
93
  # @return [Boolean] Need to generate json?
@@ -171,8 +102,8 @@ module Benchmark
171
102
 
172
103
  # Registers the given label and block pair in the job list.
173
104
  # @param label [String] Label of benchmarked code.
174
- # @param str [String] Code to be benchamrked.
175
- # @param blk [Proc] Code to be benchamrked.
105
+ # @param str [String] Code to be benchmarked.
106
+ # @param blk [Proc] Code to be benchmarked.
176
107
  # @raise [ArgumentError] Raises if str and blk are both present.
177
108
  # @raise [ArgumentError] Raises if str and blk are both absent.
178
109
  def item(label="", str=nil, &blk) # :yield:
@@ -215,15 +146,27 @@ module Benchmark
215
146
  def iterations_per_sec cycles, time_us
216
147
  MICROSECONDS_PER_SECOND * (cycles.to_f / time_us.to_f)
217
148
  end
149
+
150
+ def held_results?
151
+ File.exist?(@held_path)
152
+ end
153
+
154
+ def load_held_results
155
+ require "json"
156
+ @held_results = Hash[File.open(@held_path).map { |line|
157
+ result = JSON.parse(line)
158
+ [result['item'], result]
159
+ }]
160
+ end
218
161
 
219
162
  # Run warmup.
220
163
  def run_warmup
164
+ @stdout.start_warming if @stdout
221
165
  @list.each do |item|
166
+ next if hold? && @held_results && @held_results.key?(item.label)
167
+
222
168
  @suite.warming item.label, @warmup if @suite
223
-
224
- unless @quiet
225
- $stdout.print item.label_rjust
226
- end
169
+ @stdout.warming item.label, @warmup if @stdout
227
170
 
228
171
  Timing.clean_env
229
172
 
@@ -243,37 +186,38 @@ module Benchmark
243
186
 
244
187
  @timing[item] = cycles_per_100ms warmup_time_us, warmup_iter
245
188
 
246
- case Benchmark::IPS.options[:format]
247
- when :human
248
- $stdout.printf "%s i/100ms\n", Helpers.scale(@timing[item]) unless @quiet
249
- else
250
- $stdout.printf "%10d i/100ms\n", @timing[item] unless @quiet
251
- end
252
-
189
+ @stdout.warmup_stats warmup_time_us, @timing[item] if @stdout
253
190
  @suite.warmup_stats warmup_time_us, @timing[item] if @suite
191
+
192
+ break if hold?
254
193
  end
255
194
  end
256
195
 
257
196
  # Run calculation.
258
197
  def run
198
+ @stdout.start_running if @stdout
259
199
  @list.each do |item|
260
- @suite.running item.label, @time if @suite
261
-
262
- unless @quiet
263
- $stdout.print item.label_rjust
200
+ if hold? && @held_results && @held_results.key?(item.label)
201
+ result = @held_results[item.label]
202
+ create_report(item.label, result['measured_us'], result['iter'],
203
+ result['avg_ips'], result['sd_ips'], result['cycles'])
204
+ next
264
205
  end
206
+
207
+ @suite.running item.label, @time if @suite
208
+ @stdout.running item.label, @time if @stdout
265
209
 
266
210
  Timing.clean_env
267
211
 
268
212
  iter = 0
269
213
 
270
- target = Time.now + @time
271
-
272
214
  measurements_us = []
273
215
 
274
216
  # Running this number of cycles should take around 100ms.
275
217
  cycles = @timing[item]
276
218
 
219
+ target = Time.now + @time
220
+
277
221
  while Time.now < target
278
222
  before = Time.now
279
223
  item.call_times cycles
@@ -298,39 +242,62 @@ module Benchmark
298
242
  }
299
243
 
300
244
  avg_ips = Timing.mean(all_ips)
301
- sd_ips = Timing.stddev(all_ips).round
245
+ sd_ips = Timing.stddev(all_ips, avg_ips).round
302
246
 
303
- rep = create_report(item, measured_us, iter, avg_ips, sd_ips, cycles)
247
+ rep = create_report(item.label, measured_us, iter, avg_ips, sd_ips, cycles)
304
248
 
305
249
  if (final_time - target).abs >= (@time.to_f * MAX_TIME_SKEW)
306
250
  rep.show_total_time!
307
251
  end
308
252
 
309
- $stdout.puts " #{rep.body}" unless @quiet
310
-
253
+ @stdout.add_report rep, caller(1).first if @stdout
311
254
  @suite.add_report rep, caller(1).first if @suite
255
+
256
+ if hold? && item != @list.last
257
+ File.open @held_path, "a" do |f|
258
+ require "json"
259
+ f.write JSON.generate({
260
+ :item => item.label,
261
+ :measured_us => measured_us,
262
+ :iter => iter,
263
+ :avg_ips => avg_ips,
264
+ :sd_ips => sd_ips,
265
+ :cycles => cycles
266
+ })
267
+ f.write "\n"
268
+ end
269
+
270
+ puts
271
+ puts 'Pausing here -- run Ruby again to measure the next benchmark...'
272
+ break
273
+ end
274
+ end
275
+
276
+ if hold? && @full_report.entries.size == @list.size
277
+ File.delete @held_path if File.exist?(@held_path)
312
278
  end
313
279
  end
314
280
 
315
281
  # Run comparison of entries in +@full_report+.
316
282
  def run_comparison
317
- @full_report.run_comparison
283
+ @full_report.run_comparison if compare?
318
284
  end
319
285
 
320
286
  # Generate json from +@full_report+.
321
287
  def generate_json
322
- @full_report.generate_json @json_path
288
+ @full_report.generate_json @json_path if json?
323
289
  end
324
290
 
325
291
  # Create report by add entry to +@full_report+.
326
- # @param item [Benchmark::IPS::Job::Entry] Report item.
292
+ # @param label [String] Report item label.
327
293
  # @param measured_us [Integer] Measured time in microsecond.
328
294
  # @param iter [Integer] Iterations.
329
295
  # @param avg_ips [Float] Average iterations per second.
330
296
  # @param sd_ips [Float] Standard deviation iterations per second.
331
297
  # @param cycles [Integer] Number of Cycles.
332
- def create_report(item, measured_us, iter, avg_ips, sd_ips, cycles)
333
- @full_report.add_entry item.label, measured_us, iter, avg_ips, sd_ips, cycles
298
+ # @return [Report::Entry] Entry with data.
299
+ def create_report(label, measured_us, iter, avg_ips, sd_ips, cycles)
300
+ @full_report.add_entry label, measured_us, iter, avg_ips, sd_ips, cycles
334
301
  end
335
302
  end
336
303
  end
@@ -3,7 +3,7 @@
3
3
  module Benchmark
4
4
  module IPS
5
5
 
6
- # Report contains benchamrking entries.
6
+ # Report contains benchmarking entries.
7
7
  # Perform operations like add new entry, run comparison between entries.
8
8
  class Report
9
9
 
@@ -117,8 +117,8 @@ module Benchmark
117
117
 
118
118
  # class Report
119
119
 
120
- # Entry to represent each benchamarked code in Report.
121
- # @return [Array<Entry>] Entries in Report.
120
+ # Entry to represent each benchmarked code in Report.
121
+ # @return [Array<Report::Entry>] Entries in Report.
122
122
  attr_reader :entries
123
123
 
124
124
  # Instantiate the Report.
@@ -134,10 +134,11 @@ module Benchmark
134
134
  # @param ips [Float] Average Iterations per second.
135
135
  # @param ips_sd [Float] Standard deviation of iterations per second.
136
136
  # @param measurement_cycle [Integer] Number of cycles.
137
- # @return [Entry] Last added entry.
137
+ # @return [Report::Entry] Last added entry.
138
138
  def add_entry label, microseconds, iters, ips, ips_sd, measurement_cycle
139
- @entries << Entry.new(label, microseconds, iters, ips, ips_sd, measurement_cycle)
140
- @entries.last
139
+ entry = Entry.new(label, microseconds, iters, ips, ips_sd, measurement_cycle)
140
+ @entries << entry
141
+ entry
141
142
  end
142
143
 
143
144
  # Entries data in array for generate json.
@@ -145,7 +146,7 @@ module Benchmark
145
146
  # name: Entry#label
146
147
  # ips: Entry#ips
147
148
  # stddev: Entry#ips_sd
148
- # @return [Array] Array of entries
149
+ # @return [Array<Hash<Symbol,String|Float>] Array of hashes with :label, :ips, :stddev
149
150
  def data
150
151
  @data ||= @entries.collect do |entry|
151
152
  {
@@ -43,7 +43,7 @@ module Benchmark
43
43
  resamples
44
44
  end
45
45
 
46
- # Recycle unsed objects by starting Garbage Collector.
46
+ # Recycle used objects by starting Garbage Collector.
47
47
  def self.clean_env
48
48
  # rbx
49
49
  if GC.respond_to? :run
@@ -59,7 +59,7 @@ class TestBenchmarkIPS < Minitest::Test
59
59
 
60
60
  assert_equal "sleep 0.05", rep2.label
61
61
  assert_in_delta 20.0, rep2.iterations.to_f, 1.0
62
- assert_in_delta 20.0, rep2.ips, 1.0
62
+ assert_in_delta 20.0, rep2.ips, 2.0
63
63
  end
64
64
 
65
65
  def test_ips_alternate_config
@@ -73,7 +73,7 @@ class TestBenchmarkIPS < Minitest::Test
73
73
 
74
74
  assert_equal "sleep 0.25", rep.label
75
75
  assert_equal 4, rep.iterations
76
- assert_in_delta 4.0, rep.ips, 0.2
76
+ assert_in_delta 4.0, rep.ips, 0.4
77
77
  end
78
78
 
79
79
  def test_ips_old_config
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.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-20 00:00:00.000000000 Z
11
+ date: 2016-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.6'
19
+ version: '5.8'
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.6'
26
+ version: '5.8'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rdoc
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.13'
47
+ version: '3.14'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.13'
54
+ version: '3.14'
55
55
  description: A iterations per second enhancement to Benchmark.
56
56
  email:
57
57
  - evan@phx.io
@@ -63,7 +63,6 @@ extra_rdoc_files:
63
63
  - README.md
64
64
  files:
65
65
  - ".autotest"
66
- - ".gemtest"
67
66
  - History.txt
68
67
  - Manifest.txt
69
68
  - README.md
@@ -96,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
95
  version: '0'
97
96
  requirements: []
98
97
  rubyforge_project:
99
- rubygems_version: 2.2.2
98
+ rubygems_version: 2.5.1
100
99
  signing_key:
101
100
  specification_version: 4
102
101
  summary: A iterations per second enhancement to Benchmark.
data/.gemtest DELETED
File without changes