motion-benchmark-ips 1.0 → 1.1

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: 14cfdbe4568670fda3739ecd7a9837aa0b21a7c7
4
- data.tar.gz: 550e6b910b78289a5b525450ed4ad0238e1b2e82
3
+ metadata.gz: f60f433abf07be7027a49a2e7be67f9178e698ec
4
+ data.tar.gz: ae5e787909b978f143876b944d7631d875ed553d
5
5
  SHA512:
6
- metadata.gz: 9acf976811223997ed734be9570a30e188b5282374fab9215d0028e12034106143464de40d3b1e144cc3bfb55b1f95f912a6d103d1f1181a776c77c118feca01
7
- data.tar.gz: d00d71ba9fd3abdfaade152324a6b390099d5e5881be137ce8b856818d66f89edf64bcd10d90f19db13c6da9d14837aec4bda464c81a7e73d3f46857fd5128f6
6
+ metadata.gz: 1d90bd772df7e71967ea4d98a7e29d6fb229164da138a7a9104f68560fc62e7d4f0e5eba5497e401a447d93f9bbfa7fa0d6b3a71bf3e15128c012c9799834e9f
7
+ data.tar.gz: 6d4d4d8a635935c70cedbe1aeb48324f190f8449d21715074a9e212d2e32a53e4acad127b579f1e05a2a7f65407687f8b21c29a9dcc00bff77a35bf832d4efec
data/README.md CHANGED
@@ -18,10 +18,36 @@ Or install it yourself as:
18
18
  $ gem install motion-benchmark-ips
19
19
 
20
20
 
21
+ ## Usage
22
+ ```
23
+ Benchmark.ips do |x|
24
+ # Typical mode, runs the block as many times as it can
25
+ x.report("addition") { 1 + 2 }
26
+
27
+ # To reduce overhead, the number of iterations is passed in
28
+ # and the block must run the code the specific number of times.
29
+ # Used for when the workload is very small and any overhead
30
+ # introduces incorrectable errors.
31
+ x.report("addition2") do |times|
32
+ i = 0
33
+ while i < times
34
+ 1 + 2
35
+ i += 1
36
+ end
37
+ end
38
+ end
39
+ ```
40
+
41
+ ## Premium Support
42
+
43
+ [motion-benchmark-ips](https://github.com/infinitered/motion-benchmark-ips), as an open source project, is free to use and always will be. [Infinite Red](https://infinite.red/) offers premium motion-benchmark-ips support and general mobile app design/development services. Email us at [hello@infinite.red](mailto:hello@infinite.red) to get in touch with us for more details.
44
+
45
+
21
46
  ## LICENSE:
22
47
 
23
48
  (The MIT License)
24
49
 
50
+ ```
25
51
  Copyright (c) 2014 Watson
26
52
 
27
53
  Permission is hereby granted, free of charge, to any person obtaining
@@ -42,3 +68,4 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
42
68
  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
43
69
  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
44
70
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
71
+ ```
@@ -1,41 +1,76 @@
1
+ # encoding: utf-8
2
+
1
3
  module Benchmark
2
- def compare(*reports)
3
- return if reports.size < 2
4
-
5
- iter = false
6
- sorted = reports.sort do |a,b|
7
- if a.respond_to? :ips
8
- iter = true
9
- b.ips <=> a.ips
10
- else
11
- a.runtime <=> b.runtime
12
- end
13
- end
4
+ # Functionality of performaing comparison between reports.
5
+ #
6
+ # Usage:
7
+ #
8
+ # Add +x.compare!+ to perform comparison between reports.
9
+ #
10
+ # Example:
11
+ # > Benchmark.ips do |x|
12
+ # x.report('Reduce using tag') { [*1..10].reduce(:+) }
13
+ # x.report('Reduce using to_proc') { [*1..10].reduce(&:+) }
14
+ # x.compare!
15
+ # end
16
+ #
17
+ # Calculating -------------------------------------
18
+ # Reduce using tag 19216 i/100ms
19
+ # Reduce using to_proc 17437 i/100ms
20
+ # -------------------------------------------------
21
+ # Reduce using tag 278950.0 (±8.5%) i/s - 1402768 in 5.065112s
22
+ # Reduce using to_proc 247295.4 (±8.0%) i/s - 1238027 in 5.037299s
23
+ #
24
+ # Comparison:
25
+ # Reduce using tag: 278950.0 i/s
26
+ # Reduce using to_proc: 247295.4 i/s - 1.13x slower
27
+ #
28
+ # Besides regular Calculating report, this will also indicates which one is slower.
29
+ module Compare
14
30
 
15
- best = sorted.shift
31
+ # Compare between reports, prints out facts of each report:
32
+ # runtime, comparative speed difference.
33
+ # @param entries [Array<Report::Entry>] Reports to compare.
34
+ def compare(*entries)
35
+ return if entries.size < 2
16
36
 
17
- STDOUT.puts "\nComparison:"
37
+ sorted = entries.sort_by{ |e| e.stats.central_tendency }.reverse
18
38
 
19
- if iter
20
- STDOUT.printf "%20s: %10.1f i/s\n", best.label, best.ips
21
- else
22
- STDOUT.puts "#{best.rjust(20)}: #{best.runtime}s"
23
- end
39
+ best = sorted.shift
40
+
41
+ $stdout.puts "\nComparison:"
42
+
43
+ $stdout.printf "%20s: %10.1f i/s\n", best.label, best.stats.central_tendency
24
44
 
25
- sorted.each do |report|
26
- name = report.label
45
+ sorted.each do |report|
46
+ name = report.label.to_s
27
47
 
28
- if iter
29
- x = (best.ips.to_f / report.ips.to_f)
30
- STDOUT.printf "%20s: %10.1f i/s - %.2fx slower\n", name, report.ips, x
31
- else
32
- x = "%.2f" % (report.ips.to_f / best.ips.to_f)
33
- STDOUT.puts "#{name.rjust(20)}: #{report.runtime}s - #{x}x slower"
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
55
+ $stdout.print "same-ish: difference falls within error"
56
+ else
57
+ slowdown, error = report.stats.slowdown(best.stats)
58
+ $stdout.printf "%.2fx ", slowdown
59
+ if error
60
+ $stdout.printf " (± %.2f)", error
61
+ end
62
+ $stdout.print " slower"
63
+ end
64
+
65
+ $stdout.puts
34
66
  end
35
- end
36
67
 
37
- STDOUT.puts
68
+ footer = best.stats.footer
69
+ $stdout.puts footer.rjust(40) if footer
70
+
71
+ $stdout.puts
72
+ end
38
73
  end
39
74
 
40
- module_function :compare
75
+ extend Benchmark::Compare
41
76
  end
@@ -1,265 +1,96 @@
1
1
  # encoding: utf-8
2
- # require 'benchmark/timing'
3
- # require 'benchmark/compare'
4
-
2
+ # Performance benchmarking library
5
3
  module Benchmark
6
-
7
- class IPSReport
8
- VERSION = "1.1.0"
9
-
10
- def initialize(label, us, iters, ips, ips_sd, cycles)
11
- @label = label
12
- @microseconds = us
13
- @iterations = iters
14
- @ips = ips
15
- @ips_sd = ips_sd
16
- @measurement_cycle = cycles
17
- end
18
-
19
- attr_reader :label, :microseconds, :iterations, :ips, :ips_sd, :measurement_cycle
20
-
21
- def seconds
22
- @microseconds.to_f / 1_000_000.0
23
- end
24
-
25
- def stddev_percentage
26
- 100.0 * (@ips_sd.to_f / @ips.to_f)
27
- end
28
-
29
- alias_method :runtime, :seconds
30
-
31
- def body
32
- left = "%10.1f (±%.1f%%) i/s" % [ips, stddev_percentage]
33
- left.ljust(20) + (" - %10d in %10.6fs" % [@iterations, runtime])
34
- end
35
-
36
- def header
37
- @label.rjust(20)
38
- end
39
-
40
- def to_s
41
- "#{header} #{body}"
42
- end
43
-
44
- def display
45
- $stdout.puts to_s
46
- end
47
- end
48
-
49
- class IPSJob
50
- class Entry
51
- def initialize(label, action)
52
- @label = label
53
-
54
- if action.kind_of? String
55
- compile action
56
- @action = self
57
- @as_action = true
58
- else
59
- unless action.respond_to? :call
60
- raise ArgumentError, "invalid action, must respond to #call"
61
- end
62
-
63
- @action = action
64
-
65
- if action.respond_to? :arity and action.arity > 0
66
- @call_loop = true
67
- else
68
- @call_loop = false
69
- end
70
-
71
- @as_action = false
72
- end
4
+ # Benchmark in iterations per second, no more guessing!
5
+ # @see https://github.com/evanphx/benchmark-ips
6
+ module IPS
7
+
8
+ # Benchmark-ips Gem version.
9
+ VERSION = "2.7.2"
10
+
11
+ # CODENAME of current version.
12
+ CODENAME = "Cultivating Confidence"
13
+
14
+ # Measure code in block, each code's benchmarked result will display in
15
+ # iteration per second with standard deviation in given time.
16
+ # @param time [Integer] Specify how long should benchmark your code in seconds.
17
+ # @param warmup [Integer] Specify how long should Warmup time run in seconds.
18
+ # @return [Report]
19
+ def ips(*args)
20
+ if args[0].is_a?(Hash)
21
+ time, warmup, quiet = args[0].values_at(:time, :warmup, :quiet)
22
+ else
23
+ time, warmup, quiet = args
73
24
  end
74
25
 
75
- attr_reader :label, :action
26
+ suite = nil
76
27
 
77
- def as_action?
78
- @as_action
79
- end
28
+ sync, $stdout.sync = $stdout.sync, true
80
29
 
81
- def call_times(times)
82
- return @action.call(times) if @call_loop
83
-
84
- act = @action
85
-
86
- i = 0
87
- while i < times
88
- act.call
89
- i += 1
90
- end
30
+ if defined? Benchmark::Suite and Suite.current
31
+ suite = Benchmark::Suite.current
91
32
  end
92
33
 
93
- def compile(str)
94
- m = (class << self; self; end)
95
- code = <<-CODE
96
- def call_times(__total);
97
- __i = 0
98
- while __i < __total
99
- #{str};
100
- __i += 1
101
- end
102
- end
103
- CODE
104
- m.class_eval code
105
- end
106
- end
34
+ quiet ||= (suite && suite.quiet?)
107
35
 
108
- def initialize
109
- @list = []
110
- @compare = false
111
- end
112
-
113
- attr_reader :compare
114
-
115
- def compare!
116
- @compare = true
117
- end
118
-
119
- #
120
- # Registers the given label and block pair in the job list.
121
- #
122
- def item(label="", str=nil, &blk) # :yield:
123
- if blk and str
124
- raise ArgumentError, "specify a block and a str, but not both"
125
- end
36
+ job = Job.new({:suite => suite,
37
+ :quiet => quiet
38
+ })
126
39
 
127
- action = str || blk
128
- raise ArgumentError, "no block or string" unless action
40
+ job_opts = {}
41
+ job_opts[:time] = time unless time.nil?
42
+ job_opts[:warmup] = warmup unless warmup.nil?
129
43
 
130
- @list.push Entry.new(label, action)
131
- self
132
- end
44
+ job.config job_opts
133
45
 
134
- alias_method :report, :item
46
+ yield job
135
47
 
136
- # An array of 2-element arrays, consisting of label and block pairs.
137
- attr_reader :list
138
- end
48
+ job.load_held_results if job.hold? && job.held_results?
139
49
 
140
- def ips(time=5, warmup=2)
141
- suite = nil
50
+ job.run
142
51
 
143
- sync, $stdout.sync = $stdout.sync, true
144
-
145
- if defined? Benchmark::Suite and Suite.current
146
- suite = Benchmark::Suite.current
147
- end
52
+ $stdout.sync = sync
53
+ job.run_comparison
54
+ job.generate_json
148
55
 
149
- quiet = suite && !suite.quiet?
56
+ report = job.full_report
150
57
 
151
- job = IPSJob.new
152
- yield job
153
-
154
- reports = []
155
-
156
- timing = {}
157
-
158
- $stdout.puts "Calculating -------------------------------------" unless quiet
159
-
160
- job.list.each do |item|
161
- suite.warming item.label, warmup if suite
162
-
163
- Timing.clean_env
164
-
165
- unless quiet
166
- if item.label.size > 20
167
- $stdout.print "#{item.label}\n#{' ' * 20}"
168
- else
169
- $stdout.print item.label.rjust(20)
170
- end
58
+ if ENV['SHARE'] || ENV['SHARE_URL']
59
+ require 'benchmark/ips/share'
60
+ share = Share.new report, job
61
+ share.share
171
62
  end
172
63
 
173
- before = Time.now
174
- target = Time.now + warmup
175
-
176
- warmup_iter = 0
177
-
178
- while Time.now < target
179
- item.call_times(1)
180
- warmup_iter += 1
181
- end
182
-
183
- after = Time.now
184
-
185
- warmup_time = (after.to_f - before.to_f) * 1_000_000.0
186
-
187
- # calculate the time to run approx 100ms
188
-
189
- cycles_per_100ms = ((100_000 / warmup_time) * warmup_iter).to_i
190
- cycles_per_100ms = 1 if cycles_per_100ms <= 0
191
-
192
- timing[item] = cycles_per_100ms
193
-
194
- $stdout.printf "%10d i/100ms\n", cycles_per_100ms unless quiet
195
-
196
- suite.warmup_stats warmup_time, cycles_per_100ms if suite
64
+ report
197
65
  end
198
66
 
199
- $stdout.puts "-------------------------------------------------" unless quiet
67
+ # Set options for running the benchmarks.
68
+ # :format => [:human, :raw]
69
+ # :human format narrows precision and scales results for readability
70
+ # :raw format displays 6 places of precision and exact iteration counts
71
+ def self.options
72
+ @options ||= {:format => :human}
73
+ end
200
74
 
201
- job.list.each do |item|
202
- unless quiet
203
- if item.label.size > 20
204
- $stdout.print "#{item.label}\n#{' ' * 20}"
75
+ module Helpers
76
+ def scale(value)
77
+ scale = (Math.log10(value) / 3).to_i
78
+ suffix = case scale
79
+ when 1; 'k'
80
+ when 2; 'M'
81
+ when 3; 'B'
82
+ when 4; 'T'
83
+ when 5; 'Q'
205
84
  else
206
- $stdout.print item.label.rjust(20)
85
+ # < 1000 or > 10^15, no scale or suffix
86
+ scale = 0
87
+ ' '
207
88
  end
89
+ "%10.3f#{suffix}" % (value.to_f / (1000 ** scale))
208
90
  end
209
-
210
- Timing.clean_env
211
-
212
- suite.running item.label, time if suite
213
-
214
- iter = 0
215
-
216
- target = Time.now + time
217
-
218
- measurements = []
219
-
220
- cycles_per_100ms = timing[item]
221
-
222
- while Time.now < target
223
- before = Time.now
224
- item.call_times cycles_per_100ms
225
- after = Time.now
226
-
227
- # If for some reason the timing said this too no time (O_o)
228
- # then ignore the iteration entirely and start another.
229
- #
230
- m = ((after.to_f - before.to_f) * 1_000_000.0)
231
- next if m <= 0.0
232
-
233
- iter += cycles_per_100ms
234
-
235
- measurements << m
236
- end
237
-
238
- measured_us = measurements.inject(0) { |a,i| a + i }
239
-
240
- all_ips = measurements.map { |i| cycles_per_100ms.to_f / (i.to_f / 1_000_000) }
241
-
242
- avg_ips = Timing.mean(all_ips)
243
- sd_ips = Timing.stddev(all_ips).round
244
-
245
- rep = IPSReport.new(item.label, measured_us, iter, avg_ips, sd_ips, cycles_per_100ms)
246
-
247
- $stdout.puts " #{rep.body}" unless quiet
248
-
249
- suite.add_report rep, caller(1).first if suite
250
-
251
- reports << rep
91
+ module_function :scale
252
92
  end
253
-
254
- $stdout.sync = sync
255
-
256
- if job.compare
257
- Benchmark.compare(*reports)
258
- end
259
-
260
- return reports
261
93
  end
262
94
 
263
-
264
- module_function :ips
95
+ extend Benchmark::IPS # make ips available as module-level method
265
96
  end