d_heap 0.6.1 → 0.7.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.
@@ -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