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 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"