d_heap 0.4.0 → 0.5.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/CHANGELOG.md +19 -2
- data/Gemfile +4 -0
- data/Gemfile.lock +7 -1
- data/README.md +131 -107
- data/benchmarks/perf.rb +29 -0
- data/benchmarks/push_n.yml +6 -3
- data/benchmarks/push_n_pop_n.yml +4 -0
- data/benchmarks/push_pop.yml +6 -3
- data/benchmarks/stackprof.rb +31 -0
- data/docs/benchmarks-2.txt +63 -40
- data/docs/benchmarks-mem.txt +39 -0
- data/docs/benchmarks.txt +337 -265
- data/ext/d_heap/d_heap.c +202 -304
- data/ext/d_heap/d_heap.h +10 -4
- data/ext/d_heap/extconf.rb +8 -1
- data/lib/d_heap.rb +24 -0
- data/lib/d_heap/benchmarks.rb +2 -1
- data/lib/d_heap/benchmarks/benchmarker.rb +3 -0
- data/lib/d_heap/benchmarks/implementations.rb +86 -32
- data/lib/d_heap/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3dd1049e0a8041a328da4ed65622c2f0589475bc386a0eb6f20e466c79587bc5
|
4
|
+
data.tar.gz: ec44970feaa5ce6aef37f511e71e55342ec93b7e1a0b2a3d40f249afa4e9ac25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6b6e192dbe5980b2b79728e4b4bf413151b3e193733d1435119482edb977f0a2edd692fd156614fdfbc86f4fa1dc6ac9f7907ca640c21ca23050d07b9a1caa6
|
7
|
+
data.tar.gz: 27a987139a1fd14f73c16459f72be2bf1059dadbd212e250a28b3691dff2372e708cdf740155e2d2aa21de78cc789816a9d83f80f42d3c396510cd6fce6e6bf2
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
## Current/Unreleased
|
2
2
|
|
3
|
+
## Release v0.5.0 (2021-01-17)
|
4
|
+
|
5
|
+
* 🔥 **Breaking**: reversed order of `#push` arguments to `value, score`.
|
6
|
+
* ✨ Added `#insert(score, value)` to replace earlier version of `#push`.
|
7
|
+
* ✨ Added `#each_pop` enumerator.
|
8
|
+
* ✨ Added aliases for `deq`, `enq`, `first`, `pop_below`, `length`, and
|
9
|
+
`count`, to mimic other classes in ruby's stdlib.
|
10
|
+
* ⚡️♻️ More performance improvements:
|
11
|
+
* Created an `ENTRY` struct and store both the score and the value pointer in
|
12
|
+
the same `ENTRY *entries` array.
|
13
|
+
* Reduced unnecessary allocations or copies in both sift loops. A similar
|
14
|
+
refactoring also sped up the pure ruby benchmark implementation.
|
15
|
+
* Compiling with `-O3`.
|
16
|
+
* 📝 Updated (and in some cases, fixed) yardoc
|
17
|
+
* ♻️ Moved aliases and less performance sensitive code into ruby.
|
18
|
+
* ♻️ DRY up push/insert methods
|
19
|
+
|
3
20
|
## Release v0.4.0 (2021-01-12)
|
4
21
|
|
5
22
|
* ⚡️ Big performance improvements, by using C `long double *cscores` array
|
@@ -12,11 +29,11 @@
|
|
12
29
|
|
13
30
|
## Release v0.3.0 (2020-12-29)
|
14
31
|
|
32
|
+
* 🔥 **Breaking**: Removed class methods that operated directly on an array.
|
33
|
+
They weren't compatible with the performance improvements.
|
15
34
|
* ⚡️ Big performance improvements, by converting to a `T_DATA` struct.
|
16
35
|
* ♻️ Major refactoring/rewriting of dheap.c
|
17
36
|
* ✅ Added benchmark specs
|
18
|
-
* 🔥 Removed class methods that operated directly on an array. They weren't
|
19
|
-
compatible with the performance improvements.
|
20
37
|
|
21
38
|
## Release v0.2.2 (2020-12-27)
|
22
39
|
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
d_heap (0.
|
4
|
+
d_heap (0.5.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -14,6 +14,8 @@ GEM
|
|
14
14
|
parallel (1.19.2)
|
15
15
|
parser (2.7.2.0)
|
16
16
|
ast (~> 2.4.1)
|
17
|
+
perf (0.1.2)
|
18
|
+
priority_queue_cxx (0.3.4)
|
17
19
|
pry (0.13.1)
|
18
20
|
coderay (~> 1.1)
|
19
21
|
method_source (~> 1.0)
|
@@ -49,6 +51,7 @@ GEM
|
|
49
51
|
parser (>= 2.7.1.5)
|
50
52
|
ruby-prof (1.4.2)
|
51
53
|
ruby-progressbar (1.10.1)
|
54
|
+
stackprof (0.2.16)
|
52
55
|
unicode-display_width (1.7.0)
|
53
56
|
|
54
57
|
PLATFORMS
|
@@ -57,12 +60,15 @@ PLATFORMS
|
|
57
60
|
DEPENDENCIES
|
58
61
|
benchmark_driver
|
59
62
|
d_heap!
|
63
|
+
perf
|
64
|
+
priority_queue_cxx
|
60
65
|
pry
|
61
66
|
rake (~> 13.0)
|
62
67
|
rake-compiler
|
63
68
|
rspec (~> 3.10)
|
64
69
|
rubocop (~> 1.0)
|
65
70
|
ruby-prof
|
71
|
+
stackprof
|
66
72
|
|
67
73
|
BUNDLED WITH
|
68
74
|
2.2.3
|
data/README.md
CHANGED
@@ -4,20 +4,20 @@ A fast [_d_-ary heap][d-ary heap] [priority queue] implementation for ruby,
|
|
4
4
|
implemented as a C extension.
|
5
5
|
|
6
6
|
With a regular queue, you expect "FIFO" behavior: first in, first out. With a
|
7
|
-
stack you expect "LIFO": last in first out.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
stack you expect "LIFO": last in first out. A priority queue has a score for
|
8
|
+
each element and elements are popped in order by score. Priority queues are
|
9
|
+
often used in algorithms for e.g. [scheduling] of timers or bandwidth
|
10
|
+
management, for [Huffman coding], and various graph search algorithms such as
|
11
|
+
[Dijkstra's algorithm], [A* search], or [Prim's algorithm].
|
12
12
|
|
13
13
|
The _d_-ary heap data structure is a generalization of the [binary heap], in
|
14
|
-
which the nodes have _d_ children instead of 2. This allows for "
|
15
|
-
priority" operations to be performed more quickly with the tradeoff of
|
16
|
-
delete minimum. Additionally, _d_-ary heaps can have better memory cache
|
14
|
+
which the nodes have _d_ children instead of 2. This allows for "insert" and
|
15
|
+
"decrease priority" operations to be performed more quickly with the tradeoff of
|
16
|
+
slower delete minimum. Additionally, _d_-ary heaps can have better memory cache
|
17
17
|
behavior than binary heaps, allowing them to run more quickly in practice
|
18
18
|
despite slower worst-case time complexity. In the worst case, a _d_-ary heap
|
19
|
-
requires only `O(log n / log d)` to push, with the tradeoff that pop
|
20
|
-
n / log d)`.
|
19
|
+
requires only `O(log n / log d)` operations to push, with the tradeoff that pop
|
20
|
+
requires `O(d log n / log d)`.
|
21
21
|
|
22
22
|
Although you should probably just use the default _d_ value of `4` (see the
|
23
23
|
analysis below), it's always advisable to benchmark your specific use-case.
|
@@ -33,53 +33,58 @@ analysis below), it's always advisable to benchmark your specific use-case.
|
|
33
33
|
|
34
34
|
## Usage
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
* `heap
|
36
|
+
Quick reference:
|
37
|
+
|
38
|
+
* `heap << object` adds a value, with `Float(object)` as its score.
|
39
|
+
* `heap.push(object, score)` adds a value with an extrinsic score.
|
39
40
|
* `heap.pop` removes and returns the value with the minimum score.
|
40
41
|
* `heap.pop_lte(score)` pops if the minimum score is `<=` the provided score.
|
41
42
|
* `heap.peek` to view the minimum value without popping it.
|
43
|
+
* `heap.clear` to remove all items from the heap.
|
44
|
+
* `heap.empty?` returns true if the heap is empty.
|
45
|
+
* `heap.size` returns the number of items in the heap.
|
42
46
|
|
43
|
-
The
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
long long`. _This is architecture dependant but on an IA-64 system this is 64
|
49
|
-
bits, which gives a range of -18,446,744,073,709,551,615 to
|
50
|
-
+18446744073709551615._
|
51
|
-
|
52
|
-
_Comparing arbitary objects via_ `a <=> b` _was the original design and may
|
53
|
-
be added back in a future version,_ if (and only if) _it can be done without
|
54
|
-
impacting the speed of numeric comparisons._
|
47
|
+
The basic API is `#push(object, score)` and `pop`. If your values behave as
|
48
|
+
their own score, then you can push with `#<<`. If the score changes while the
|
49
|
+
object is still in the heap, it will not be re-evaluated again. The score must
|
50
|
+
either be `Integer` or `Float` or convertable to a `Float` via `Float(score)`
|
51
|
+
(i.e. it should implement `#to_f`).
|
55
52
|
|
56
53
|
```ruby
|
57
54
|
require "d_heap"
|
58
55
|
|
59
|
-
Task = Struct.new(:id)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
heap.
|
72
|
-
heap.
|
73
|
-
|
74
|
-
|
56
|
+
Task = Struct.new(:id, :time) do
|
57
|
+
def to_f; time.to_f end
|
58
|
+
end
|
59
|
+
t1 = Task.new(1, Time.now + 5*60)
|
60
|
+
t2 = Task.new(2, Time.now + 50)
|
61
|
+
t3 = Task.new(3, Time.now + 60)
|
62
|
+
t4 = Task.new(4, Time.now + 5)
|
63
|
+
|
64
|
+
# if the object returns its own score via #to_f, "<<" is the simplest API
|
65
|
+
heap << t1 << t2
|
66
|
+
|
67
|
+
# or push with an explicit score
|
68
|
+
heap.push t3, t4.to_f
|
69
|
+
heap.push t4, t4 # score can be implicitly cast with Float
|
70
|
+
|
71
|
+
# peek and pop
|
72
|
+
heap.pop # => #<struct Task id=4, time=2021-01-17 17:02:22.5574 -0500>
|
73
|
+
heap.pop # => #<struct Task id=2, time=2021-01-17 17:03:07.5574 -0500>
|
74
|
+
heap.peek # => #<struct Task id=3, time=2021-01-17 17:03:17.5574 -0500>
|
75
|
+
heap.pop # => #<struct Task id=3, time=2021-01-17 17:03:17.5574 -0500>
|
76
|
+
heap.pop # => #<struct Task id=1, time=2021-01-17 17:07:17.5574 -0500>
|
75
77
|
heap.empty? # => true
|
76
78
|
heap.pop # => nil
|
77
79
|
```
|
78
80
|
|
79
|
-
|
80
|
-
`
|
81
|
-
|
82
|
-
|
81
|
+
Constraining scores to numeric values gives more than 50% speedup under some
|
82
|
+
benchmarks! _n.b._ `Integer` _scores must have an absolute value that fits
|
83
|
+
into_ `unsigned long long`. _This is architecture dependant but on an IA-64
|
84
|
+
system this is 64 bits, which gives a range of -18,446,744,073,709,551,615 to
|
85
|
+
+18446744073709551615. Comparing arbitary objects via_ `a <=> b` _was the
|
86
|
+
original design and may be added back in a future version,_ if (and only if) _it
|
87
|
+
can be done without impacting the speed of numeric comparisons._
|
83
88
|
|
84
89
|
```ruby
|
85
90
|
heap.clear
|
@@ -142,9 +147,10 @@ Or install it yourself as:
|
|
142
147
|
## Motivation
|
143
148
|
|
144
149
|
One naive approach to a priority queue is to maintain an array in sorted order.
|
145
|
-
This can be very simply implemented
|
146
|
-
This can be very fast—`Array#pop` is `O(1)`—but the worst-case
|
147
|
-
`O(n)` because it may need to `memcpy` a significant portion of
|
150
|
+
This can be very simply implemented in ruby with `Array#bseach_index` +
|
151
|
+
`Array#insert`. This can be very fast—`Array#pop` is `O(1)`—but the worst-case
|
152
|
+
for insert is `O(n)` because it may need to `memcpy` a significant portion of
|
153
|
+
the array.
|
148
154
|
|
149
155
|
The standard way to implement a priority queue is with a binary heap. Although
|
150
156
|
this increases the time for `pop`, it converts the amortized time per push + pop
|
@@ -152,7 +158,7 @@ from `O(n)` to `O(d log n / log d)`.
|
|
152
158
|
|
153
159
|
However, I was surprised to find that—at least for some benchmarks—my pure ruby
|
154
160
|
heap implementation was much slower than inserting into and popping from a fully
|
155
|
-
sorted array. The
|
161
|
+
sorted array. The reasons for this surprising result: Although it is `O(n)`,
|
156
162
|
`memcpy` has a _very_ small constant factor, and calling `<=>` from ruby code
|
157
163
|
has relatively _much_ larger constant factors. If your queue contains only a
|
158
164
|
few thousand items, the overhead of those extra calls to `<=>` is _far_ more
|
@@ -162,10 +168,9 @@ sorted array.
|
|
162
168
|
|
163
169
|
Moving the sift-up and sift-down code into C helps some. But much more helpful
|
164
170
|
is optimizing the comparison of numeric scores, so `a <=> b` never needs to be
|
165
|
-
called. I'm hopeful that MJIT will eventually obsolete this C-extension.
|
166
|
-
|
167
|
-
|
168
|
-
the high overhead of `<=>`.
|
171
|
+
called. I'm hopeful that MJIT will eventually obsolete this C-extension. This
|
172
|
+
can be hotspot code, and a basic ruby implementation could perform well if `<=>`
|
173
|
+
had much lower overhead.
|
169
174
|
|
170
175
|
## Analysis
|
171
176
|
|
@@ -217,7 +222,7 @@ as an array which only stores values.
|
|
217
222
|
|
218
223
|
_See `bin/benchmarks` and `docs/benchmarks.txt`, as well as `bin/profile` and
|
219
224
|
`docs/profile.txt` for more details or updated results. These benchmarks were
|
220
|
-
measured with v0.
|
225
|
+
measured with v0.5.0 and ruby 2.7.2 without MJIT enabled._
|
221
226
|
|
222
227
|
These benchmarks use very simple implementations for a pure-ruby heap and an
|
223
228
|
array that is kept sorted using `Array#bsearch_index` and `Array#insert`. For
|
@@ -248,21 +253,24 @@ relatively small. The pure ruby binary heap is 2x or more slower than bsearch +
|
|
248
253
|
insert for common common push/pop scenario.
|
249
254
|
|
250
255
|
== push N (N=5) ==========================================================
|
251
|
-
push N (c_dheap):
|
252
|
-
push N (
|
253
|
-
push N (
|
256
|
+
push N (c_dheap): 1969700.7 i/s
|
257
|
+
push N (c++ stl): 1049738.1 i/s - 1.88x slower
|
258
|
+
push N (rb_heap): 928435.2 i/s - 2.12x slower
|
259
|
+
push N (bsearch): 921060.0 i/s - 2.14x slower
|
254
260
|
|
255
261
|
== push N then pop N (N=5) ===============================================
|
256
|
-
push N + pop N (c_dheap):
|
257
|
-
push N + pop N (
|
258
|
-
push N + pop N (
|
259
|
-
push N + pop N (
|
262
|
+
push N + pop N (c_dheap): 1375805.0 i/s
|
263
|
+
push N + pop N (c++ stl): 1134997.5 i/s - 1.21x slower
|
264
|
+
push N + pop N (findmin): 862913.1 i/s - 1.59x slower
|
265
|
+
push N + pop N (bsearch): 762887.1 i/s - 1.80x slower
|
266
|
+
push N + pop N (rb_heap): 506890.4 i/s - 2.71x slower
|
260
267
|
|
261
268
|
== Push/pop with pre-filled queue of size=N (N=5) ========================
|
262
|
-
push + pop (c_dheap):
|
263
|
-
push + pop (
|
264
|
-
push + pop (
|
265
|
-
push + pop (
|
269
|
+
push + pop (c_dheap): 9044435.5 i/s
|
270
|
+
push + pop (c++ stl): 7534583.4 i/s - 1.20x slower
|
271
|
+
push + pop (findmin): 5026155.1 i/s - 1.80x slower
|
272
|
+
push + pop (bsearch): 4300260.0 i/s - 2.10x slower
|
273
|
+
push + pop (rb_heap): 2299499.7 i/s - 3.93x slower
|
266
274
|
|
267
275
|
By N=21, `DHeap` has pulled significantly ahead of bsearch + insert for all
|
268
276
|
scenarios, but the pure ruby heap is still slower than every other
|
@@ -270,52 +278,68 @@ implementation—even resorting the array after every `#push`—in any scenario
|
|
270
278
|
uses `#pop`.
|
271
279
|
|
272
280
|
== push N (N=21) =========================================================
|
273
|
-
push N (c_dheap):
|
274
|
-
push N (
|
275
|
-
push N (
|
281
|
+
push N (c_dheap): 464231.4 i/s
|
282
|
+
push N (c++ stl): 305546.7 i/s - 1.52x slower
|
283
|
+
push N (rb_heap): 202803.7 i/s - 2.29x slower
|
284
|
+
push N (bsearch): 168678.7 i/s - 2.75x slower
|
276
285
|
|
277
286
|
== push N then pop N (N=21) ==============================================
|
278
|
-
push N + pop N (c_dheap):
|
279
|
-
push N + pop N (
|
280
|
-
push N + pop N (
|
281
|
-
push N + pop N (
|
287
|
+
push N + pop N (c_dheap): 298350.3 i/s
|
288
|
+
push N + pop N (c++ stl): 252227.1 i/s - 1.18x slower
|
289
|
+
push N + pop N (findmin): 161998.7 i/s - 1.84x slower
|
290
|
+
push N + pop N (bsearch): 143432.3 i/s - 2.08x slower
|
291
|
+
push N + pop N (rb_heap): 79622.1 i/s - 3.75x slower
|
282
292
|
|
283
293
|
== Push/pop with pre-filled queue of size=N (N=21) =======================
|
284
|
-
push + pop (c_dheap):
|
285
|
-
push + pop (
|
286
|
-
push + pop (
|
287
|
-
push + pop (
|
288
|
-
|
289
|
-
|
290
|
-
of
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
push + pop (rb_heap)
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
push
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
294
|
+
push + pop (c_dheap): 8855093.4 i/s
|
295
|
+
push + pop (c++ stl): 7223079.5 i/s - 1.23x slower
|
296
|
+
push + pop (findmin): 4542913.7 i/s - 1.95x slower
|
297
|
+
push + pop (bsearch): 3461802.4 i/s - 2.56x slower
|
298
|
+
push + pop (rb_heap): 1845488.7 i/s - 4.80x slower
|
299
|
+
|
300
|
+
At higher values of N, a heaps logarithmic growth leads to only a little
|
301
|
+
slowdown of `#push`, while insert's linear growth causes it to run noticably
|
302
|
+
slower and slower. But because `#pop` is `O(1)` for a sorted array and `O(d log
|
303
|
+
n / log d)` for a heap, scenarios involving both `#push` and `#pop` remain
|
304
|
+
relatively close, and bsearch + insert still runs faster than a pure ruby heap,
|
305
|
+
even up to queues with 10k items. But as queue size increases beyond than that,
|
306
|
+
the linear time compexity to keep a sorted array dominates.
|
307
|
+
|
308
|
+
== push + pop (rb_heap)
|
309
|
+
queue size = 10000: 736618.2 i/s
|
310
|
+
queue size = 25000: 670186.8 i/s - 1.10x slower
|
311
|
+
queue size = 50000: 618156.7 i/s - 1.19x slower
|
312
|
+
queue size = 100000: 579250.7 i/s - 1.27x slower
|
313
|
+
queue size = 250000: 572795.0 i/s - 1.29x slower
|
314
|
+
queue size = 500000: 543648.3 i/s - 1.35x slower
|
315
|
+
queue size = 1000000: 513523.4 i/s - 1.43x slower
|
316
|
+
queue size = 2500000: 460848.9 i/s - 1.60x slower
|
317
|
+
queue size = 5000000: 445234.5 i/s - 1.65x slower
|
318
|
+
queue size = 10000000: 423119.0 i/s - 1.74x slower
|
319
|
+
|
320
|
+
== push + pop (bsearch)
|
321
|
+
queue size = 10000: 786334.2 i/s
|
322
|
+
queue size = 25000: 364963.8 i/s - 2.15x slower
|
323
|
+
queue size = 50000: 200520.6 i/s - 3.92x slower
|
324
|
+
queue size = 100000: 88607.0 i/s - 8.87x slower
|
325
|
+
queue size = 250000: 34530.5 i/s - 22.77x slower
|
326
|
+
queue size = 500000: 17965.4 i/s - 43.77x slower
|
327
|
+
queue size = 1000000: 5638.7 i/s - 139.45x slower
|
328
|
+
queue size = 2500000: 1302.0 i/s - 603.93x slower
|
329
|
+
queue size = 5000000: 592.0 i/s - 1328.25x slower
|
330
|
+
queue size = 10000000: 288.8 i/s - 2722.66x slower
|
331
|
+
|
332
|
+
== push + pop (c_dheap)
|
333
|
+
queue size = 10000: 7311366.6 i/s
|
334
|
+
queue size = 50000: 6737824.5 i/s - 1.09x slower
|
335
|
+
queue size = 25000: 6407340.6 i/s - 1.14x slower
|
336
|
+
queue size = 100000: 6254396.3 i/s - 1.17x slower
|
337
|
+
queue size = 250000: 5917684.5 i/s - 1.24x slower
|
338
|
+
queue size = 500000: 5126307.6 i/s - 1.43x slower
|
339
|
+
queue size = 1000000: 4403494.1 i/s - 1.66x slower
|
340
|
+
queue size = 2500000: 3304088.2 i/s - 2.21x slower
|
341
|
+
queue size = 5000000: 2664897.7 i/s - 2.74x slower
|
342
|
+
queue size = 10000000: 2137927.6 i/s - 3.42x slower
|
319
343
|
|
320
344
|
## Profiling
|
321
345
|
|
data/benchmarks/perf.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "perf"
|
5
|
+
|
6
|
+
system("#{RbConfig.ruby} bin/rake compile", err: :out, exception: true)
|
7
|
+
require "d_heap/benchmarks"
|
8
|
+
include DHeap::Benchmarks # rubocop:disable Style/MixinUsage
|
9
|
+
fill_random_vals
|
10
|
+
|
11
|
+
n = ENV.fetch("BENCH_N", 5_000_000).to_i
|
12
|
+
# interval = ENV.fetch("PROF_INTERVAL", 100).to_i # measured in μs
|
13
|
+
|
14
|
+
i = 0
|
15
|
+
|
16
|
+
q = initq RbHeap
|
17
|
+
n.times { q << n }
|
18
|
+
q.clear
|
19
|
+
|
20
|
+
Perf.record do
|
21
|
+
while i < n
|
22
|
+
q << random_val
|
23
|
+
i += 1
|
24
|
+
end
|
25
|
+
while 0 < i # rubocop:disable Style/NumericPredicate
|
26
|
+
q.pop
|
27
|
+
i -= 1
|
28
|
+
end
|
29
|
+
end
|
data/benchmarks/push_n.yml
CHANGED
@@ -15,14 +15,17 @@ benchmark:
|
|
15
15
|
q << random_val
|
16
16
|
i += 1
|
17
17
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
name: "push N (findmin)"
|
19
|
+
prelude: "q = initq FindMin"
|
20
|
+
- script: *script
|
21
21
|
name: "push N (bsearch)"
|
22
22
|
prelude: "q = initq BSearch"
|
23
23
|
- script: *script
|
24
24
|
name: "push N (rb_heap)"
|
25
25
|
prelude: "q = initq RbHeap"
|
26
|
+
- script: *script
|
27
|
+
name: "push N (c++ stl)"
|
28
|
+
prelude: "q = initq CppSTL"
|
26
29
|
- script: *script
|
27
30
|
name: "push N (c_dheap)"
|
28
31
|
prelude: "q = initq DHeap"
|