d_heap 0.5.0 → 0.6.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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +2 -2
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/.yardopts +10 -0
- data/CHANGELOG.md +19 -6
- data/Gemfile +4 -0
- data/Gemfile.lock +10 -1
- data/N +7 -0
- data/README.md +185 -231
- data/benchmarks/push_n.yml +10 -6
- data/benchmarks/push_n_pop_n.yml +27 -10
- data/benchmarks/push_pop.yml +5 -0
- data/bin/bench_charts +13 -0
- data/d_heap.gemspec +1 -1
- data/ext/d_heap/d_heap.c +435 -140
- data/ext/d_heap/extconf.rb +3 -4
- data/images/push_n.png +0 -0
- data/images/push_n_pop_n.png +0 -0
- data/images/push_pop.png +0 -0
- data/images/wikipedia-min-heap.png +0 -0
- data/lib/benchmark_driver/runner/ips_zero_fail.rb +89 -51
- data/lib/d_heap.rb +81 -18
- data/lib/d_heap/benchmarks/implementations.rb +30 -28
- data/lib/d_heap/benchmarks/rspec_matchers.rb +29 -51
- data/lib/d_heap/version.rb +1 -1
- metadata +10 -4
- data/ext/d_heap/d_heap.h +0 -50
data/ext/d_heap/extconf.rb
CHANGED
@@ -8,10 +8,9 @@ require "mkmf"
|
|
8
8
|
# $CFLAGS << " -g -ginline-points "
|
9
9
|
# $CFLAGS << " -fno-omit-frame-pointer "
|
10
10
|
|
11
|
-
|
12
|
-
CONFIG["
|
13
|
-
|
14
|
-
CONFIG["warnflags"] << " -Werror"
|
11
|
+
if enable_config("debug")
|
12
|
+
CONFIG["warnflags"] << " -Werror -Wpedantic "
|
13
|
+
end
|
15
14
|
|
16
15
|
have_func "rb_gc_mark_movable" # since ruby-2.7
|
17
16
|
|
data/images/push_n.png
ADDED
Binary file
|
Binary file
|
data/images/push_pop.png
ADDED
Binary file
|
Binary file
|
@@ -29,77 +29,115 @@ class BenchmarkDriver::Runner::IpsZeroFail < BenchmarkDriver::Runner::Ips
|
|
29
29
|
class Job < BenchmarkDriver::DefaultJob
|
30
30
|
attr_accessor :warmup_value, :warmup_duration, :warmup_loop_count
|
31
31
|
|
32
|
+
def add_warmup_attrs(value, duration, loop_count)
|
33
|
+
self.warmup_value = value
|
34
|
+
self.warmup_duration = duration
|
35
|
+
self.warmup_loop_count = loop_count
|
36
|
+
end
|
37
|
+
|
32
38
|
end
|
33
39
|
|
34
40
|
# BenchmarkDriver::Runner looks for this class
|
35
41
|
JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC])
|
36
42
|
|
37
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/BlockLength, Layout/LineLength, Layout/SpaceInsideBlockBraces, Style/BlockDelimiters
|
38
|
-
|
39
43
|
# This method is dynamically called by `BenchmarkDriver::JobRunner.run`
|
40
44
|
# @param [Array<BenchmarkDriver::Default::Job>] jobs
|
41
45
|
def run(jobs)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
@output.report(values: { metric => value }, duration: duration, loop_count: loop_count)
|
54
|
-
end
|
55
|
-
|
56
|
-
warmup_loop_count = loop_count
|
57
|
-
|
58
|
-
loop_count = (loop_count.to_f * @config.run_duration / duration).floor
|
59
|
-
Job.new(**job.to_h.merge(loop_count: loop_count))
|
60
|
-
.tap {|j| j.warmup_value = value }
|
61
|
-
.tap {|j| j.warmup_duration = duration }
|
62
|
-
.tap {|j| j.warmup_loop_count = warmup_loop_count }
|
63
|
-
end
|
64
|
-
end
|
65
|
-
.compact
|
66
|
-
end
|
46
|
+
jobs = run_all_jobs_warmup(jobs)
|
47
|
+
run_all_jobs_benchmarks(jobs)
|
48
|
+
end
|
49
|
+
|
50
|
+
def run_all_jobs_warmup(jobs)
|
51
|
+
return jobs if jobs.all?(&:loop_count)
|
52
|
+
@output.with_warmup do
|
53
|
+
jobs.map! {|job|
|
54
|
+
# skip warmup if loop_count is set
|
55
|
+
job.loop_count ? job : output_warmup_and_config_job(job)
|
56
|
+
}
|
67
57
|
end
|
58
|
+
end
|
68
59
|
|
60
|
+
def run_all_jobs_benchmarks(jobs)
|
69
61
|
@output.with_benchmark do
|
70
62
|
jobs.each do |job|
|
71
63
|
@output.with_job(name: job.name) do
|
72
64
|
job.runnable_contexts(@contexts).each do |context|
|
73
|
-
|
74
|
-
result =
|
75
|
-
if job.loop_count&.positive?
|
76
|
-
loop_count = job.loop_count
|
77
|
-
BenchmarkDriver::Repeater.with_repeat(**repeat_params) do
|
78
|
-
run_benchmark(job, context: context)
|
79
|
-
end
|
80
|
-
else
|
81
|
-
loop_count = job.warmup_loop_count
|
82
|
-
repeater_value = [job.warmup_value, job.warmup_duration]
|
83
|
-
BenchmarkDriver::Repeater::RepeatResult.new(
|
84
|
-
value: repeater_value, all_values: [repeater_value]
|
85
|
-
)
|
86
|
-
end
|
87
|
-
value, duration = result.value
|
88
|
-
@output.with_context(name: context.name, executable: context.executable, gems: context.gems, prelude: context.prelude) do
|
89
|
-
@output.report(
|
90
|
-
values: { metric => value },
|
91
|
-
all_values: { metric => result.all_values },
|
92
|
-
duration: duration,
|
93
|
-
loop_count: loop_count,
|
94
|
-
)
|
95
|
-
end
|
65
|
+
run_and_report_job(job, context)
|
96
66
|
end
|
97
67
|
end
|
98
68
|
end
|
99
69
|
end
|
100
70
|
end
|
101
71
|
|
102
|
-
|
72
|
+
def output_warmup_and_config_job(job)
|
73
|
+
@output.with_job(name: job.name) do
|
74
|
+
context = job.runnable_contexts(@contexts).first
|
75
|
+
value, duration, warmup_loop_count = run_and_report_warmup_job(job, context)
|
76
|
+
loop_count = (warmup_loop_count.to_f * @config.run_duration / duration).floor
|
77
|
+
Job.new(**job.to_h.merge(loop_count: loop_count))
|
78
|
+
.tap {|j| j.add_warmup_attrs(value, duration, warmup_loop_count) }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def run_and_report_warmup_job(job, context)
|
83
|
+
duration, loop_count = run_warmup(job, context: context)
|
84
|
+
value, duration = value_duration(duration: duration, loop_count: loop_count)
|
85
|
+
output_with_context(context) do
|
86
|
+
@output.report(
|
87
|
+
values: {metric => value}, duration: duration, loop_count: loop_count
|
88
|
+
)
|
89
|
+
end
|
90
|
+
[value, duration, loop_count]
|
91
|
+
end
|
92
|
+
|
93
|
+
def run_and_report_job(job, context)
|
94
|
+
result, loop_count = run_job_with_repeater(job, context)
|
95
|
+
value, duration = result.value
|
96
|
+
output_with_context(context) do
|
97
|
+
@output.report(
|
98
|
+
values: { metric => value },
|
99
|
+
all_values: { metric => result.all_values },
|
100
|
+
duration: duration,
|
101
|
+
loop_count: loop_count,
|
102
|
+
)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def output_with_context(context, &block)
|
107
|
+
@output.with_context(
|
108
|
+
name: context.name,
|
109
|
+
executable: context.executable,
|
110
|
+
gems: context.gems,
|
111
|
+
prelude: context.prelude,
|
112
|
+
&block
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
def run_job_with_repeater(job, context)
|
117
|
+
repeat_params = { config: @config, larger_better: true, rest_on_average: :average }
|
118
|
+
if job.loop_count&.positive?
|
119
|
+
run_job_with_own_loop_count(job, context, repeat_params)
|
120
|
+
else
|
121
|
+
run_job_with_warmup_loop_count(job, context, repeat_params)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def run_job_with_own_loop_count(job, context, repeat_params)
|
126
|
+
loop_count = job.loop_count
|
127
|
+
result = BenchmarkDriver::Repeater.with_repeat(**repeat_params) {
|
128
|
+
run_benchmark(job, context: context)
|
129
|
+
}
|
130
|
+
[result, loop_count]
|
131
|
+
end
|
132
|
+
|
133
|
+
def run_job_with_warmup_loop_count(job, context, repeat_params)
|
134
|
+
loop_count = job.warmup_loop_count
|
135
|
+
repeater_value = [job.warmup_value, job.warmup_duration]
|
136
|
+
result = BenchmarkDriver::Repeater::RepeatResult.new(
|
137
|
+
value: repeater_value, all_values: [repeater_value]
|
138
|
+
)
|
139
|
+
[result, loop_count]
|
140
|
+
end
|
103
141
|
|
104
142
|
def run_warmup(job, context:)
|
105
143
|
start = Time.now
|
data/lib/d_heap.rb
CHANGED
@@ -10,24 +10,80 @@ require "d_heap/version"
|
|
10
10
|
# the nodes have _d_ children instead of 2. This allows for "decrease priority"
|
11
11
|
# operations to be performed more quickly with the tradeoff of slower delete
|
12
12
|
# minimum. Additionally, _d_-ary heaps can have better memory cache behavior than
|
13
|
-
# binary heaps, allowing them to
|
13
|
+
# binary heaps, allowing them to pop more quickly in practice despite slower
|
14
14
|
# worst-case time complexity.
|
15
15
|
#
|
16
|
+
# Although _d_ can be configured when creating the heap, it's usually best to
|
17
|
+
# keep the default value of 4, because d=4 gives the smallest coefficient for
|
18
|
+
# <tt>(d + 1) log n / log d</tt> result. As always, use benchmarks for your
|
19
|
+
# particular use-case.
|
20
|
+
#
|
21
|
+
# @example Basic push, peek, and pop
|
22
|
+
# # create some example objects to place in our heap
|
23
|
+
# Task = Struct.new(:id, :time) do
|
24
|
+
# def to_f; time.to_f end
|
25
|
+
# end
|
26
|
+
# t1 = Task.new(1, Time.now + 5*60)
|
27
|
+
# t2 = Task.new(2, Time.now + 50)
|
28
|
+
# t3 = Task.new(3, Time.now + 60)
|
29
|
+
# t4 = Task.new(4, Time.now + 5)
|
30
|
+
#
|
31
|
+
# # create the heap
|
32
|
+
# require "d_heap"
|
33
|
+
# heap = DHeap.new
|
34
|
+
#
|
35
|
+
# # push with an explicit score (which might be extrinsic to the value)
|
36
|
+
# heap.push t1, t1.to_f
|
37
|
+
#
|
38
|
+
# # the score will be implicitly cast with Float, so any object with #to_f
|
39
|
+
# heap.push t2, t2
|
40
|
+
#
|
41
|
+
# # if the object has an intrinsic score via #to_f, "<<" is the simplest API
|
42
|
+
# heap << t3 << t4
|
43
|
+
#
|
44
|
+
# # pop returns the lowest scored item, and removes it from the heap
|
45
|
+
# heap.pop # => #<struct Task id=4, time=2021-01-17 17:02:22.5574 -0500>
|
46
|
+
# heap.pop # => #<struct Task id=2, time=2021-01-17 17:03:07.5574 -0500>
|
47
|
+
#
|
48
|
+
# # peek returns the lowest scored item, without removing it from the heap
|
49
|
+
# heap.peek # => #<struct Task id=3, time=2021-01-17 17:03:17.5574 -0500>
|
50
|
+
# heap.pop # => #<struct Task id=3, time=2021-01-17 17:03:17.5574 -0500>
|
51
|
+
#
|
52
|
+
# # pop_lte handles the common "h.pop if h.peek_score < max" pattern
|
53
|
+
# heap.pop_lte(Time.now + 65) # => nil
|
54
|
+
#
|
55
|
+
# # the heap size can be inspected with size and empty?
|
56
|
+
# heap.empty? # => false
|
57
|
+
# heap.size # => 1
|
58
|
+
# heap.pop # => #<struct Task id=1, time=2021-01-17 17:07:17.5574 -0500>
|
59
|
+
# heap.empty? # => true
|
60
|
+
# heap.size # => 0
|
61
|
+
#
|
62
|
+
# # popping from an empty heap returns nil
|
63
|
+
# heap.pop # => nil
|
64
|
+
#
|
16
65
|
class DHeap
|
17
|
-
alias deq
|
18
|
-
alias
|
19
|
-
alias
|
20
|
-
alias
|
21
|
-
|
22
|
-
|
23
|
-
alias
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
66
|
+
alias deq pop
|
67
|
+
alias shift pop
|
68
|
+
alias next pop
|
69
|
+
alias pop_all_lt pop_all_below
|
70
|
+
alias pop_below pop_lt
|
71
|
+
|
72
|
+
alias enq push
|
73
|
+
|
74
|
+
alias first peek
|
75
|
+
|
76
|
+
alias length size
|
77
|
+
alias count size
|
78
|
+
|
79
|
+
# Initialize a _d_-ary min-heap.
|
80
|
+
#
|
81
|
+
# @param d [Integer] Number of children for each parent node.
|
82
|
+
# Higher values generally speed up push but slow down pop.
|
83
|
+
# If all pushes are popped, the default is probably best.
|
84
|
+
# @param capacity [Integer] initial capacity of the heap.
|
85
|
+
def initialize(d: DEFAULT_D, capacity: DEFAULT_CAPA) # rubocop:disable Naming/MethodParameterName
|
86
|
+
__init_without_kw__(d, capacity)
|
31
87
|
end
|
32
88
|
|
33
89
|
# Consumes the heap by popping each minumum value until it is empty.
|
@@ -35,13 +91,20 @@ class DHeap
|
|
35
91
|
# If you want to iterate over the heap without consuming it, you will need to
|
36
92
|
# first call +#dup+
|
37
93
|
#
|
94
|
+
# @param with_score [Boolean] if scores shoul also be yielded
|
95
|
+
#
|
38
96
|
# @yieldparam value [Object] each value that would be popped
|
97
|
+
# @yieldparam score [Numeric] each value's score, if +with_scores+ is true
|
39
98
|
#
|
40
99
|
# @return [Enumerator] if no block is given
|
41
100
|
# @return [nil] if a block is given
|
42
|
-
def each_pop
|
43
|
-
return to_enum(__method__) unless block_given?
|
44
|
-
|
101
|
+
def each_pop(with_scores: false)
|
102
|
+
return to_enum(__method__, with_scores: with_scores) unless block_given?
|
103
|
+
if with_scores
|
104
|
+
yield(*pop_with_score) until empty?
|
105
|
+
else
|
106
|
+
yield pop until empty?
|
107
|
+
end
|
45
108
|
nil
|
46
109
|
end
|
47
110
|
|
@@ -101,14 +101,28 @@ module DHeap::Benchmarks
|
|
101
101
|
end
|
102
102
|
|
103
103
|
# a very simple pure ruby binary heap
|
104
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
105
104
|
class RbHeap < ExamplePriorityQueue
|
106
105
|
|
107
106
|
def <<(value)
|
108
107
|
raise ArgumentError unless value
|
109
108
|
@a.push(value)
|
110
|
-
|
111
|
-
|
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])
|
112
126
|
while 0 < index # rubocop:disable Style/NumericPredicate
|
113
127
|
parent_index = (index - 1) / 2
|
114
128
|
parent_value = @a[parent_index]
|
@@ -117,43 +131,28 @@ module DHeap::Benchmarks
|
|
117
131
|
index = parent_index
|
118
132
|
end
|
119
133
|
@a[index] = value
|
120
|
-
# dbg "__push__(%p)" % [value]
|
121
134
|
# check_heap!(index)
|
122
135
|
end
|
123
136
|
|
124
|
-
def
|
125
|
-
return if @a.empty?
|
126
|
-
popped = @a.first
|
127
|
-
value = @a.pop
|
128
|
-
last_index = @a.size - 1
|
137
|
+
def sift_down(index, last_index = @a.size - 1, value = @a[index])
|
129
138
|
last_parent = (last_index - 1) / 2
|
130
|
-
return popped unless 0 <= last_index
|
131
|
-
|
132
|
-
# sift down from 0
|
133
|
-
index = 0
|
134
|
-
child_index = 1
|
135
139
|
while index <= last_parent
|
136
|
-
child_value =
|
137
|
-
# select min child
|
138
|
-
if child_index < last_index
|
139
|
-
other_child_index = child_index + 1
|
140
|
-
other_child_value = @a[other_child_index]
|
141
|
-
if other_child_value < child_value
|
142
|
-
child_value = other_child_value
|
143
|
-
child_index = other_child_index
|
144
|
-
end
|
145
|
-
end
|
140
|
+
child_index, child_value = select_min_child(index, last_index)
|
146
141
|
break if value <= child_value
|
147
142
|
@a[index] = child_value
|
148
143
|
index = child_index
|
149
144
|
child_index = index * 2 + 1
|
150
145
|
end
|
151
146
|
@a[index] = value
|
152
|
-
|
153
|
-
popped
|
154
147
|
end
|
155
148
|
|
156
|
-
|
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
|
157
156
|
|
158
157
|
def check_heap!(idx)
|
159
158
|
check_heap_up!(idx)
|
@@ -186,7 +185,6 @@ module DHeap::Benchmarks
|
|
186
185
|
end
|
187
186
|
|
188
187
|
end
|
189
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
190
188
|
|
191
189
|
# minor adjustments to the "priority_queue_cxx" gem, to match the API
|
192
190
|
class CppSTL
|
@@ -201,6 +199,10 @@ module DHeap::Benchmarks
|
|
201
199
|
@q = FastContainers::PriorityQueue.new(:min)
|
202
200
|
end
|
203
201
|
|
202
|
+
def empty?
|
203
|
+
@q.empty?
|
204
|
+
end
|
205
|
+
|
204
206
|
def pop
|
205
207
|
@q.pop
|
206
208
|
rescue RuntimeError
|
@@ -39,30 +39,6 @@ module DHeap::Benchmarks
|
|
39
39
|
matcher :perform_at_least do |expected|
|
40
40
|
supports_block_expectations
|
41
41
|
|
42
|
-
def __debug__(name, caller_binding)
|
43
|
-
lvars = __debug_lvars__(caller_binding)
|
44
|
-
ivars = __debug_ivars__(caller_binding)
|
45
|
-
puts "%s, locals => %p, ivars => %p" % [name, lvars, ivars]
|
46
|
-
end
|
47
|
-
|
48
|
-
def __debug_lvars__(caller_binding)
|
49
|
-
caller_binding.local_variables.map {|lvar|
|
50
|
-
next if %i[type unit].include?(lvar)
|
51
|
-
next if (val = caller_binding.local_variable_get(lvar)).nil?
|
52
|
-
[lvar, val]
|
53
|
-
}.compact.to_h
|
54
|
-
end
|
55
|
-
|
56
|
-
def __debug_ivars__(caller_binding)
|
57
|
-
instance_variables.map {|ivar|
|
58
|
-
next if %i[@name @actual @expected_as_array @matcher_execution_context
|
59
|
-
@chained_method_clauses @block_arg]
|
60
|
-
.include?(ivar)
|
61
|
-
next if (val = instance_variable_get(ivar)).nil?
|
62
|
-
[ivar, val]
|
63
|
-
}.compact.to_h
|
64
|
-
end
|
65
|
-
|
66
42
|
%i[
|
67
43
|
is_at_least
|
68
44
|
running_at_most
|
@@ -70,7 +46,6 @@ module DHeap::Benchmarks
|
|
70
46
|
warmup_at_most
|
71
47
|
].each do |type|
|
72
48
|
chain type do |number|
|
73
|
-
# __debug__ "%s(%p)" % [type, number], binding
|
74
49
|
reason, value = ___number_reason_and_value___
|
75
50
|
if reason || value
|
76
51
|
raise "Need to handle unit-less number first: %s(%p)" % [reason, value]
|
@@ -88,22 +63,15 @@ module DHeap::Benchmarks
|
|
88
63
|
milliseconds
|
89
64
|
].each do |unit|
|
90
65
|
chain unit do
|
91
|
-
# __debug__ unit, binding
|
92
66
|
reason, value = ___number_reason_and_value___
|
93
67
|
raise "No number was specified" unless reason && value
|
94
|
-
|
95
|
-
when :running_at_most; apply_max_run unit
|
96
|
-
when :running_at_least; apply_min_run unit
|
97
|
-
when :warmup_at_most; apply_warmup unit
|
98
|
-
else raise "%s is incompatible with %s(%p)" % [unit, reason, value]
|
99
|
-
end
|
68
|
+
apply_number_to_reason(reason, value, unit)
|
100
69
|
@number_for = @number_val = nil
|
101
70
|
end
|
102
71
|
end
|
103
72
|
|
104
73
|
# TODO: let IPS set time to run instead of iterations to run
|
105
74
|
chain :ips do
|
106
|
-
# __debug__ "ips", binding
|
107
75
|
reason, value = ___number_reason_and_value___
|
108
76
|
raise "'ips' unit is only for assertions" unless reason == :is_at_least
|
109
77
|
raise "Already asserting %s ips" % [@expect_ips] if @expect_ips
|
@@ -115,7 +83,6 @@ module DHeap::Benchmarks
|
|
115
83
|
|
116
84
|
# need to use method because "chain" can't take a block
|
117
85
|
def times_faster_than(&other)
|
118
|
-
# __debug__ "times_faster_than"
|
119
86
|
reason, value = ___number_reason_and_value___
|
120
87
|
raise "'times_faster_than' is only for assertions" unless reason == :is_at_least
|
121
88
|
raise "Already asserting %sx comparison" % [@expect_cmp] if @expect_cmp
|
@@ -174,7 +141,6 @@ module DHeap::Benchmarks
|
|
174
141
|
chain :__convert_expected_to_ivars__ do
|
175
142
|
@number_val ||= expected
|
176
143
|
@number_for ||= :is_at_least if @number_val
|
177
|
-
# __debug__ "__convert_expected_to_ivars__", binding
|
178
144
|
expected = nil
|
179
145
|
end
|
180
146
|
private :__convert_expected_to_ivars__
|
@@ -184,30 +150,43 @@ module DHeap::Benchmarks
|
|
184
150
|
[@number_for, @number_val]
|
185
151
|
end
|
186
152
|
|
187
|
-
def
|
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)
|
188
173
|
case unit
|
189
|
-
when :seconds;
|
190
|
-
when :
|
191
|
-
when :times; @min_iter = Integer(@number_val)
|
192
|
-
else raise "Invalid unit %s for %s(%p)" % [unit, @number_for, @number_val]
|
174
|
+
when :seconds; @min_time = value
|
175
|
+
when :times; @min_iter = value
|
193
176
|
end
|
194
177
|
end
|
195
178
|
|
196
|
-
def apply_max_run(unit)
|
179
|
+
def apply_max_run(value, unit)
|
197
180
|
case unit
|
198
|
-
when :seconds;
|
199
|
-
when :
|
200
|
-
when :times; @max_iter = Integer(@number_val)
|
201
|
-
else raise "Invalid unit %s for %s(%p)" % [unit, @number_for, @number_val]
|
181
|
+
when :seconds; @max_time = value
|
182
|
+
when :times; @max_iter = value
|
202
183
|
end
|
203
184
|
end
|
204
185
|
|
205
|
-
def apply_warmup(unit)
|
186
|
+
def apply_warmup(value, unit)
|
206
187
|
case unit
|
207
|
-
when :seconds;
|
208
|
-
when :
|
209
|
-
when :times; @warmup_iter = Integer(@number_val)
|
210
|
-
else raise "Invalid unit %s for %s(%p)" % [unit, @number_for, @number_val]
|
188
|
+
when :seconds; @warmup_time = value
|
189
|
+
when :times; @warmup_iter = value
|
211
190
|
end
|
212
191
|
end
|
213
192
|
|
@@ -224,7 +203,6 @@ module DHeap::Benchmarks
|
|
224
203
|
|
225
204
|
def run_measurements
|
226
205
|
puts header if loud?
|
227
|
-
# __debug__ "run_measurements", binding
|
228
206
|
warmup
|
229
207
|
take_measurements
|
230
208
|
end
|