benchmark-ips 2.3.0 → 2.4.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: 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