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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 413c0a93e2c3cbdbb86ee433df47a310034d453e441a150d8317dc055b4a9a90
4
- data.tar.gz: 4bf67447021da03b07da7f44bcf97a66f13fa42f6f67bcfe9a49d0866c8b8167
3
+ metadata.gz: 3dd1049e0a8041a328da4ed65622c2f0589475bc386a0eb6f20e466c79587bc5
4
+ data.tar.gz: ec44970feaa5ce6aef37f511e71e55342ec93b7e1a0b2a3d40f249afa4e9ac25
5
5
  SHA512:
6
- metadata.gz: 5e55bf53c1062686e0863fb9c3b09f3c2b8b936b0cf83985092e1e906b0b24f40e02a42eada048dcea732a60ec4e3695bb861943b424a8cd3152b227abad8a4e
7
- data.tar.gz: e021616d6dcdcec943fec11783f2147a2d175aa3a0caf668c2339e05795e32475cdf7be90201c38d7108c74087e22140a9da9d3f211d7f0335e3c173ae83893b
6
+ metadata.gz: a6b6e192dbe5980b2b79728e4b4bf413151b3e193733d1435119482edb977f0a2edd692fd156614fdfbc86f4fa1dc6ac9f7907ca640c21ca23050d07b9a1caa6
7
+ data.tar.gz: 27a987139a1fd14f73c16459f72be2bf1059dadbd212e250a28b3691dff2372e708cdf740155e2d2aa21de78cc789816a9d83f80f42d3c396510cd6fce6e6bf2
@@ -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
@@ -10,3 +10,7 @@ gem "rake", "~> 13.0"
10
10
  gem "rake-compiler"
11
11
  gem "rspec", "~> 3.10"
12
12
  gem "rubocop", "~> 1.0"
13
+
14
+ gem "perf"
15
+ gem "priority_queue_cxx"
16
+ gem "stackprof"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- d_heap (0.4.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. With a priority queue, you push
8
- elements along with a score and the lowest scored element is the first to be
9
- popped. Priority queues are often used in algorithms for e.g. [scheduling] of
10
- timers or bandwidth management, [Huffman coding], and various graph search
11
- algorithms such as [Dijkstra's algorithm], [A* search], or [Prim's algorithm].
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 "decrease
15
- priority" operations to be performed more quickly with the tradeoff of slower
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 is `O(d log
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
- The basic API is:
37
- * `heap << object` adds a value as its own score.
38
- * `heap.push(score, value)` adds a value with an extrinsic score.
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 score must be `Integer` or `Float` or convertable to a `Float` via
44
- `Float(score)` (i.e. it should implement `#to_f`). Constraining scores to
45
- numeric values gives a 40+% speedup under some benchmarks!
46
-
47
- _n.b._ `Integer` _scores must have an absolute value that fits into_ `unsigned
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) # for demonstration
60
-
61
- heap = DHeap.new # defaults to a 4-heap
62
-
63
- # storing [score, value] tuples
64
- heap.push Time.now + 5*60, Task.new(1)
65
- heap.push Time.now + 30, Task.new(2)
66
- heap.push Time.now + 60, Task.new(3)
67
- heap.push Time.now + 5, Task.new(4)
68
-
69
- # peeking and popping (using last to get the task and ignore the time)
70
- heap.pop # => Task[4]
71
- heap.pop # => Task[2]
72
- heap.peek # => Task[3], but don't pop it from the heap
73
- heap.pop # => Task[3]
74
- heap.pop # => Task[1]
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
- If your values behave as their own score, by being convertible via
80
- `Float(value)`, then you can use `#<<` for implicit scoring. The score should
81
- not change for as long as the value remains in the heap, since it will not be
82
- re-evaluated after being pushed.
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 using `Array#bseach_index` + `Array#insert`.
146
- This can be very fast—`Array#pop` is `O(1)`—but the worst-case for insert is
147
- `O(n)` because it may need to `memcpy` a significant portion of the array.
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 reason for this surprising result: Although it is `O(n)`,
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. JRuby
166
- or TruffleRuby may already run the pure ruby version at high speed. This can be
167
- hotspot code, and the basic ruby implementation should perform well if not for
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.4.0 and ruby 2.7.2 without MJIT enabled._
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): 1701338.1 i/s
252
- push N (rb_heap): 971614.1 i/s - 1.75x slower
253
- push N (bsearch): 946363.7 i/s - 1.80x slower
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): 1087944.8 i/s
257
- push N + pop N (findmin): 841708.1 i/s - 1.29x slower
258
- push N + pop N (bsearch): 773252.7 i/s - 1.41x slower
259
- push N + pop N (rb_heap): 471852.9 i/s - 2.31x slower
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): 5525418.8 i/s
263
- push + pop (findmin): 5003904.8 i/s - 1.10x slower
264
- push + pop (bsearch): 4320581.8 i/s - 1.28x slower
265
- push + pop (rb_heap): 2207042.0 i/s - 2.50x slower
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): 408307.0 i/s
274
- push N (rb_heap): 212275.2 i/s - 1.92x slower
275
- push N (bsearch): 169583.2 i/s - 2.41x slower
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): 199435.5 i/s
279
- push N + pop N (findmin): 162024.5 i/s - 1.23x slower
280
- push N + pop N (bsearch): 146284.3 i/s - 1.36x slower
281
- push N + pop N (rb_heap): 72289.0 i/s - 2.76x slower
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): 4836860.0 i/s
285
- push + pop (findmin): 4467453.9 i/s - 1.08x slower
286
- push + pop (bsearch): 3345458.4 i/s - 1.45x slower
287
- push + pop (rb_heap): 1560476.3 i/s - 3.10x slower
288
-
289
- At higher values of N, `DHeap`'s logarithmic growth leads to little slowdown
290
- of `DHeap#push`, while insert's linear growth causes it to run slower and
291
- slower. But because `#pop` is O(1) for a sorted array and O(d log n / log d)
292
- for a _d_-heap, scenarios involving `#pop` remain relatively close even as N
293
- increases to 5k:
294
-
295
- == Push/pop with pre-filled queue of size=N (N=5461) ==============
296
- push + pop (c_dheap): 2718225.1 i/s
297
- push + pop (bsearch): 1793546.4 i/s - 1.52x slower
298
- push + pop (rb_heap): 707139.9 i/s - 3.84x slower
299
- push + pop (findmin): 122316.0 i/s - 22.22x slower
300
-
301
- Somewhat surprisingly, bsearch + insert still runs faster than a pure ruby heap
302
- for the repeated push/pop scenario, all the way up to N as high as 87k:
303
-
304
- == push N (N=87381) ======================================================
305
- push N (c_dheap): 92.8 i/s
306
- push N (rb_heap): 43.5 i/s - 2.13x slower
307
- push N (bsearch): 2.9 i/s - 31.70x slower
308
-
309
- == push N then pop N (N=87381) ===========================================
310
- push N + pop N (c_dheap): 22.6 i/s
311
- push N + pop N (rb_heap): 5.5 i/s - 4.08x slower
312
- push N + pop N (bsearch): 2.9 i/s - 7.90x slower
313
-
314
- == Push/pop with pre-filled queue of size=N (N=87381) ====================
315
- push + pop (c_dheap): 1815277.3 i/s
316
- push + pop (bsearch): 762343.2 i/s - 2.38x slower
317
- push + pop (rb_heap): 535913.6 i/s - 3.39x slower
318
- push + pop (findmin): 2262.8 i/s - 802.24x slower
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
 
@@ -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
@@ -15,14 +15,17 @@ benchmark:
15
15
  q << random_val
16
16
  i += 1
17
17
  end
18
- # name: "push N (findmin)"
19
- # prelude: "q = initq FindMin"
20
- # - script: *script
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"