motion-benchmark-ips 1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 14cfdbe4568670fda3739ecd7a9837aa0b21a7c7
4
+ data.tar.gz: 550e6b910b78289a5b525450ed4ad0238e1b2e82
5
+ SHA512:
6
+ metadata.gz: 9acf976811223997ed734be9570a30e188b5282374fab9215d0028e12034106143464de40d3b1e144cc3bfb55b1f95f912a6d103d1f1181a776c77c118feca01
7
+ data.tar.gz: d00d71ba9fd3abdfaade152324a6b390099d5e5881be137ce8b856818d66f89edf64bcd10d90f19db13c6da9d14837aec4bda464c81a7e73d3f46857fd5128f6
@@ -0,0 +1,44 @@
1
+ # motion-benchmark-ips
2
+
3
+ Provides iteration per second benchmarking for RubyMotion.
4
+ The original is https://github.com/evanphx/benchmark-ips.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'motion-benchmark-ips'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install motion-benchmark-ips
19
+
20
+
21
+ ## LICENSE:
22
+
23
+ (The MIT License)
24
+
25
+ Copyright (c) 2014 Watson
26
+
27
+ Permission is hereby granted, free of charge, to any person obtaining
28
+ a copy of this software and associated documentation files (the
29
+ 'Software'), to deal in the Software without restriction, including
30
+ without limitation the rights to use, copy, modify, merge, publish,
31
+ distribute, sublicense, and/or sell copies of the Software, and to
32
+ permit persons to whom the Software is furnished to do so, subject to
33
+ the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be
36
+ included in all copies or substantial portions of the Software.
37
+
38
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
39
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
41
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
42
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
43
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
44
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "This file must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+ lib_dir_path = File.dirname(File.expand_path(__FILE__))
6
+ Motion::Project::App.setup do |app|
7
+ app.files.unshift(Dir.glob(File.join(lib_dir_path, "project/**/*.rb")))
8
+ end
@@ -0,0 +1,41 @@
1
+ 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
14
+
15
+ best = sorted.shift
16
+
17
+ STDOUT.puts "\nComparison:"
18
+
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
24
+
25
+ sorted.each do |report|
26
+ name = report.label
27
+
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"
34
+ end
35
+ end
36
+
37
+ STDOUT.puts
38
+ end
39
+
40
+ module_function :compare
41
+ end
@@ -0,0 +1,265 @@
1
+ # encoding: utf-8
2
+ # require 'benchmark/timing'
3
+ # require 'benchmark/compare'
4
+
5
+ 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
73
+ end
74
+
75
+ attr_reader :label, :action
76
+
77
+ def as_action?
78
+ @as_action
79
+ end
80
+
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
91
+ end
92
+
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
107
+
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
126
+
127
+ action = str || blk
128
+ raise ArgumentError, "no block or string" unless action
129
+
130
+ @list.push Entry.new(label, action)
131
+ self
132
+ end
133
+
134
+ alias_method :report, :item
135
+
136
+ # An array of 2-element arrays, consisting of label and block pairs.
137
+ attr_reader :list
138
+ end
139
+
140
+ def ips(time=5, warmup=2)
141
+ suite = nil
142
+
143
+ sync, $stdout.sync = $stdout.sync, true
144
+
145
+ if defined? Benchmark::Suite and Suite.current
146
+ suite = Benchmark::Suite.current
147
+ end
148
+
149
+ quiet = suite && !suite.quiet?
150
+
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
171
+ end
172
+
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
197
+ end
198
+
199
+ $stdout.puts "-------------------------------------------------" unless quiet
200
+
201
+ job.list.each do |item|
202
+ unless quiet
203
+ if item.label.size > 20
204
+ $stdout.print "#{item.label}\n#{' ' * 20}"
205
+ else
206
+ $stdout.print item.label.rjust(20)
207
+ end
208
+ 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
252
+ end
253
+
254
+ $stdout.sync = sync
255
+
256
+ if job.compare
257
+ Benchmark.compare(*reports)
258
+ end
259
+
260
+ return reports
261
+ end
262
+
263
+
264
+ module_function :ips
265
+ end
@@ -0,0 +1,40 @@
1
+ module Benchmark
2
+ module Timing
3
+ def self.mean(samples)
4
+ sum = samples.inject(0) { |acc, i| acc + i }
5
+ sum / samples.size
6
+ end
7
+
8
+ def self.variance(samples, m=nil)
9
+ m ||= mean(samples)
10
+
11
+ total = samples.inject(0) { |acc, i| acc + ((i - m) ** 2) }
12
+
13
+ total / samples.size
14
+ end
15
+
16
+ def self.stddev(samples, m=nil)
17
+ Math.sqrt variance(samples, m)
18
+ end
19
+
20
+ def self.resample_mean(samples, resample_times=100)
21
+ resamples = []
22
+
23
+ resample_times.times do
24
+ resample = samples.map { samples[rand(samples.size)] }
25
+ resamples << Timing.mean(resample)
26
+ end
27
+
28
+ resamples
29
+ end
30
+
31
+ def self.clean_env
32
+ # rbx
33
+ if GC.respond_to? :run
34
+ GC.run(true)
35
+ else
36
+ GC.start
37
+ end
38
+ end
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: motion-benchmark-ips
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Watson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Provides iteration per second benchmarking for RubyMotion
28
+ email:
29
+ - watson1978@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - lib/motion-benchmark-ips.rb
36
+ - lib/project/compare.rb
37
+ - lib/project/ips.rb
38
+ - lib/project/timing.rb
39
+ homepage: ''
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.2.0
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Provides iteration per second benchmarking for RubyMotion
63
+ test_files: []