d_heap 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,116 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "d_heap/benchmarks"
4
-
5
- require "benchmark_driver"
6
- require "shellwords"
7
- require "English"
8
-
9
- module DHeap::Benchmarks
10
- # Benchmarks different implementations with different sizes
11
- class Benchmarker
12
- include Randomness
13
- include Scenarios
14
-
15
- N_COUNTS = [
16
- 5, # 1 + 4
17
- 21, # 1 + 4 + 16
18
- 85, # 1 + 4 + 16 + 64
19
- 341, # 1 + 4 + 16 + 64 + 256
20
- 1365, # 1 + 4 + 16 + 64 + 256 + 1024
21
- 5461, # 1 + 4 + 16 + 64 + 256 + 1024 + 4096
22
- 21_845, # 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + 16384
23
- 87_381, # 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + 16384 + 65536
24
- ].freeze
25
-
26
- attr_reader :time
27
- attr_reader :iterations_for_push_pop
28
- attr_reader :io
29
-
30
- def initialize(
31
- time: Integer(ENV.fetch("BENCHMARK_TIME", 10)),
32
- iterations_for_push_pop: 10_000,
33
- io: $stdout
34
- )
35
- @time = time
36
- @iterations_for_push_pop = Integer(iterations_for_push_pop)
37
- @io = io
38
- end
39
-
40
- def call(queue_size: ENV.fetch("BENCHMARK_QUEUE_SIZE", :unset))
41
- DHeap::Benchmarks.puts_version_info("Benchmarking")
42
- sizes = (queue_size == :unset) ? N_COUNTS : [Integer(queue_size)]
43
- sizes.each do |size|
44
- benchmark_size(size)
45
- end
46
- end
47
-
48
- def benchmark_size(size)
49
- sep "#", "Benchmarks with N=#{size} (t=#{time}sec/benchmark)", big: true
50
- io.puts
51
- benchmark_push_n size
52
- benchmark_push_n_then_pop_n size
53
- benchmark_repeated_push_pop size
54
- end
55
-
56
- def benchmark_push_n(queue_size)
57
- benchmarking("push N", "push_n", queue_size)
58
- end
59
-
60
- def benchmark_push_n_then_pop_n(queue_size)
61
- benchmarking("push N then pop N", "push_n_pop_n", queue_size)
62
- end
63
-
64
- def benchmark_repeated_push_pop(queue_size)
65
- benchmarking(
66
- "Push/pop with pre-filled queue (size=N)", "push_pop", queue_size
67
- )
68
- end
69
-
70
- private
71
-
72
- # TODO: move somewhere else...
73
- def skip_profiling?(queue_size, impl)
74
- impl.klass == DHeap::Benchmarks::PushAndResort && 10_000 < queue_size
75
- end
76
-
77
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
78
-
79
- def benchmarking(name, file, size)
80
- Bundler.with_unbundled_env do
81
- sep "==", "#{name} (N=#{size})"
82
- cmd = %W[
83
- bin/benchmark-driver
84
- --bundler
85
- --run-duration 6
86
- --timeout 15
87
- --runner ips_zero_fail
88
- benchmarks/#{file}.yml
89
- ]
90
- if file == "push_n"
91
- cmd << "--filter" << /dheap|\bstl\b|\bbsearch\b|\brb_heap\b/.to_s
92
- end
93
- env = ENV.to_h.merge(
94
- "BENCH_N" => size.to_s,
95
- "RUBYLIB" => File.expand_path("../..", __dir__),
96
- )
97
- system(env, *cmd)
98
- end
99
- end
100
-
101
- def sep(sep, msg = "", width: 80, big: false)
102
- txt = String.new
103
- txt += "#{sep * (width / sep.length)}\n" if big
104
- txt += sep
105
- txt += " #{msg}" if msg && !msg.empty?
106
- txt += " " unless big
107
- txt += sep * ((width - txt.length) / sep.length) unless big
108
- txt += "\n"
109
- txt += "#{sep * (width / sep.length)}\n" if big
110
- io.print txt
111
- end
112
-
113
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
114
-
115
- end
116
- end
@@ -1,224 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fc"
4
-
5
- module DHeap::Benchmarks
6
-
7
- # base class for example priority queues
8
- class ExamplePriorityQueue
9
- attr_reader :a
10
-
11
- # quick initialization by simply sorting the array once.
12
- def initialize(count = nil, &block)
13
- @a = []
14
- return unless count
15
- count.times {|i| @a << block.call(i) }
16
- @a.sort!
17
- end
18
-
19
- def clear
20
- @a.clear
21
- end
22
-
23
- def empty?
24
- @a.empty?
25
- end
26
-
27
- if ENV["LOG_LEVEL"] == "debug"
28
- def dbg(msg)
29
- puts "%20s: %p, %p" % [msg, @a.first, (@a[1..-1] || []).each_slice(2).to_a]
30
- end
31
- else
32
- def dbg(msg) nil end
33
- end
34
-
35
- end
36
-
37
- # The most naive approach--completely unsorted!--is ironically not the worst.
38
- class FindMin < ExamplePriorityQueue
39
-
40
- # O(1)
41
- def <<(score)
42
- raise ArgumentError unless score
43
- @a.push score
44
- end
45
-
46
- # O(n)
47
- def pop
48
- return unless (score = @a.min)
49
- index = @a.rindex(score)
50
- @a.delete_at(index)
51
- score
52
- end
53
-
54
- end
55
-
56
- # Re-sorting after each insert: this both naive and performs the worst.
57
- class Sorting < ExamplePriorityQueue
58
-
59
- # O(n log n)
60
- def <<(score)
61
- raise ArgumentError unless score
62
- @a.push score
63
- @a.sort!
64
- end
65
-
66
- # O(1)
67
- def pop
68
- @a.shift
69
- end
70
-
71
- end
72
-
73
- # A very simple example priority queue that is implemented with a sorted array.
74
- #
75
- # It uses Array#bsearch + Array#insert to push new values, and Array#pop to pop
76
- # the min value.
77
- class BSearch < ExamplePriorityQueue
78
-
79
- # Array#bsearch_index is O(log n)
80
- # Array#insert is O(n)
81
- #
82
- # So this should be O(n).
83
- #
84
- # In practice though, memcpy has a *very* small constant factor.
85
- # And bsearch_index uses *exactly* (log n / log 2) comparisons.
86
- def <<(score)
87
- raise ArgumentError unless score
88
- index = @a.bsearch_index {|other| score > other } || @a.length
89
- @a.insert(index, score)
90
- end
91
-
92
- # Array#pop is O(1). It updates length without changing capacity or contents.
93
- #
94
- # No comparisons are necessary.
95
- #
96
- # shift is usually also O(1) and could be used if it were sorted normally.
97
- def pop
98
- @a.pop
99
- end
100
-
101
- end
102
-
103
- # a very simple pure ruby binary heap
104
- class RbHeap < ExamplePriorityQueue
105
-
106
- def <<(value)
107
- raise ArgumentError unless value
108
- @a.push(value)
109
- sift_up(@a.size - 1, value)
110
- end
111
-
112
- def pop
113
- return if @a.empty?
114
- popped = @a.first
115
- value = @a.pop
116
- last_index = @a.size - 1
117
- return popped unless 0 <= last_index
118
-
119
- sift_down(0, last_index, value)
120
- popped
121
- end
122
-
123
- private
124
-
125
- def sift_up(index, value = @a[index])
126
- while 0 < index # rubocop:disable Style/NumericPredicate
127
- parent_index = (index - 1) / 2
128
- parent_value = @a[parent_index]
129
- break if parent_value <= value
130
- @a[index] = parent_value
131
- index = parent_index
132
- end
133
- @a[index] = value
134
- # check_heap!(index)
135
- end
136
-
137
- def sift_down(index, last_index = @a.size - 1, value = @a[index])
138
- last_parent = (last_index - 1) / 2
139
- while index <= last_parent
140
- child_index, child_value = select_min_child(index, last_index)
141
- break if value <= child_value
142
- @a[index] = child_value
143
- index = child_index
144
- child_index = index * 2 + 1
145
- end
146
- @a[index] = value
147
- end
148
-
149
- def select_min_child(index, last_index = @a.size - 1)
150
- child_index = index * 2 + 1
151
- if child_index < last_index && a[child_index + 1] < @a[child_index]
152
- child_index += 1
153
- end
154
- [child_index, @a[child_index]]
155
- end
156
-
157
- def check_heap!(idx)
158
- check_heap_up!(idx)
159
- check_heap_dn!(idx)
160
- end
161
-
162
- # compares index to its parent
163
- def check_heap_at!(idx)
164
- value = @a[idx]
165
- unless idx <= 0
166
- pidx = (idx - 1) / 2
167
- pval = @a[pidx]
168
- raise "@a[#{idx}] == #{value}, #{pval} > #{value}" if pval > value
169
- end
170
- value
171
- end
172
-
173
- def check_heap_up!(idx)
174
- return if idx <= 0
175
- pidx = (idx - 1) / 2
176
- check_heap_at!(pidx)
177
- check_heap_up!(pidx)
178
- end
179
-
180
- def check_heap_dn!(idx)
181
- return unless @a.size <= idx
182
- check_heap_at!(idx)
183
- check_heap_down!(idx * 2 + 1)
184
- check_heap_down!(idx * 2 + 2)
185
- end
186
-
187
- end
188
-
189
- # minor adjustments to the "priority_queue_cxx" gem, to match the API
190
- class CppSTL
191
-
192
- def initialize
193
- clear
194
- end
195
-
196
- def <<(value); @q.push(value, value) end
197
-
198
- def clear
199
- @q = FastContainers::PriorityQueue.new(:min)
200
- end
201
-
202
- def empty?
203
- @q.empty?
204
- end
205
-
206
- def pop
207
- @q.pop
208
- rescue RuntimeError
209
- nil
210
- end
211
-
212
- end
213
-
214
- # Different duck-typed priority queue implemenations
215
- IMPLEMENTATIONS = [
216
- OpenStruct.new(name: " push and resort", klass: Sorting).freeze,
217
- OpenStruct.new(name: " find min + del", klass: FindMin).freeze,
218
- OpenStruct.new(name: "bsearch + insert", klass: BSearch).freeze,
219
- OpenStruct.new(name: "ruby binary heap", klass: RbHeap).freeze,
220
- OpenStruct.new(name: "C++STL PriorityQ", klass: CppSTL).freeze,
221
- OpenStruct.new(name: "quaternary DHeap", klass: DHeap).freeze,
222
- ].freeze
223
-
224
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "d_heap/benchmarks"
4
-
5
- require "ruby-prof"
6
-
7
- module DHeap::Benchmarks
8
- # Profiles different implementations with different sizes
9
- class Profiler
10
- include Randomness
11
- include Scenarios
12
-
13
- N_COUNTS = [
14
- 5, # 1 + 4
15
- 1365, # 1 + 4 + 16 + 64 + 256 + 1024
16
- 87_381, # 1 + 4 + 16 + 64 + 256 + 1024 + 4096 + 16384 + 65536
17
- ].freeze
18
-
19
- def call(
20
- queue_size: ENV.fetch("PROFILE_QUEUE_SIZE", :unset),
21
- iterations: ENV.fetch("PROFILE_ITERATIONS", 1_000_000)
22
- )
23
- DHeap::Benchmarks.puts_version_info("Profiling")
24
- fill_random_vals
25
- sizes = queue_size == :unset ? N_COUNTS : [Integer(queue_size)]
26
- sizes.each do |size|
27
- profile_all(size, iterations)
28
- end
29
- end
30
-
31
- def profile_all(queue_size, iterations, io: $stdout)
32
- io.puts <<~TEXT
33
- ########################################################################
34
- # Profile w/ N=#{queue_size} (i=#{iterations})
35
- # (n.b. RubyProf & tracepoint can change relative performance.
36
- # A sampling profiler can provide more accurate relative metrics.
37
- ########################################################################
38
-
39
- TEXT
40
- DHeap::Benchmarks::IMPLEMENTATIONS.each do |impl|
41
- profile_one(impl, queue_size, iterations, io: io)
42
- end
43
- end
44
-
45
- # TODO: move somewhere else...
46
- def skip_profiling?(queue_size, impl)
47
- impl.klass == DHeap::Benchmarks::Sorting && 10_000 < queue_size
48
- end
49
-
50
- def profile_one(impl, queue_size, iterations, io: $stdout)
51
- return if skip_profiling?(queue_size, impl)
52
- io.puts "Filling #{impl.name} ---------------------------"
53
- queue = impl.klass.new
54
- push_n(queue, queue_size)
55
- io.puts "Profiling #{impl.name} ---------------------------"
56
- profiling do
57
- repeated_push_pop(queue, iterations)
58
- end
59
- end
60
-
61
- def profiling(io: $stdout, &block)
62
- # do the thing
63
- result = RubyProf.profile(&block)
64
- # report_the_thing
65
- printer = RubyProf::FlatPrinter.new(result)
66
- printer.print($stdout, min_percent: 1.0)
67
- io.puts
68
- end
69
-
70
- end
71
- end
@@ -1,352 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "d_heap/benchmarks"
4
-
5
- module DHeap::Benchmarks
6
-
7
- # Profiles different implementations with different sizes
8
- module RSpecMatchers # rubocop:disable Metrics/ModuleLength
9
- extend RSpec::Matchers::DSL
10
-
11
- # Assert ips (iterations per second):
12
- #
13
- # expect { ... }.to perform_at_least(1_000_000).ips
14
- # .running_at_least(10).times # optional, defaults to 1
15
- # .running_at_least(10).seconds # optional, defaults to 1s
16
- # .running_at_most(10_000_000).times # optional, defaults to nil
17
- # .running_at_most(2).seconds # optional, defaults to 2s
18
- # .warmup_at_most(1000).times # optional, defaults to 1k
19
- # .warmup_at_most(0.100).seconds # optional, defaults to 0.1s
20
- # .iterations_per_round # optional, defaults to 1
21
- # .and_at_least(1.1).times.faster_than { ... } # can also compare
22
- #
23
- # Assert comparison (and optionally runtime or ips):
24
- #
25
- # expect { ... }.to perform_at_least(2.5).times_faster_than { ... }
26
- # .running_at_least(10).times # optional, defaults to 1
27
- # .running_at_least(10).seconds # optional, defaults to 1s
28
- # .running_at_most(10_000_000).times # optional, defaults to nil
29
- # .running_at_most(2).seconds # optional, defaults to 2s
30
- # .warmup_at_most(1000).times # optional, defaults to 1k
31
- # .warmup_at_most(0.100).seconds # optional, defaults to 0.1s
32
- # .iterations_per_call # optional, defaults to 1
33
- # .and_at_least(100).ips { ... } # can also assert ips
34
- #
35
- # n.b: Given a known constant number of iterations, run time and ips are both
36
- # measuring the same underlying metric.
37
- #
38
- # rubocop:disable Metrics/BlockLength, Layout/SpaceAroundOperators
39
- matcher :perform_at_least do |expected|
40
- supports_block_expectations
41
-
42
- %i[
43
- is_at_least
44
- running_at_most
45
- running_at_least
46
- warmup_at_most
47
- ].each do |type|
48
- chain type do |number|
49
- reason, value = ___number_reason_and_value___
50
- if reason || value
51
- raise "Need to handle unit-less number first: %s(%p)" % [reason, value]
52
- end
53
- @number_for = type
54
- @number_val = number
55
- end
56
- end
57
-
58
- alias_method :and_at_least, :is_at_least
59
-
60
- %i[
61
- times
62
- seconds
63
- milliseconds
64
- ].each do |unit|
65
- chain unit do
66
- reason, value = ___number_reason_and_value___
67
- raise "No number was specified" unless reason && value
68
- apply_number_to_reason(reason, value, unit)
69
- @number_for = @number_val = nil
70
- end
71
- end
72
-
73
- # TODO: let IPS set time to run instead of iterations to run
74
- chain :ips do
75
- reason, value = ___number_reason_and_value___
76
- raise "'ips' unit is only for assertions" unless reason == :is_at_least
77
- raise "Already asserting %s ips" % [@expect_ips] if @expect_ips
78
- raise "'ips' assertion has already been made" if @expect_ips
79
- raise "Unknown assertion count" unless value
80
- @expect_ips = Integer(value)
81
- @number_for = @number_val = nil
82
- end
83
-
84
- # need to use method because "chain" can't take a block
85
- def times_faster_than(&other)
86
- reason, value = ___number_reason_and_value___
87
- raise "'times_faster_than' is only for assertions" unless reason == :is_at_least
88
- raise "Already asserting %sx comparison" % [@expect_cmp] if @expect_cmp
89
- raise ArgumentError, "must provide a proc" unless other
90
- @expect_cmp = Float(value)
91
- @cmp_proc = other
92
- @number_for = @number_val = nil
93
- self
94
- end
95
-
96
- chain :loudly do @volume = :loud end
97
- chain :quietly do @volume = :quiet end
98
- chain :volume do |volume|
99
- raise "Invalid volume" unless %i[loud quiet].include?(volume)
100
- @volume = volume
101
- end
102
-
103
- chain :iterations_per_round do |iterations|
104
- if @iterations_per_round
105
- raise "Already set iterations per round (%p)" [@iterations_per_round]
106
- end
107
- @iterations_per_round = Integer(iterations)
108
- end
109
-
110
- match do |actual|
111
- require "benchmark"
112
- raise "Need to expect a proc or block" unless actual.respond_to?(:to_proc)
113
- raise "Need a performance assertion" unless assertion?
114
- @actual_proc = actual
115
- prepare_for_measurement
116
- if @max_iter && (@max_iter % @iterations_per_round) != 0
117
- raise "Iterations per round (%p) must divide evenly by max iterations (%p)" % [
118
- @iterations_per_round, @max_iter,
119
- ]
120
- end
121
- run_measurements
122
- cmp_okay? && ips_okay?
123
- end
124
-
125
- description do
126
- [
127
- @expect_cmp && cmp_okay_msg,
128
- @expect_ips && ips_okay_msg,
129
- ].join(", and ")
130
- end
131
-
132
- failure_message do
133
- [
134
- cmp_okay? ? nil : "expected to #{cmp_okay_msg} but #{cmp_fail_msg}", # =>
135
- ips_okay? ? nil : "expected to #{ips_okay_msg} but #{ips_fail_msg}",
136
- ].compact.join(", and ")
137
- end
138
-
139
- private
140
-
141
- chain :__convert_expected_to_ivars__ do
142
- @number_val ||= expected
143
- @number_for ||= :is_at_least if @number_val
144
- expected = nil
145
- end
146
- private :__convert_expected_to_ivars__
147
-
148
- def ___number_reason_and_value___
149
- __convert_expected_to_ivars__
150
- [@number_for, @number_val]
151
- end
152
-
153
- def apply_number_to_reason(reason, value, unit)
154
- normalized_value, normalized_unit = normalize_unit(unit)
155
- case reason
156
- when :running_at_most; apply_max_run normalized_value, normalized_unit
157
- when :running_at_least; apply_min_run normalized_value, normalized_unit
158
- when :warmup_at_most; apply_warmup normalized_value, normalized_unit
159
- else raise "%s is incompatible with %s(%p)" % [unit, reason, value]
160
- end
161
- end
162
-
163
- def normalize_unit(unit)
164
- case unit
165
- when :seconds; [Float(@number_val), :seconds]
166
- when :milliseconds; [Float(@number_val) / 1000.0, :seconds]
167
- when :times; [Integer(@number_val), :times]
168
- else raise "Invalid unit %s for %s(%p)" % [unit, reason, value]
169
- end
170
- end
171
-
172
- def apply_min_run(value, unit)
173
- case unit
174
- when :seconds; @min_time = value
175
- when :times; @min_iter = value
176
- end
177
- end
178
-
179
- def apply_max_run(value, unit)
180
- case unit
181
- when :seconds; @max_time = value
182
- when :times; @max_iter = value
183
- end
184
- end
185
-
186
- def apply_warmup(value, unit)
187
- case unit
188
- when :seconds; @warmup_time = value
189
- when :times; @warmup_iter = value
190
- end
191
- end
192
-
193
- def prepare_for_measurement
194
- @volume ||= ENV.fetch("RSPEC_BENCHMARK_VOLUME", :quiet).to_sym
195
- @max_time ||= 2
196
- @min_time ||= 1
197
- @min_iter ||= 1
198
- @warmup_time ||= 0.100
199
- @warmup_iter ||= 1000
200
- @iterations_per_round ||= 1
201
- nil
202
- end
203
-
204
- def run_measurements
205
- puts header if loud?
206
- warmup
207
- take_measurements
208
- end
209
-
210
- def header
211
- max_rounds = @max_iter && @max_iter / @iterations_per_round
212
- [
213
- "Warmup time %s, or iterations: %s" % [@min_iter, @max_iter],
214
- "Benchmark time (%s..%s) or iterations (%s..%s), max rounds: %p" % [
215
- @min_time, @max_time, @min_iter, @max_iter, max_rounds,
216
- ],
217
- "%-10s %s" % ["", Benchmark::CAPTION],
218
- ].join("\n")
219
- end
220
-
221
- def warmup
222
- return unless 0 < @warmup_time && 0 < @warmup_iter # rubocop:disable Style/NumericPredicate
223
- args = [@warmup_iter, 0, @warmup_time, 1, @warmup_iter]
224
- measure("warmup", *args, &@actual_proc)
225
- measure("warmup cmp", *args, &@cmp_proc) if @cmp_proc
226
- end
227
-
228
- def take_measurements
229
- args = [@iterations_per_round, @min_time, @max_time, @min_iter, @max_iter]
230
- @actual_tms = measure("actual", *args, &@actual_proc)
231
- @cmp_tms = measure("cmp", *args, &@cmp_proc) if @cmp_proc
232
- return unless @cmp_proc
233
- # how many times faster?
234
- @actual_cmp = @actual_tms.ips_real / @cmp_tms.ips_real
235
- puts "Ran %0.3fx as fast as comparison" % [@actual_cmp] if loud?
236
- end
237
-
238
- def loud?; @volume == :loud end
239
-
240
- def assertion?; !!(@expect_cmp || @expect_ips) end
241
-
242
- def cmp_okay?; !@expect_cmp || @expect_cmp < @actual_cmp end
243
- def ips_okay?; !@expect_tms || @expect_tms.ips < @actual_tms.ips end
244
-
245
- def measure(name, ipr, *args)
246
- measurements = TmsMeasurements.new(name, ipr, *args)
247
- measurements.max_rounds.times do
248
- # GC.start(full_mark: true, immediate_sweep: true)
249
- # GC.compact
250
- measurements << Benchmark.measure do
251
- yield ipr
252
- end
253
- # p measurements.real
254
- break if measurements.max_time < measurements.real
255
- end
256
- log_measurement(name, measurements)
257
- measurements
258
- end
259
-
260
- # rubocop:disable Metrics/AbcSize
261
- def units_str(num)
262
- if num >= 10**12; "%7.3fT" % [num.to_f / 10**12]
263
- elsif num >= 10** 9; "%7.3fB" % [num.to_f / 10** 9]
264
- elsif num >= 10** 6; "%7.3fM" % [num.to_f / 10** 6]
265
- elsif num >= 10** 3; "%7.3fk" % [num.to_f / 10** 3]
266
- else "%7.3f" % [num.to_f]
267
- end
268
- end
269
- # rubocop:enable Metrics/AbcSize
270
-
271
- def log_measurement(name, measurements)
272
- return unless loud?
273
- puts "%-10s %s => %s ips (%d rounds)" % [
274
- name,
275
- measurements.tms.to_s.rstrip,
276
- units_str(measurements.ips_real),
277
- measurements.size,
278
- ]
279
- end
280
-
281
- def cmp_okay_msg; "run %0.2fx faster" % [@expect_cmp] end
282
- def cmp_fail_msg; "was only %0.2fx as fast" % [@actual_cmp] end
283
- def ips_okay_msg; "run with %s ips" % [units_str(@expect_ips)] end
284
- def ips_fail_msg; "was only %s ips" % [units_str(@actual_ips)] end
285
-
286
- end
287
- # rubocop:enable Metrics/BlockLength, Layout/SpaceAroundOperators
288
-
289
- alias_matcher :perform_with, :perform
290
-
291
- end
292
-
293
- # Replicates a subset of the functionality in benchmark-ips
294
- #
295
- # TODO: merge this with benchmark-ips
296
- # TODO: implement (or remove) min_time, min_iter
297
- class TmsMeasurements
298
- attr_reader :iterations_per_entry
299
- attr_reader :iterations
300
-
301
- attr_reader :min_time
302
- attr_reader :max_time
303
-
304
- attr_reader :min_iter
305
- attr_reader :max_iter
306
-
307
- def initialize(name, ipe, min_time, max_time, min_iter, max_iter) # rubocop:disable Metrics/ParameterLists
308
- @name = name
309
- @iterations_per_entry = Integer(ipe)
310
- @min_time = Float(min_time)
311
- @max_time = Float(max_time)
312
- @min_iter = Integer(min_iter)
313
- @max_iter = Integer(max_iter)
314
- @entries = []
315
- @sum = Benchmark::Tms.new
316
- @iterations = 0
317
- end
318
-
319
- def size; entries.size end
320
-
321
- def <<(tms)
322
- raise TypeError, "not a #{Benchmark::Tms}" unless tms.is_a?(Benchmark::Tms)
323
- raise IndexError, "full" if @max_iter <= size
324
- @sum += tms
325
- @iterations += @iterations_per_entry
326
- @entries << tms
327
- self
328
- end
329
-
330
- def sum; @sum.dup end
331
- alias tms sum
332
-
333
- def entries; @entries.dup end
334
-
335
- def cstime; @sum.cstime end
336
- def cutime; @sum.cutime end
337
- def real; @sum.real end
338
- def stime; @sum.stime end
339
- def total; @sum.total end
340
- def utime; @sum.utime end
341
-
342
- def ips_real; @iterations / real end
343
- def ips_total; @iterations / total end
344
- def ips_utime; @iterations / utime end
345
-
346
- def max_rounds
347
- @max_iter && @max_iter / @iterations_per_entry
348
- end
349
-
350
- end
351
-
352
- end