motion-benchmark-ips 1.0 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,189 @@
1
+ # encoding: utf-8
2
+
3
+ module Benchmark
4
+ module IPS
5
+
6
+ # Report contains benchmarking entries.
7
+ # Perform operations like add new entry, run comparison between entries.
8
+ class Report
9
+
10
+ # Represents benchmarking code data for Report.
11
+ class Entry
12
+ # Instantiate the Benchmark::IPS::Report::Entry.
13
+ # @param [#to_s] label Label of entry.
14
+ # @param [Integer] us Measured time in microsecond.
15
+ # @param [Integer] iters Iterations.
16
+ # @param [Object] stats Statistics.
17
+ # @param [Integer] cycles Number of Cycles.
18
+ def initialize(label, us, iters, stats, cycles)
19
+ @label = label
20
+ @microseconds = us
21
+ @iterations = iters
22
+ @stats = stats
23
+ @measurement_cycle = cycles
24
+ @show_total_time = false
25
+ end
26
+
27
+ # Label of entry.
28
+ # @return [String] the label of entry.
29
+ attr_reader :label
30
+
31
+ # Measured time in microsecond.
32
+ # @return [Integer] number of microseconds.
33
+ attr_reader :microseconds
34
+
35
+ # Number of Iterations.
36
+ # @return [Integer] number of iterations.
37
+ attr_reader :iterations
38
+
39
+ # Statistical summary of samples.
40
+ # @return [Object] statisical summary.
41
+ attr_reader :stats
42
+
43
+ # LEGACY: Iterations per second.
44
+ # @return [Float] number of iterations per second.
45
+ def ips
46
+ @stats.central_tendency
47
+ end
48
+
49
+ # LEGACY: Standard deviation of iteration per second.
50
+ # @return [Float] standard deviation of iteration per second.
51
+ def ips_sd
52
+ @stats.error
53
+ end
54
+
55
+ # Number of Cycles.
56
+ # @return [Integer] number of cycles.
57
+ attr_reader :measurement_cycle
58
+
59
+ # Control if the total time the job took is reported.
60
+ # Typically this value is not significant because it's very
61
+ # close to the expected time, so it's supressed by default.
62
+ def show_total_time!
63
+ @show_total_time = true
64
+ end
65
+
66
+ # Return entry's microseconds in seconds.
67
+ # @return [Float] +@microseconds+ in seconds.
68
+ def seconds
69
+ @microseconds.to_f / 1_000_000.0
70
+ end
71
+
72
+ # Return entry's standard deviation of iteration per second in percentage.
73
+ # @return [Float] +@ips_sd+ in percentage.
74
+ def error_percentage
75
+ 100.0 * (@stats.error.to_f / @stats.central_tendency)
76
+ end
77
+
78
+ alias_method :runtime, :seconds
79
+
80
+ # Return Entry body text with left padding.
81
+ # Body text contains information of iteration per second with
82
+ # percentage of standard deviation, iterations in runtime.
83
+ # @return [String] Left justified body.
84
+ def body
85
+ case Benchmark::IPS.options[:format]
86
+ when :human
87
+ left = "%s (±%4.1f%%) i/s" % [Helpers.scale(@stats.central_tendency), error_percentage]
88
+ iters = Helpers.scale(@iterations)
89
+
90
+ if @show_total_time
91
+ left.ljust(20) + (" - %s in %10.6fs" % [iters, runtime])
92
+ else
93
+ left.ljust(20) + (" - %s" % iters)
94
+ end
95
+ else
96
+ left = "%10.1f (±%.1f%%) i/s" % [@stats.central_tendency, error_percentage]
97
+
98
+ if @show_total_time
99
+ left.ljust(20) + (" - %10d in %10.6fs" % [@iterations, runtime])
100
+ else
101
+ left.ljust(20) + (" - %10d" % @iterations)
102
+ end
103
+ end
104
+ end
105
+
106
+ # Return header with padding if +@label+ is < length of 20.
107
+ # @return [String] Right justified header (+@label+).
108
+ def header
109
+ @label.to_s.rjust(20)
110
+ end
111
+
112
+ # Return string repesentation of Entry object.
113
+ # @return [String] Header and body.
114
+ def to_s
115
+ "#{header} #{body}"
116
+ end
117
+
118
+ # Print entry to current standard output ($stdout).
119
+ def display
120
+ $stdout.puts to_s
121
+ end
122
+ end # End of Entry
123
+
124
+ # class Report
125
+
126
+ # Entry to represent each benchmarked code in Report.
127
+ # @return [Array<Report::Entry>] Entries in Report.
128
+ attr_reader :entries
129
+
130
+ # Instantiate the Report.
131
+ def initialize
132
+ @entries = []
133
+ @data = nil
134
+ end
135
+
136
+ # Add entry to report.
137
+ # @param label [String] Entry label.
138
+ # @param microseconds [Integer] Measured time in microsecond.
139
+ # @param iters [Integer] Iterations.
140
+ # @param stats [Object] Statistical results.
141
+ # @param measurement_cycle [Integer] Number of cycles.
142
+ # @return [Report::Entry] Last added entry.
143
+ def add_entry label, microseconds, iters, stats, measurement_cycle
144
+ entry = Entry.new(label, microseconds, iters, stats, measurement_cycle)
145
+ @entries.delete_if { |e| e.label == label }
146
+ @entries << entry
147
+ entry
148
+ end
149
+
150
+ # Entries data in array for generate json.
151
+ # Each entry is a hash, consists of:
152
+ # name: Entry#label
153
+ # ips: Entry#ips
154
+ # stddev: Entry#ips_sd
155
+ # microseconds: Entry#microseconds
156
+ # iterations: Entry#iterations
157
+ # cycles: Entry#measurement_cycles
158
+ # @return [Array<Hash<Symbol,String|Float|Integer>] Array of hashes
159
+ def data
160
+ @data ||= @entries.collect do |entry|
161
+ {
162
+ :name => entry.label,
163
+ :central_tendency => entry.stats.central_tendency,
164
+ :ips => entry.stats.central_tendency, # for backwards compatibility
165
+ :error => entry.stats.error,
166
+ :stddev => entry.stats.error, # for backwards compatibility
167
+ :microseconds => entry.microseconds,
168
+ :iterations => entry.iterations,
169
+ :cycles => entry.measurement_cycle,
170
+ }
171
+ end
172
+ end
173
+
174
+ # Run comparison of entries.
175
+ def run_comparison
176
+ Benchmark.compare(*@entries)
177
+ end
178
+
179
+ # Generate json from Report#data to given path.
180
+ # @param path [String] path to generate json.
181
+ def generate_json(path)
182
+ File.open path, "w" do |f|
183
+ require "json"
184
+ f.write JSON.pretty_generate(data)
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,51 @@
1
+ module Benchmark
2
+ module IPS
3
+ module Stats
4
+
5
+ class Bootstrap
6
+
7
+ attr_reader :data
8
+
9
+ def initialize(samples, confidence)
10
+ dependencies
11
+ @iterations = 10_000
12
+ @confidence = (confidence / 100.0).to_s
13
+ @data = Kalibera::Data.new({[0] => samples}, [1, samples.size])
14
+ interval = @data.bootstrap_confidence_interval(@iterations, @confidence)
15
+ @median = interval.median
16
+ @error = interval.error
17
+ end
18
+
19
+ def central_tendency
20
+ @median
21
+ end
22
+
23
+ def error
24
+ @error
25
+ end
26
+
27
+ def slowdown(baseline)
28
+ low, slowdown, high = baseline.data.bootstrap_quotient(@data, @iterations, @confidence)
29
+ error = Timing.mean([slowdown - low, high - slowdown])
30
+ [slowdown, error]
31
+ end
32
+
33
+ def footer
34
+ "with #{(@confidence.to_f * 100).round(1)}% confidence"
35
+ end
36
+
37
+ def dependencies
38
+ require 'kalibera'
39
+ rescue LoadError
40
+ puts
41
+ puts "Can't load the kalibera gem - this is required to use the :bootstrap stats options."
42
+ puts "It's optional, so we don't formally depend on it and it isn't installed along with benchmark-ips."
43
+ puts "You probably want to do something like 'gem install kalibera' to fix this."
44
+ abort
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,33 @@
1
+ module Benchmark
2
+ module IPS
3
+ module Stats
4
+
5
+ class SD
6
+
7
+ def initialize(samples)
8
+ @mean = Timing.mean(samples)
9
+ @error = Timing.stddev(samples, @mean).round
10
+ end
11
+
12
+ def central_tendency
13
+ @mean
14
+ end
15
+
16
+ def error
17
+ @error
18
+ end
19
+
20
+ def slowdown(baseline)
21
+ slowdown = baseline.central_tendency.to_f / central_tendency
22
+ [slowdown, nil]
23
+ end
24
+
25
+ def footer
26
+ nil
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -1,10 +1,20 @@
1
1
  module Benchmark
2
+ # Perform caclulations on Timing results.
2
3
  module Timing
4
+ # Microseconds per second.
5
+ MICROSECONDS_PER_SECOND = 1_000_000
6
+
7
+ # Calculate (arithmetic) mean of given samples.
8
+ # @param [Array] samples Samples to calculate mean.
9
+ # @return [Float] Mean of given samples.
3
10
  def self.mean(samples)
4
- sum = samples.inject(0) { |acc, i| acc + i }
11
+ sum = samples.inject(:+)
5
12
  sum / samples.size
6
13
  end
7
14
 
15
+ # Calculate variance of given samples.
16
+ # @param [Float] m Optional mean (Expected value).
17
+ # @return [Float] Variance of given samples.
8
18
  def self.variance(samples, m=nil)
9
19
  m ||= mean(samples)
10
20
 
@@ -13,21 +23,15 @@ module Benchmark
13
23
  total / samples.size
14
24
  end
15
25
 
26
+ # Calculate standard deviation of given samples.
27
+ # @param [Array] samples Samples to calculate standard deviation.
28
+ # @param [Float] m Optional mean (Expected value).
29
+ # @return [Float] standard deviation of given samples.
16
30
  def self.stddev(samples, m=nil)
17
31
  Math.sqrt variance(samples, m)
18
32
  end
19
33
 
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
-
34
+ # Recycle used objects by starting Garbage Collector.
31
35
  def self.clean_env
32
36
  # rbx
33
37
  if GC.respond_to? :run
@@ -36,5 +40,41 @@ module Benchmark
36
40
  GC.start
37
41
  end
38
42
  end
43
+
44
+ # Use a monotonic clock if available, otherwise use Time
45
+ begin
46
+ ## RubyMotion has not supported Process.clock_gettime
47
+ # Process.clock_gettime Process::CLOCK_MONOTONIC, :float_microsecond
48
+
49
+ # # Get an object that represents now and can be converted to microseconds
50
+ # def self.now
51
+ # Process.clock_gettime Process::CLOCK_MONOTONIC, :float_microsecond
52
+ # end
53
+
54
+ # # Add one second to the time represenetation
55
+ # def self.add_second(t, s)
56
+ # t + (s * MICROSECONDS_PER_SECOND)
57
+ # end
58
+
59
+ # # Return the number of microseconds between the 2 moments
60
+ # def self.time_us(before, after)
61
+ # after - before
62
+ # end
63
+ # rescue NameError
64
+ # Get an object that represents now and can be converted to microseconds
65
+ def self.now
66
+ Time.now
67
+ end
68
+
69
+ # Add one second to the time represenetation
70
+ def self.add_second(t, s)
71
+ t + s
72
+ end
73
+
74
+ # Return the number of microseconds between the 2 moments
75
+ def self.time_us(before, after)
76
+ (after.to_f - before.to_f) * MICROSECONDS_PER_SECOND
77
+ end
78
+ end
39
79
  end
40
80
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion-benchmark-ips
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.0'
4
+ version: '1.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Watson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-24 00:00:00.000000000 Z
11
+ date: 2017-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -35,8 +35,14 @@ files:
35
35
  - lib/motion-benchmark-ips.rb
36
36
  - lib/project/compare.rb
37
37
  - lib/project/ips.rb
38
+ - lib/project/ips/job.rb
39
+ - lib/project/ips/job/entry.rb
40
+ - lib/project/ips/job/stdout_report.rb
41
+ - lib/project/ips/report.rb
42
+ - lib/project/ips/stats/bootstrap.rb
43
+ - lib/project/ips/stats/sd.rb
38
44
  - lib/project/timing.rb
39
- homepage: ''
45
+ homepage: https://github.com/Watson1978/motion-benchmark-ips
40
46
  licenses:
41
47
  - MIT
42
48
  metadata: {}
@@ -56,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
56
62
  version: '0'
57
63
  requirements: []
58
64
  rubyforge_project:
59
- rubygems_version: 2.2.0
65
+ rubygems_version: 2.6.11
60
66
  signing_key:
61
67
  specification_version: 4
62
68
  summary: Provides iteration per second benchmarking for RubyMotion