motion-benchmark-ips 1.0 → 1.1
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 +4 -4
- data/README.md +27 -0
- data/lib/project/compare.rb +65 -30
- data/lib/project/ips.rb +67 -236
- data/lib/project/ips/job.rb +351 -0
- data/lib/project/ips/job/entry.rb +77 -0
- data/lib/project/ips/job/stdout_report.rb +64 -0
- data/lib/project/ips/report.rb +189 -0
- data/lib/project/ips/stats/bootstrap.rb +51 -0
- data/lib/project/ips/stats/sd.rb +33 -0
- data/lib/project/timing.rb +52 -12
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f60f433abf07be7027a49a2e7be67f9178e698ec
|
4
|
+
data.tar.gz: ae5e787909b978f143876b944d7631d875ed553d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
+
```
|
data/lib/project/compare.rb
CHANGED
@@ -1,41 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Benchmark
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
37
|
+
sorted = entries.sort_by{ |e| e.stats.central_tendency }.reverse
|
18
38
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
26
|
-
|
45
|
+
sorted.each do |report|
|
46
|
+
name = report.label.to_s
|
27
47
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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
|
-
|
75
|
+
extend Benchmark::Compare
|
41
76
|
end
|
data/lib/project/ips.rb
CHANGED
@@ -1,265 +1,96 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
#
|
3
|
-
# require 'benchmark/compare'
|
4
|
-
|
2
|
+
# Performance benchmarking library
|
5
3
|
module Benchmark
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
26
|
+
suite = nil
|
76
27
|
|
77
|
-
|
78
|
-
@as_action
|
79
|
-
end
|
28
|
+
sync, $stdout.sync = $stdout.sync, true
|
80
29
|
|
81
|
-
|
82
|
-
|
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
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
128
|
-
|
40
|
+
job_opts = {}
|
41
|
+
job_opts[:time] = time unless time.nil?
|
42
|
+
job_opts[:warmup] = warmup unless warmup.nil?
|
129
43
|
|
130
|
-
|
131
|
-
self
|
132
|
-
end
|
44
|
+
job.config job_opts
|
133
45
|
|
134
|
-
|
46
|
+
yield job
|
135
47
|
|
136
|
-
|
137
|
-
attr_reader :list
|
138
|
-
end
|
48
|
+
job.load_held_results if job.hold? && job.held_results?
|
139
49
|
|
140
|
-
|
141
|
-
suite = nil
|
50
|
+
job.run
|
142
51
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
suite = Benchmark::Suite.current
|
147
|
-
end
|
52
|
+
$stdout.sync = sync
|
53
|
+
job.run_comparison
|
54
|
+
job.generate_json
|
148
55
|
|
149
|
-
|
56
|
+
report = job.full_report
|
150
57
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
-
|
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
|