d_heap 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|