d_heap 0.2.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/.github/workflows/main.yml +26 -0
- data/.rubocop.yml +199 -0
- data/CHANGELOG.md +59 -0
- data/Gemfile +10 -2
- data/Gemfile.lock +42 -5
- data/README.md +392 -109
- data/Rakefile +8 -2
- data/benchmarks/perf.rb +29 -0
- data/benchmarks/push_n.yml +31 -0
- data/benchmarks/push_n_pop_n.yml +35 -0
- data/benchmarks/push_pop.yml +27 -0
- data/benchmarks/stackprof.rb +31 -0
- data/bin/bench_n +7 -0
- data/bin/benchmark-driver +29 -0
- data/bin/benchmarks +10 -0
- data/bin/console +1 -0
- data/bin/profile +10 -0
- data/bin/rubocop +29 -0
- data/d_heap.gemspec +11 -6
- data/docs/benchmarks-2.txt +75 -0
- data/docs/benchmarks-mem.txt +39 -0
- data/docs/benchmarks.txt +515 -0
- data/docs/profile.txt +392 -0
- data/ext/d_heap/d_heap.c +555 -225
- data/ext/d_heap/d_heap.h +24 -48
- data/ext/d_heap/extconf.rb +20 -0
- data/lib/benchmark_driver/runner/ips_zero_fail.rb +120 -0
- data/lib/d_heap.rb +40 -2
- data/lib/d_heap/benchmarks.rb +112 -0
- data/lib/d_heap/benchmarks/benchmarker.rb +116 -0
- data/lib/d_heap/benchmarks/implementations.rb +222 -0
- data/lib/d_heap/benchmarks/profiler.rb +71 -0
- data/lib/d_heap/benchmarks/rspec_matchers.rb +374 -0
- data/lib/d_heap/version.rb +4 -1
- metadata +54 -3
data/docs/profile.txt
ADDED
@@ -0,0 +1,392 @@
|
|
1
|
+
Profiling run at 2021-01-10 21:34:56 -0500
|
2
|
+
ruby v2.7.2, DHeap v0.3.0
|
3
|
+
|
4
|
+
~~~~~~ filling @dheap_bm_random_vals with 1.0M ~~~~~~
|
5
|
+
########################################################################
|
6
|
+
# Profile w/ N=5 (i=1000000)
|
7
|
+
# (n.b. RubyProf & tracepoint can change relative performance.
|
8
|
+
# A sampling profiler can provide more accurate relative metrics.
|
9
|
+
########################################################################
|
10
|
+
|
11
|
+
Filling push and resort ---------------------------
|
12
|
+
Profiling push and resort ---------------------------
|
13
|
+
Measure Mode: wall_time
|
14
|
+
Thread ID: 1360
|
15
|
+
Fiber ID: 1340
|
16
|
+
Total: 2.639873
|
17
|
+
Sort by: self_time
|
18
|
+
|
19
|
+
%self total self wait child calls name location
|
20
|
+
31.49 2.640 0.831 0.000 1.808 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
21
|
+
25.69 1.219 0.678 0.000 0.541 1000000 DHeap::Benchmarks::Sorting#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:35
|
22
|
+
15.00 0.396 0.396 0.000 0.000 1000000 Array#sort!
|
23
|
+
12.38 0.462 0.327 0.000 0.136 1000000 DHeap::Benchmarks::Sorting#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:42
|
24
|
+
5.50 0.145 0.145 0.000 0.000 1000000 Array#push
|
25
|
+
5.13 0.136 0.136 0.000 0.000 1000000 Array#shift
|
26
|
+
4.81 0.127 0.127 0.000 0.000 1000000 Array#fetch
|
27
|
+
|
28
|
+
* recursively called methods
|
29
|
+
|
30
|
+
Columns are:
|
31
|
+
|
32
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
33
|
+
total - The time spent in this method and its children.
|
34
|
+
self - The time spent in this method.
|
35
|
+
wait - The amount of time this method waited for other threads.
|
36
|
+
child - The time spent in this method's children.
|
37
|
+
calls - The number of times this method was called.
|
38
|
+
name - The name of the method.
|
39
|
+
location - The location of the method.
|
40
|
+
|
41
|
+
The interpretation of method names is:
|
42
|
+
|
43
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
44
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
45
|
+
|
46
|
+
Filling bsearch + insert ---------------------------
|
47
|
+
Profiling bsearch + insert ---------------------------
|
48
|
+
Measure Mode: wall_time
|
49
|
+
Thread ID: 1360
|
50
|
+
Fiber ID: 1340
|
51
|
+
Total: 2.634233
|
52
|
+
Sort by: self_time
|
53
|
+
|
54
|
+
%self total self wait child calls name location
|
55
|
+
30.73 2.634 0.810 0.000 1.825 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
56
|
+
25.94 1.228 0.683 0.000 0.545 1000000 DHeap::Benchmarks::BSearch#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:61
|
57
|
+
14.83 0.391 0.391 0.000 0.000 1000000 Array#bsearch_index
|
58
|
+
12.79 0.462 0.337 0.000 0.125 1000000 DHeap::Benchmarks::BSearch#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:70
|
59
|
+
5.85 0.154 0.154 0.000 0.000 1000000 Array#insert
|
60
|
+
5.10 0.134 0.134 0.000 0.000 1000000 Array#fetch
|
61
|
+
4.76 0.125 0.125 0.000 0.000 1000000 Array#pop
|
62
|
+
|
63
|
+
* recursively called methods
|
64
|
+
|
65
|
+
Columns are:
|
66
|
+
|
67
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
68
|
+
total - The time spent in this method and its children.
|
69
|
+
self - The time spent in this method.
|
70
|
+
wait - The amount of time this method waited for other threads.
|
71
|
+
child - The time spent in this method's children.
|
72
|
+
calls - The number of times this method was called.
|
73
|
+
name - The name of the method.
|
74
|
+
location - The location of the method.
|
75
|
+
|
76
|
+
The interpretation of method names is:
|
77
|
+
|
78
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
79
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
80
|
+
|
81
|
+
Filling ruby binary heap ---------------------------
|
82
|
+
Profiling ruby binary heap ---------------------------
|
83
|
+
Measure Mode: wall_time
|
84
|
+
Thread ID: 1360
|
85
|
+
Fiber ID: 1340
|
86
|
+
Total: 4.940103
|
87
|
+
Sort by: self_time
|
88
|
+
|
89
|
+
%self total self wait child calls name location
|
90
|
+
37.38 1.973 1.846 0.000 0.126 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:80
|
91
|
+
33.66 2.042 1.663 0.000 0.380 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:96
|
92
|
+
16.10 4.940 0.795 0.000 4.145 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
93
|
+
2.64 0.130 0.130 0.000 0.000 1000000 Array#pop
|
94
|
+
2.63 0.130 0.130 0.000 0.000 1000000 Array#first
|
95
|
+
2.62 0.130 0.130 0.000 0.000 1000000 Array#fetch
|
96
|
+
2.56 0.126 0.126 0.000 0.000 1000000 Array#push
|
97
|
+
2.43 0.120 0.120 0.000 0.000 1000000 Array#last
|
98
|
+
|
99
|
+
* recursively called methods
|
100
|
+
|
101
|
+
Columns are:
|
102
|
+
|
103
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
104
|
+
total - The time spent in this method and its children.
|
105
|
+
self - The time spent in this method.
|
106
|
+
wait - The amount of time this method waited for other threads.
|
107
|
+
child - The time spent in this method's children.
|
108
|
+
calls - The number of times this method was called.
|
109
|
+
name - The name of the method.
|
110
|
+
location - The location of the method.
|
111
|
+
|
112
|
+
The interpretation of method names is:
|
113
|
+
|
114
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
115
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
116
|
+
|
117
|
+
Filling quaternary DHeap ---------------------------
|
118
|
+
Profiling quaternary DHeap ---------------------------
|
119
|
+
Measure Mode: wall_time
|
120
|
+
Thread ID: 1360
|
121
|
+
Fiber ID: 1340
|
122
|
+
Total: 1.231464
|
123
|
+
Sort by: self_time
|
124
|
+
|
125
|
+
%self total self wait child calls name location
|
126
|
+
60.13 1.231 0.741 0.000 0.491 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
127
|
+
16.72 0.206 0.206 0.000 0.000 1000000 DHeap#<<
|
128
|
+
12.85 0.158 0.158 0.000 0.000 1000000 DHeap#pop
|
129
|
+
10.30 0.127 0.127 0.000 0.000 1000000 Array#fetch
|
130
|
+
|
131
|
+
* recursively called methods
|
132
|
+
|
133
|
+
Columns are:
|
134
|
+
|
135
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
136
|
+
total - The time spent in this method and its children.
|
137
|
+
self - The time spent in this method.
|
138
|
+
wait - The amount of time this method waited for other threads.
|
139
|
+
child - The time spent in this method's children.
|
140
|
+
calls - The number of times this method was called.
|
141
|
+
name - The name of the method.
|
142
|
+
location - The location of the method.
|
143
|
+
|
144
|
+
The interpretation of method names is:
|
145
|
+
|
146
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
147
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
148
|
+
|
149
|
+
########################################################################
|
150
|
+
# Profile w/ N=1365 (i=1000000)
|
151
|
+
# (n.b. RubyProf & tracepoint can change relative performance.
|
152
|
+
# A sampling profiler can provide more accurate relative metrics.
|
153
|
+
########################################################################
|
154
|
+
|
155
|
+
Filling push and resort ---------------------------
|
156
|
+
Profiling push and resort ---------------------------
|
157
|
+
Measure Mode: wall_time
|
158
|
+
Thread ID: 1360
|
159
|
+
Fiber ID: 1340
|
160
|
+
Total: 41.950612
|
161
|
+
Sort by: self_time
|
162
|
+
|
163
|
+
%self total self wait child calls name location
|
164
|
+
94.10 39.478 39.478 0.000 0.000 1000000 Array#sort!
|
165
|
+
2.11 41.951 0.884 0.000 41.066 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
166
|
+
1.68 40.328 0.707 0.000 39.621 1000000 DHeap::Benchmarks::Sorting#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:35
|
167
|
+
|
168
|
+
* recursively called methods
|
169
|
+
|
170
|
+
Columns are:
|
171
|
+
|
172
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
173
|
+
total - The time spent in this method and its children.
|
174
|
+
self - The time spent in this method.
|
175
|
+
wait - The amount of time this method waited for other threads.
|
176
|
+
child - The time spent in this method's children.
|
177
|
+
calls - The number of times this method was called.
|
178
|
+
name - The name of the method.
|
179
|
+
location - The location of the method.
|
180
|
+
|
181
|
+
The interpretation of method names is:
|
182
|
+
|
183
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
184
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
185
|
+
|
186
|
+
Filling bsearch + insert ---------------------------
|
187
|
+
Profiling bsearch + insert ---------------------------
|
188
|
+
Measure Mode: wall_time
|
189
|
+
Thread ID: 1360
|
190
|
+
Fiber ID: 1340
|
191
|
+
Total: 3.559064
|
192
|
+
Sort by: self_time
|
193
|
+
|
194
|
+
%self total self wait child calls name location
|
195
|
+
37.92 1.349 1.349 0.000 0.000 1000000 Array#bsearch_index
|
196
|
+
22.76 3.559 0.810 0.000 2.749 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
197
|
+
18.47 2.157 0.657 0.000 1.499 1000000 DHeap::Benchmarks::BSearch#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:61
|
198
|
+
9.45 0.462 0.336 0.000 0.125 1000000 DHeap::Benchmarks::BSearch#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:70
|
199
|
+
4.21 0.150 0.150 0.000 0.000 1000000 Array#insert
|
200
|
+
3.67 0.131 0.131 0.000 0.000 1000000 Array#fetch
|
201
|
+
3.53 0.125 0.125 0.000 0.000 1000000 Array#pop
|
202
|
+
|
203
|
+
* recursively called methods
|
204
|
+
|
205
|
+
Columns are:
|
206
|
+
|
207
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
208
|
+
total - The time spent in this method and its children.
|
209
|
+
self - The time spent in this method.
|
210
|
+
wait - The amount of time this method waited for other threads.
|
211
|
+
child - The time spent in this method's children.
|
212
|
+
calls - The number of times this method was called.
|
213
|
+
name - The name of the method.
|
214
|
+
location - The location of the method.
|
215
|
+
|
216
|
+
The interpretation of method names is:
|
217
|
+
|
218
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
219
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
220
|
+
|
221
|
+
Filling ruby binary heap ---------------------------
|
222
|
+
Profiling ruby binary heap ---------------------------
|
223
|
+
Measure Mode: wall_time
|
224
|
+
Thread ID: 1360
|
225
|
+
Fiber ID: 1340
|
226
|
+
Total: 11.581886
|
227
|
+
Sort by: self_time
|
228
|
+
|
229
|
+
%self total self wait child calls name location
|
230
|
+
52.04 6.160 6.027 0.000 0.132 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:80
|
231
|
+
35.10 4.453 4.065 0.000 0.388 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:96
|
232
|
+
7.21 11.582 0.835 0.000 10.747 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
233
|
+
1.16 0.134 0.134 0.000 0.000 1000000 Array#fetch
|
234
|
+
1.14 0.132 0.132 0.000 0.000 1000000 Array#push
|
235
|
+
1.14 0.132 0.132 0.000 0.000 1000000 Array#pop
|
236
|
+
1.13 0.131 0.131 0.000 0.000 1000000 Array#first
|
237
|
+
1.08 0.125 0.125 0.000 0.000 1000000 Array#last
|
238
|
+
|
239
|
+
* recursively called methods
|
240
|
+
|
241
|
+
Columns are:
|
242
|
+
|
243
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
244
|
+
total - The time spent in this method and its children.
|
245
|
+
self - The time spent in this method.
|
246
|
+
wait - The amount of time this method waited for other threads.
|
247
|
+
child - The time spent in this method's children.
|
248
|
+
calls - The number of times this method was called.
|
249
|
+
name - The name of the method.
|
250
|
+
location - The location of the method.
|
251
|
+
|
252
|
+
The interpretation of method names is:
|
253
|
+
|
254
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
255
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
256
|
+
|
257
|
+
Filling quaternary DHeap ---------------------------
|
258
|
+
Profiling quaternary DHeap ---------------------------
|
259
|
+
Measure Mode: wall_time
|
260
|
+
Thread ID: 1360
|
261
|
+
Fiber ID: 1340
|
262
|
+
Total: 1.431426
|
263
|
+
Sort by: self_time
|
264
|
+
|
265
|
+
%self total self wait child calls name location
|
266
|
+
50.61 1.431 0.724 0.000 0.707 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
267
|
+
21.61 0.309 0.309 0.000 0.000 1000000 DHeap#<<
|
268
|
+
19.18 0.275 0.275 0.000 0.000 1000000 DHeap#pop
|
269
|
+
8.59 0.123 0.123 0.000 0.000 1000000 Array#fetch
|
270
|
+
|
271
|
+
* recursively called methods
|
272
|
+
|
273
|
+
Columns are:
|
274
|
+
|
275
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
276
|
+
total - The time spent in this method and its children.
|
277
|
+
self - The time spent in this method.
|
278
|
+
wait - The amount of time this method waited for other threads.
|
279
|
+
child - The time spent in this method's children.
|
280
|
+
calls - The number of times this method was called.
|
281
|
+
name - The name of the method.
|
282
|
+
location - The location of the method.
|
283
|
+
|
284
|
+
The interpretation of method names is:
|
285
|
+
|
286
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
287
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
288
|
+
|
289
|
+
########################################################################
|
290
|
+
# Profile w/ N=87381 (i=1000000)
|
291
|
+
# (n.b. RubyProf & tracepoint can change relative performance.
|
292
|
+
# A sampling profiler can provide more accurate relative metrics.
|
293
|
+
########################################################################
|
294
|
+
|
295
|
+
Filling bsearch + insert ---------------------------
|
296
|
+
Profiling bsearch + insert ---------------------------
|
297
|
+
Measure Mode: wall_time
|
298
|
+
Thread ID: 1360
|
299
|
+
Fiber ID: 1340
|
300
|
+
Total: 5.894803
|
301
|
+
Sort by: self_time
|
302
|
+
|
303
|
+
%self total self wait child calls name location
|
304
|
+
34.53 2.035 2.035 0.000 0.000 1000000 Array#bsearch_index
|
305
|
+
30.22 1.782 1.782 0.000 0.000 1000000 Array#insert
|
306
|
+
13.74 5.895 0.810 0.000 5.085 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
307
|
+
11.52 4.496 0.679 0.000 3.817 1000000 DHeap::Benchmarks::BSearch#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:61
|
308
|
+
5.70 0.459 0.336 0.000 0.124 1000000 DHeap::Benchmarks::BSearch#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:70
|
309
|
+
2.20 0.130 0.130 0.000 0.000 1000000 Array#fetch
|
310
|
+
2.10 0.124 0.124 0.000 0.000 1000000 Array#pop
|
311
|
+
|
312
|
+
* recursively called methods
|
313
|
+
|
314
|
+
Columns are:
|
315
|
+
|
316
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
317
|
+
total - The time spent in this method and its children.
|
318
|
+
self - The time spent in this method.
|
319
|
+
wait - The amount of time this method waited for other threads.
|
320
|
+
child - The time spent in this method's children.
|
321
|
+
calls - The number of times this method was called.
|
322
|
+
name - The name of the method.
|
323
|
+
location - The location of the method.
|
324
|
+
|
325
|
+
The interpretation of method names is:
|
326
|
+
|
327
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
328
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
329
|
+
|
330
|
+
Filling ruby binary heap ---------------------------
|
331
|
+
Profiling ruby binary heap ---------------------------
|
332
|
+
Measure Mode: wall_time
|
333
|
+
Thread ID: 1360
|
334
|
+
Fiber ID: 1340
|
335
|
+
Total: 16.514635
|
336
|
+
Sort by: self_time
|
337
|
+
|
338
|
+
%self total self wait child calls name location
|
339
|
+
45.67 7.926 7.542 0.000 0.384 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:96
|
340
|
+
45.43 7.630 7.502 0.000 0.128 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:80
|
341
|
+
5.00 16.515 0.826 0.000 15.688 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
342
|
+
|
343
|
+
* recursively called methods
|
344
|
+
|
345
|
+
Columns are:
|
346
|
+
|
347
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
348
|
+
total - The time spent in this method and its children.
|
349
|
+
self - The time spent in this method.
|
350
|
+
wait - The amount of time this method waited for other threads.
|
351
|
+
child - The time spent in this method's children.
|
352
|
+
calls - The number of times this method was called.
|
353
|
+
name - The name of the method.
|
354
|
+
location - The location of the method.
|
355
|
+
|
356
|
+
The interpretation of method names is:
|
357
|
+
|
358
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
359
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
360
|
+
|
361
|
+
Filling quaternary DHeap ---------------------------
|
362
|
+
Profiling quaternary DHeap ---------------------------
|
363
|
+
Measure Mode: wall_time
|
364
|
+
Thread ID: 1360
|
365
|
+
Fiber ID: 1340
|
366
|
+
Total: 1.622729
|
367
|
+
Sort by: self_time
|
368
|
+
|
369
|
+
%self total self wait child calls name location
|
370
|
+
44.66 1.623 0.725 0.000 0.898 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
371
|
+
27.42 0.445 0.445 0.000 0.000 1000000 DHeap#pop
|
372
|
+
20.41 0.331 0.331 0.000 0.000 1000000 DHeap#<<
|
373
|
+
7.51 0.122 0.122 0.000 0.000 1000000 Array#fetch
|
374
|
+
|
375
|
+
* recursively called methods
|
376
|
+
|
377
|
+
Columns are:
|
378
|
+
|
379
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
380
|
+
total - The time spent in this method and its children.
|
381
|
+
self - The time spent in this method.
|
382
|
+
wait - The amount of time this method waited for other threads.
|
383
|
+
child - The time spent in this method's children.
|
384
|
+
calls - The number of times this method was called.
|
385
|
+
name - The name of the method.
|
386
|
+
location - The location of the method.
|
387
|
+
|
388
|
+
The interpretation of method names is:
|
389
|
+
|
390
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
391
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
392
|
+
|
data/ext/d_heap/d_heap.c
CHANGED
@@ -1,327 +1,657 @@
|
|
1
|
+
#include <float.h>
|
1
2
|
#include "d_heap.h"
|
2
3
|
|
3
|
-
ID
|
4
|
-
ID
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
#define
|
14
|
-
|
15
|
-
|
16
|
-
#define
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
#define
|
24
|
-
#define
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
4
|
+
ID id_cmp; // <=>
|
5
|
+
ID id_abs; // abs
|
6
|
+
|
7
|
+
typedef struct dheap_struct {
|
8
|
+
int d;
|
9
|
+
long size;
|
10
|
+
long capa;
|
11
|
+
ENTRY *entries;
|
12
|
+
} dheap_t;
|
13
|
+
|
14
|
+
#define DHEAP_SCORE(heap, idx) ((heap)->entries[idx].score)
|
15
|
+
#define DHEAP_VALUE(heap, idx) ((heap)->entries[idx].value)
|
16
|
+
#define DHEAP_IDX_LAST(heap) (DHEAP_SIZE((heap)) - 1)
|
17
|
+
#define DHEAP_IDX_PARENT(heap, idx) (((idx) - 1) / (heap)->d)
|
18
|
+
#define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
|
19
|
+
#define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
|
20
|
+
|
21
|
+
#define DHEAP_SIZE(heap) ((heap)->size)
|
22
|
+
|
23
|
+
#define CMP_LT(a, b) ((a) < (b))
|
24
|
+
#define CMP_LTE(a, b) ((a) <= (b))
|
25
|
+
#define CMP_GT(a, b) ((a) > (b))
|
26
|
+
#define CMP_GTE(a, b) ((a) >= (b))
|
27
|
+
|
28
|
+
#if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
|
29
|
+
#error 'unsigned long long' should fit into 'long double' mantissa
|
30
|
+
#endif
|
31
|
+
|
32
|
+
// copied and modified from ruby's object.c
|
33
|
+
#define FIX2SCORE(x) (long double)FIX2LONG(x)
|
34
|
+
// We could translate a much wider range of values to long double by
|
35
|
+
// implementing a new `rb_big2ldbl(x)` function. But requires reaching into
|
36
|
+
// T_BIGNUM internals.
|
37
|
+
static inline long double
|
38
|
+
BIG2SCORE(VALUE x)
|
39
|
+
{
|
40
|
+
if (RBIGNUM_POSITIVE_P(x)) {
|
41
|
+
unsigned long long ull = rb_big2ull(x);
|
42
|
+
return (long double)ull;
|
43
|
+
} else {
|
44
|
+
unsigned long long ull;
|
45
|
+
long double ldbl;
|
46
|
+
x = rb_funcall(x, id_abs, 0);
|
47
|
+
ull = rb_big2ull(x);
|
48
|
+
ldbl = (long double) ull;
|
49
|
+
return -ldbl;
|
32
50
|
}
|
51
|
+
}
|
52
|
+
#define INT2SCORE(x) \
|
53
|
+
(FIXNUM_P(x) ? FIX2SCORE(x) : BIG2SCORE(x))
|
54
|
+
#define NUM2SCORE(x) \
|
55
|
+
(FIXNUM_P(x) ? FIX2SCORE(x) : \
|
56
|
+
RB_TYPE_P(x, T_BIGNUM) ? BIG2SCORE(x) : \
|
57
|
+
(Check_Type(x, T_FLOAT), (long double)RFLOAT_VALUE(x)))
|
58
|
+
static inline long double
|
59
|
+
RAT2SCORE(VALUE x)
|
60
|
+
{
|
61
|
+
VALUE num = rb_rational_num(x);
|
62
|
+
VALUE den = rb_rational_den(x);
|
63
|
+
return NUM2SCORE(num) / NUM2SCORE(den);
|
64
|
+
}
|
33
65
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
66
|
+
/*
|
67
|
+
* Convert both T_FIXNUM and T_FLOAT (and sometimes T_BIGNUM, T_RATIONAL,
|
68
|
+
* String, etc) to SCORE
|
69
|
+
* * with no loss of precision (where possible for Integer and Float),
|
70
|
+
* * raises an exception if
|
71
|
+
* * a positive integer is too large for unsigned long long (should be 64bit)
|
72
|
+
* * a negative integer is too small for signed long long (should be 64bit)
|
73
|
+
* * reduced to long double (should be 80 or 128 bit) if it is Rational
|
74
|
+
* * reduced to double precision if the value is convertable by Float(x)
|
75
|
+
*/
|
76
|
+
static inline long double
|
77
|
+
VAL2SCORE(VALUE score)
|
78
|
+
{
|
79
|
+
// assert that long double can hold 'unsigned long long':
|
80
|
+
// static_assert(sizeof(unsigned long long) * 8 <= LDBL_MANT_DIG);
|
81
|
+
// assert that long double can hold T_FLOAT
|
82
|
+
// static_assert(sizeof(double) <= sizeof(long double));
|
83
|
+
|
84
|
+
switch (TYPE(score)) {
|
85
|
+
case T_FIXNUM:
|
86
|
+
return FIX2SCORE(score);
|
87
|
+
case T_BIGNUM:
|
88
|
+
return BIG2SCORE(score);
|
89
|
+
case T_RATIONAL:
|
90
|
+
return RAT2SCORE(score);
|
91
|
+
default:
|
92
|
+
return (long double)(NUM2DBL(rb_Float(score)));
|
40
93
|
}
|
94
|
+
}
|
41
95
|
|
42
|
-
#
|
43
|
-
DHEAP_Check_d_size(d); \
|
44
|
-
Check_Type(heap_array, T_ARRAY); \
|
45
|
-
long last_index = DHEAP_LAST_IDX(heap_array); \
|
46
|
-
DHEAP_Check_Sift_Idx(sift_index, last_index); \
|
47
|
-
\
|
48
|
-
VALUE sift_value = DHEAP_VALUE(heap_array, sift_index); \
|
49
|
-
VALUE sift_score = DHEAP_SCORE(heap_array, sift_index);
|
96
|
+
#ifdef ORIG_SCORE_CMP_CODE
|
50
97
|
|
51
|
-
|
52
|
-
dheap_ary_sift_up(VALUE heap_array, int d, long sift_index) {
|
53
|
-
DHEAP_Check_Sift_Args(heap_array, d, sift_index);
|
54
|
-
struct cmp_opt_data cmp_opt = { 0, 0 };
|
55
|
-
// sift it up to where it belongs
|
56
|
-
for (long parent_index; 0 < sift_index; sift_index = parent_index) {
|
57
|
-
// puts(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
|
58
|
-
parent_index = IDX_PARENT(sift_index);
|
59
|
-
VALUE parent_score = DHEAP_SCORE(heap_array, parent_index);
|
98
|
+
#define VAL2SCORE(score) (score)
|
60
99
|
|
61
|
-
|
62
|
-
|
100
|
+
#define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
|
101
|
+
#define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
|
102
|
+
#define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
|
103
|
+
#define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
|
63
104
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
105
|
+
// from internal/compar.h
|
106
|
+
#define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
|
107
|
+
/*
|
108
|
+
* short-circuit evaluation for a few basic types.
|
109
|
+
*
|
110
|
+
* Only Integer, Float, and String are optimized,
|
111
|
+
* and only when both arguments are the same type.
|
112
|
+
*/
|
113
|
+
static inline int
|
114
|
+
optimized_cmp(SCORE a, SCORE b) {
|
115
|
+
if (a == b) // Fixnum equality and object equality
|
116
|
+
return 0;
|
117
|
+
if (FIXNUM_P(a) && FIXNUM_P(b))
|
118
|
+
return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
|
119
|
+
if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
|
120
|
+
{
|
121
|
+
double x, y;
|
122
|
+
x = RFLOAT_VALUE(a);
|
123
|
+
y = RFLOAT_VALUE(b);
|
124
|
+
if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
|
125
|
+
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
68
126
|
}
|
69
|
-
|
70
|
-
|
127
|
+
if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
|
128
|
+
return FIX2INT(rb_big_cmp(a, b));
|
129
|
+
if (STRING_P(a) && STRING_P(b))
|
130
|
+
return rb_str_cmp(a, b);
|
131
|
+
|
132
|
+
// give up on an optimized version and just call (a <=> b)
|
133
|
+
return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
|
71
134
|
}
|
72
135
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
136
|
+
#endif
|
137
|
+
|
138
|
+
#define DHEAP_Check_d_size(d) do { \
|
139
|
+
if (d < 2) { \
|
140
|
+
rb_raise(rb_eArgError, "DHeap d=%d is too small", d); \
|
141
|
+
} \
|
142
|
+
if (d > DHEAP_MAX_D) { \
|
143
|
+
rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
|
144
|
+
} \
|
145
|
+
} while (0)
|
146
|
+
|
147
|
+
#ifdef __D_HEAP_DEBUG
|
148
|
+
#define ASSERT_DHEAP_INDEX(heap, index) do { \
|
149
|
+
if (index < 0) { \
|
150
|
+
rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
|
151
|
+
} \
|
152
|
+
else if (DHEAP_IDX_LAST(heap) < index) { \
|
153
|
+
rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
|
154
|
+
} \
|
155
|
+
} while (0)
|
156
|
+
#else
|
157
|
+
#define ASSERT_DHEAP_INDEX(heap, index)
|
158
|
+
#endif
|
159
|
+
|
160
|
+
static void
|
161
|
+
dheap_compact(void *ptr)
|
162
|
+
{
|
163
|
+
dheap_t *heap = ptr;
|
164
|
+
for (long i = 0; i < heap->size; ++i) {
|
165
|
+
if (DHEAP_VALUE(heap, i))
|
166
|
+
dheap_gc_location(DHEAP_VALUE(heap, i));
|
167
|
+
}
|
168
|
+
}
|
100
169
|
|
101
|
-
|
102
|
-
|
170
|
+
static void
|
171
|
+
dheap_mark(void *ptr)
|
172
|
+
{
|
173
|
+
dheap_t *heap = ptr;
|
174
|
+
for (long i = 0; i < heap->size; ++i) {
|
175
|
+
if (DHEAP_VALUE(heap, i))
|
176
|
+
rb_gc_mark_movable(DHEAP_VALUE(heap,i));
|
177
|
+
}
|
178
|
+
}
|
103
179
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
180
|
+
static void
|
181
|
+
dheap_free(void *ptr)
|
182
|
+
{
|
183
|
+
dheap_t *heap = ptr;
|
184
|
+
heap->size = 0;
|
185
|
+
if (heap->entries) {
|
186
|
+
xfree(heap->entries);
|
187
|
+
heap->entries = NULL;
|
108
188
|
}
|
109
|
-
|
110
|
-
|
189
|
+
heap->capa = 0;
|
190
|
+
xfree(ptr);
|
111
191
|
}
|
112
192
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
193
|
+
static size_t
|
194
|
+
dheap_memsize(const void *ptr)
|
195
|
+
{
|
196
|
+
const dheap_t *heap = ptr;
|
197
|
+
size_t size = 0;
|
198
|
+
size += sizeof(*heap);
|
199
|
+
size += sizeof(ENTRY) * heap->capa;
|
200
|
+
return size;
|
201
|
+
}
|
202
|
+
|
203
|
+
static const rb_data_type_t dheap_data_type = {
|
204
|
+
"DHeap",
|
205
|
+
{
|
206
|
+
(void (*)(void*))dheap_mark,
|
207
|
+
(void (*)(void*))dheap_free,
|
208
|
+
(size_t (*)(const void *))dheap_memsize,
|
209
|
+
dheap_compact_callback(dheap_compact),
|
210
|
+
},
|
211
|
+
0, 0,
|
212
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
213
|
+
};
|
117
214
|
|
118
|
-
/*
|
119
|
-
* call-seq:
|
120
|
-
* DHeap.array_sift_up(heap_array, d, sift_index)
|
121
|
-
*
|
122
|
-
* Treats +heap_array+ as a +d+-ary heap and sifts up from +sift_index+ to restore
|
123
|
-
* the heap property.
|
124
|
-
*
|
125
|
-
* Time complexity: O(d log n / log d). If the average up shifted element sorts
|
126
|
-
* into the bottom layer (e.g. new timers), this can avg O(1).
|
127
|
-
*
|
128
|
-
*/
|
129
215
|
static VALUE
|
130
|
-
|
131
|
-
|
132
|
-
|
216
|
+
dheap_s_alloc(VALUE klass)
|
217
|
+
{
|
218
|
+
VALUE obj;
|
219
|
+
dheap_t *heap;
|
220
|
+
|
221
|
+
obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
|
222
|
+
heap->d = DHEAP_DEFAULT_D;
|
223
|
+
|
224
|
+
heap->size = 0;
|
225
|
+
heap->capa = 0;
|
226
|
+
heap->entries = NULL;
|
227
|
+
|
228
|
+
return obj;
|
229
|
+
}
|
230
|
+
|
231
|
+
static inline dheap_t *
|
232
|
+
get_dheap_struct(VALUE self)
|
233
|
+
{
|
234
|
+
dheap_t *heap;
|
235
|
+
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
236
|
+
return heap;
|
237
|
+
}
|
238
|
+
|
239
|
+
void
|
240
|
+
dheap_set_capa(dheap_t *heap, long new_capa)
|
241
|
+
{
|
242
|
+
// Do nothing if we already have the capacity or are resizing too small
|
243
|
+
if (new_capa <= heap->capa || new_capa <= heap->size) return;
|
244
|
+
|
245
|
+
// allocate
|
246
|
+
if (heap->entries) {
|
247
|
+
RB_REALLOC_N(heap->entries, ENTRY, new_capa);
|
248
|
+
} else {
|
249
|
+
heap->entries = RB_ZALLOC_N(ENTRY, new_capa);
|
250
|
+
}
|
251
|
+
heap->capa = new_capa;
|
252
|
+
}
|
253
|
+
|
254
|
+
static void
|
255
|
+
dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
|
256
|
+
{
|
257
|
+
long new_size = heap->size + incr_by;
|
258
|
+
|
259
|
+
// check for overflow of new_size
|
260
|
+
if (DHEAP_MAX_SIZE - incr_by < heap->size)
|
261
|
+
rb_raise(rb_eIndexError, "index %ld too big", new_size);
|
262
|
+
|
263
|
+
// if it existing capacity is too small
|
264
|
+
if (heap->capa < new_size) {
|
265
|
+
// double it...
|
266
|
+
long new_capa = new_size * 2;
|
267
|
+
if (DHEAP_CAPA_INCR_MAX < new_size)
|
268
|
+
new_size = new_size + DHEAP_CAPA_INCR_MAX;
|
269
|
+
// check for overflow of new_capa
|
270
|
+
if (DHEAP_MAX_SIZE / 2 < new_size) new_capa = DHEAP_MAX_SIZE;
|
271
|
+
// cap max incr_by
|
272
|
+
if (heap->capa + DHEAP_CAPA_INCR_MAX < new_capa)
|
273
|
+
new_capa = heap->capa + DHEAP_CAPA_INCR_MAX;
|
274
|
+
|
275
|
+
dheap_set_capa(heap, new_capa);
|
276
|
+
}
|
133
277
|
}
|
134
278
|
|
135
279
|
/*
|
136
|
-
*
|
137
|
-
*
|
138
|
-
*
|
139
|
-
* Treats +heap_array+ as a +d+-ary heap and sifts down from +sift_index+ to
|
140
|
-
* restore the heap property.
|
141
|
-
*
|
142
|
-
* Time complexity: O(d log n / log d). If the average down shifted element
|
143
|
-
* sorts into the bottom layer (e.g. canceled timers), this can avg O(1).
|
280
|
+
* @overload initialize(d = DHeap::DEFAULT_D)
|
281
|
+
* Initialize a _d_-ary min-heap.
|
144
282
|
*
|
283
|
+
* @param d [Integer] maximum number of children per parent
|
145
284
|
*/
|
146
|
-
static VALUE
|
147
|
-
dheap_sift_down_s(VALUE unused, VALUE heap_array, VALUE dval, VALUE idxval) {
|
148
|
-
DHEAP_Load_Sift_Vals(heap_array, dval, idxval);
|
149
|
-
return dheap_ary_sift_down(heap_array, d, sift_index);
|
150
|
-
}
|
151
|
-
|
152
285
|
static VALUE
|
153
286
|
dheap_initialize(int argc, VALUE *argv, VALUE self) {
|
287
|
+
dheap_t *heap;
|
288
|
+
int d;
|
289
|
+
|
154
290
|
rb_check_arity(argc, 0, 1);
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
}
|
291
|
+
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
292
|
+
|
293
|
+
d = argc ? NUM2INT(argv[0]) : DHEAP_DEFAULT_D;
|
159
294
|
DHEAP_Check_d_size(d);
|
160
|
-
|
161
|
-
|
295
|
+
heap->d = d;
|
296
|
+
|
297
|
+
dheap_set_capa(heap, DHEAP_DEFAULT_SIZE);
|
298
|
+
|
162
299
|
return self;
|
163
300
|
}
|
164
301
|
|
165
|
-
|
166
|
-
|
167
|
-
|
302
|
+
/* @!visibility private */
|
303
|
+
static VALUE
|
304
|
+
dheap_initialize_copy(VALUE copy, VALUE orig)
|
305
|
+
{
|
306
|
+
dheap_t *heap_copy;
|
307
|
+
dheap_t *heap_orig = get_dheap_struct(orig);
|
308
|
+
|
309
|
+
rb_check_frozen(copy);
|
310
|
+
TypedData_Get_Struct(copy, dheap_t, &dheap_data_type, heap_copy);
|
311
|
+
|
312
|
+
heap_copy->d = heap_orig->d;
|
313
|
+
|
314
|
+
dheap_set_capa(heap_copy, heap_orig->capa);
|
315
|
+
heap_copy->size = heap_orig->size;
|
316
|
+
if (heap_copy->size)
|
317
|
+
MEMCPY(heap_copy->entries, heap_orig->entries, ENTRY, heap_orig->size);
|
318
|
+
|
319
|
+
return copy;
|
320
|
+
}
|
321
|
+
|
322
|
+
VALUE
|
323
|
+
dheap_sift_up(dheap_t *heap, long index) {
|
324
|
+
ENTRY entry = heap->entries[index];
|
325
|
+
|
326
|
+
ASSERT_DHEAP_INDEX(heap, index);
|
327
|
+
|
328
|
+
// sift it up to where it belongs
|
329
|
+
for (long parent_index; 0 < index; index = parent_index) {
|
330
|
+
/* debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index)); */
|
331
|
+
parent_index = DHEAP_IDX_PARENT(heap, index);
|
332
|
+
|
333
|
+
// parent is smaller: heap is restored
|
334
|
+
if (CMP_LTE(DHEAP_SCORE(heap, parent_index), entry.score)) break;
|
335
|
+
|
336
|
+
// parent is larger: swap and continue sifting up
|
337
|
+
heap->entries[index] = heap->entries[parent_index];
|
338
|
+
}
|
339
|
+
heap->entries[index] = entry;
|
340
|
+
/* debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index)); */
|
341
|
+
return LONG2NUM(index);
|
342
|
+
}
|
343
|
+
|
344
|
+
/*
|
345
|
+
* this is a tiny bit more complicated than the binary heap version
|
346
|
+
*/
|
347
|
+
static inline long
|
348
|
+
dheap_find_min_child(dheap_t *heap, long parent, long last_index) {
|
349
|
+
long min_child = DHEAP_IDX_CHILD_0(heap, parent);
|
350
|
+
long last_sib = DHEAP_IDX_CHILD_D(heap, parent);
|
351
|
+
if (last_index < last_sib) last_sib = last_index;
|
352
|
+
|
353
|
+
for (long sibidx = min_child + 1; sibidx <= last_sib; ++sibidx) {
|
354
|
+
if (CMP_LT(DHEAP_SCORE(heap, sibidx),
|
355
|
+
DHEAP_SCORE(heap, min_child))) {
|
356
|
+
min_child = sibidx;
|
357
|
+
}
|
358
|
+
}
|
359
|
+
return min_child;
|
360
|
+
}
|
361
|
+
|
362
|
+
void
|
363
|
+
dheap_sift_down(dheap_t *heap, long index, long last_index) {
|
364
|
+
if (last_index < 1 || DHEAP_IDX_PARENT(heap, last_index) < index) {
|
365
|
+
// short-circuit: no chance for a child
|
366
|
+
return;
|
367
|
+
|
368
|
+
} else {
|
369
|
+
ENTRY entry = heap->entries[index];
|
370
|
+
|
371
|
+
long last_parent = DHEAP_IDX_PARENT(heap, last_index);
|
372
|
+
|
373
|
+
ASSERT_DHEAP_INDEX(heap, index);
|
374
|
+
|
375
|
+
// iteratively sift it down to where it belongs
|
376
|
+
while (index <= last_parent) {
|
377
|
+
// find min child
|
378
|
+
long min_child = dheap_find_min_child(heap, index, last_index);
|
379
|
+
|
380
|
+
// child is larger: heap is restored
|
381
|
+
if (CMP_LTE(entry.score, DHEAP_SCORE(heap, min_child))) break;
|
382
|
+
|
383
|
+
// child is smaller: swap and continue sifting down
|
384
|
+
heap->entries[index] = heap->entries[min_child];
|
385
|
+
index = min_child;
|
386
|
+
}
|
387
|
+
heap->entries[index] = entry;
|
388
|
+
// debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index));
|
389
|
+
}
|
390
|
+
}
|
391
|
+
|
392
|
+
/*
|
393
|
+
* @return [Integer] the number of elements in the heap
|
394
|
+
*/
|
395
|
+
static VALUE
|
396
|
+
dheap_size(VALUE self)
|
397
|
+
{
|
398
|
+
dheap_t *heap = get_dheap_struct(self);
|
399
|
+
long size = DHEAP_SIZE(heap);
|
168
400
|
return LONG2NUM(size);
|
169
401
|
}
|
170
402
|
|
171
|
-
|
172
|
-
|
173
|
-
|
403
|
+
/*
|
404
|
+
* @return [Boolean] if the heap is empty
|
405
|
+
*/
|
406
|
+
static VALUE
|
407
|
+
dheap_empty_p(VALUE self)
|
408
|
+
{
|
409
|
+
dheap_t *heap = get_dheap_struct(self);
|
410
|
+
long size = DHEAP_SIZE(heap);
|
174
411
|
return size == 0 ? Qtrue : Qfalse;
|
175
412
|
}
|
176
413
|
|
177
|
-
|
178
|
-
|
414
|
+
/*
|
415
|
+
* @return [Integer] the maximum number of children per parent
|
416
|
+
*/
|
179
417
|
static VALUE
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
return rb_call_super(0, NULL);
|
418
|
+
dheap_attr_d(VALUE self)
|
419
|
+
{
|
420
|
+
dheap_t *heap = get_dheap_struct(self);
|
421
|
+
return INT2FIX(heap->d);
|
185
422
|
}
|
186
423
|
|
424
|
+
/* @!visibility private */
|
187
425
|
static VALUE
|
188
|
-
|
426
|
+
dheap_init_clone(VALUE clone, VALUE orig, VALUE kwfreeze)
|
189
427
|
{
|
190
|
-
|
191
|
-
|
192
|
-
|
428
|
+
dheap_initialize_copy(clone, orig);
|
429
|
+
if (RTEST(kwfreeze) || (kwfreeze == Qnil && OBJ_FROZEN(orig))) {
|
430
|
+
rb_funcall(clone, rb_intern("freeze"), 0);
|
431
|
+
}
|
432
|
+
return clone;
|
433
|
+
}
|
434
|
+
|
435
|
+
static inline void
|
436
|
+
dheap_push_entry(dheap_t *heap, ENTRY *entry) {
|
437
|
+
dheap_ensure_room_for_push(heap, 1);
|
438
|
+
heap->entries[heap->size] = *entry;
|
439
|
+
++heap->size;
|
440
|
+
dheap_sift_up(heap, heap->size - 1);
|
441
|
+
}
|
442
|
+
|
443
|
+
/*
|
444
|
+
* Inserts a value into the heap, using a score to determine sort-order.
|
445
|
+
*
|
446
|
+
* Score comes first, as an analogy with the +Array#insert+ index.
|
447
|
+
*
|
448
|
+
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
449
|
+
*
|
450
|
+
* @param score [Integer,Float,#to_f] a score to compare against other scores.
|
451
|
+
* @param value [Object] an object that is associated with the score.
|
452
|
+
*
|
453
|
+
* @return [self]
|
454
|
+
*/
|
455
|
+
static VALUE
|
456
|
+
dheap_insert(VALUE self, VALUE score, VALUE value) {
|
457
|
+
ENTRY entry;
|
458
|
+
dheap_t *heap = get_dheap_struct(self);
|
459
|
+
rb_check_frozen(self);
|
460
|
+
|
461
|
+
entry.score = VAL2SCORE(score);
|
462
|
+
entry.value = value;
|
463
|
+
dheap_push_entry(heap, &entry);
|
464
|
+
|
465
|
+
return self;
|
193
466
|
}
|
194
467
|
|
195
468
|
/*
|
196
|
-
*
|
469
|
+
* @overload push(value, score = value)
|
470
|
+
*
|
471
|
+
* Push a value onto heap, using a score to determine sort-order.
|
472
|
+
*
|
473
|
+
* Value comes first because the separate score is optional, and because it feels
|
474
|
+
* like a more natural variation on +Array#push+ or +Queue#enq+. If a score
|
475
|
+
* isn't provided, the value must be an Integer or can be cast with
|
476
|
+
* +Float(value)+.
|
197
477
|
*
|
198
|
-
*
|
478
|
+
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
199
479
|
*
|
480
|
+
* @param value [Object] an object that is associated with the score.
|
481
|
+
* @param score [Integer,Float,#to_f] a score to compare against other scores.
|
482
|
+
*
|
483
|
+
* @return [self]
|
200
484
|
*/
|
201
485
|
static VALUE
|
202
486
|
dheap_push(int argc, VALUE *argv, VALUE self) {
|
203
|
-
|
204
|
-
|
205
|
-
|
487
|
+
dheap_t *heap = get_dheap_struct(self);
|
488
|
+
ENTRY entry;
|
489
|
+
rb_check_frozen(self);
|
206
490
|
|
207
|
-
|
208
|
-
|
209
|
-
|
491
|
+
rb_check_arity(argc, 1, 2);
|
492
|
+
entry.value = argv[0];
|
493
|
+
entry.score = VAL2SCORE(argc < 2 ? entry.value : argv[1]);
|
494
|
+
dheap_push_entry(heap, &entry);
|
210
495
|
|
211
|
-
return
|
496
|
+
return self;
|
212
497
|
}
|
213
498
|
|
214
499
|
/*
|
215
|
-
*
|
500
|
+
* Pushes a value onto the heap.
|
216
501
|
*
|
217
|
-
*
|
502
|
+
* The score will be derived from the value, by using the value itself if it is
|
503
|
+
* an Integer, otherwise by casting it with +Float(value)+.
|
218
504
|
*
|
219
|
-
*
|
505
|
+
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
220
506
|
*
|
507
|
+
* @param value [Integer,#to_f] a value with an intrinsic numeric score
|
508
|
+
* @return [self]
|
221
509
|
*/
|
222
510
|
static VALUE
|
223
|
-
dheap_left_shift(VALUE self, VALUE
|
224
|
-
|
511
|
+
dheap_left_shift(VALUE self, VALUE value) {
|
512
|
+
dheap_t *heap = get_dheap_struct(self);
|
513
|
+
ENTRY entry;
|
514
|
+
rb_check_frozen(self);
|
515
|
+
|
516
|
+
entry.score = VAL2SCORE(value);
|
517
|
+
entry.value = value;
|
518
|
+
dheap_push_entry(heap, &entry);
|
519
|
+
|
225
520
|
return self;
|
226
521
|
}
|
227
522
|
|
228
|
-
|
229
|
-
VALUE ary = DHEAP_GET_A(self); \
|
230
|
-
VALUE dval = DHEAP_GET_D(self); \
|
231
|
-
long last_index = DHEAP_LAST_IDX(ary); \
|
523
|
+
static const ENTRY EmptyDheapEntry;
|
232
524
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
VALUE
|
237
|
-
|
238
|
-
|
239
|
-
|
525
|
+
static inline VALUE
|
526
|
+
dheap_pop0(dheap_t *heap)
|
527
|
+
{
|
528
|
+
VALUE popped = DHEAP_VALUE(heap, 0);
|
529
|
+
if (0 < --heap->size) {
|
530
|
+
heap->entries[0] = heap->entries[heap->size];
|
531
|
+
heap->entries[heap->size] = EmptyDheapEntry; // unnecessary to zero?
|
532
|
+
dheap_sift_down(heap, 0, heap->size - 1);
|
533
|
+
}
|
534
|
+
return popped;
|
535
|
+
}
|
240
536
|
|
537
|
+
/*
|
538
|
+
* Clears all values from the heap, leaving it empty.
|
539
|
+
*
|
540
|
+
* @return [self]
|
541
|
+
*/
|
241
542
|
static VALUE
|
242
|
-
|
243
|
-
|
244
|
-
|
543
|
+
dheap_clear(VALUE self) {
|
544
|
+
dheap_t *heap = get_dheap_struct(self);
|
545
|
+
rb_check_frozen(self);
|
546
|
+
if (0 < DHEAP_SIZE(heap)) {
|
547
|
+
heap->size = 0;
|
548
|
+
}
|
549
|
+
return self;
|
245
550
|
}
|
246
551
|
|
247
552
|
/*
|
248
|
-
*
|
249
|
-
* heap property.
|
250
|
-
*
|
251
|
-
* Time complexity: O(d log n / log d).
|
553
|
+
* Returns the next value on the heap to be popped without popping it.
|
252
554
|
*
|
555
|
+
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
556
|
+
* @return [Object] the next value to be popped without popping it.
|
253
557
|
*/
|
254
558
|
static VALUE
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
|
261
|
-
return pop_value;
|
559
|
+
dheap_peek(VALUE self)
|
560
|
+
{
|
561
|
+
dheap_t *heap = get_dheap_struct(self);
|
562
|
+
if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
|
563
|
+
return DHEAP_VALUE(heap, 0);
|
262
564
|
}
|
263
565
|
|
264
566
|
/*
|
265
|
-
* Pops the minimum value from the top of the heap
|
266
|
-
* heap property.
|
567
|
+
* Pops the minimum value from the top of the heap
|
267
568
|
*
|
268
|
-
* Time complexity: O(d log n / log d)
|
569
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
269
570
|
*
|
571
|
+
* @return [Object] the value with the minimum score
|
572
|
+
*
|
573
|
+
* @see #peek
|
574
|
+
* @see #pop_lt
|
575
|
+
* @see #pop_lte
|
270
576
|
*/
|
271
577
|
static VALUE
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
struct cmp_opt_data cmp_opt = { 0, 0 };
|
279
|
-
if (below_score && !CMP_LTE(pop_score, below_score, cmp_opt)) return Qnil;
|
280
|
-
|
281
|
-
DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
|
282
|
-
return pop_value;
|
578
|
+
dheap_pop(VALUE self)
|
579
|
+
{
|
580
|
+
dheap_t *heap = get_dheap_struct(self);
|
581
|
+
rb_check_frozen(self);
|
582
|
+
if (DHEAP_SIZE(heap) <= 0) return Qnil;
|
583
|
+
return dheap_pop0(heap);
|
283
584
|
}
|
284
585
|
|
285
586
|
/*
|
286
|
-
* Pops the minimum value
|
287
|
-
*
|
587
|
+
* Pops the minimum value only if it is less than or equal to a max score.
|
588
|
+
*
|
589
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
288
590
|
*
|
289
|
-
* Time complexity: O(d log n / log d)
|
591
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
290
592
|
*
|
593
|
+
* @return [Object] the value with the minimum score
|
594
|
+
*
|
595
|
+
* @see #peek
|
596
|
+
* @see #pop
|
597
|
+
* @see #pop_lt
|
291
598
|
*/
|
292
599
|
static VALUE
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
600
|
+
dheap_pop_lte(VALUE self, VALUE max_score)
|
601
|
+
{
|
602
|
+
dheap_t *heap = get_dheap_struct(self);
|
603
|
+
rb_check_frozen(self);
|
604
|
+
if (DHEAP_SIZE(heap) <= 0) return Qnil;
|
605
|
+
if (!CMP_LTE(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
|
606
|
+
return dheap_pop0(heap);
|
607
|
+
}
|
301
608
|
|
302
|
-
|
303
|
-
|
609
|
+
/*
|
610
|
+
* Pops the minimum value only if it is less than a max score.
|
611
|
+
*
|
612
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
613
|
+
*
|
614
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
615
|
+
*
|
616
|
+
* @return [Object] the value with the minimum score
|
617
|
+
*
|
618
|
+
* @see #peek
|
619
|
+
* @see #pop
|
620
|
+
* @see #pop_lte
|
621
|
+
*/
|
622
|
+
static VALUE
|
623
|
+
dheap_pop_lt(VALUE self, VALUE max_score)
|
624
|
+
{
|
625
|
+
dheap_t *heap = get_dheap_struct(self);
|
626
|
+
rb_check_frozen(self);
|
627
|
+
if (DHEAP_SIZE(heap) <= 0) return Qnil;
|
628
|
+
if (!CMP_LT(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
|
629
|
+
return dheap_pop0(heap);
|
304
630
|
}
|
305
631
|
|
306
632
|
void
|
307
633
|
Init_d_heap(void)
|
308
634
|
{
|
309
635
|
id_cmp = rb_intern_const("<=>");
|
310
|
-
|
311
|
-
id_ivar_d = rb_intern_const("d");
|
636
|
+
id_abs = rb_intern_const("abs");
|
312
637
|
|
313
638
|
rb_cDHeap = rb_define_class("DHeap", rb_cObject);
|
314
|
-
|
315
|
-
rb_define_singleton_method(rb_cDHeap, "heap_sift_up", dheap_sift_up_s, 3);
|
639
|
+
rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
|
316
640
|
|
317
|
-
|
318
|
-
|
319
|
-
rb_define_method(rb_cDHeap, "freeze", dheap_freeze, 0);
|
641
|
+
rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
|
642
|
+
rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
|
320
643
|
|
321
|
-
rb_define_method(rb_cDHeap, "
|
322
|
-
rb_define_method(rb_cDHeap, "
|
644
|
+
rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
|
645
|
+
rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
|
646
|
+
rb_define_private_method(rb_cDHeap, "__init_clone__", dheap_init_clone, 2);
|
323
647
|
|
648
|
+
rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
|
649
|
+
rb_define_method(rb_cDHeap, "size", dheap_size, 0);
|
650
|
+
rb_define_method(rb_cDHeap, "empty?", dheap_empty_p, 0);
|
324
651
|
rb_define_method(rb_cDHeap, "peek", dheap_peek, 0);
|
652
|
+
|
653
|
+
rb_define_method(rb_cDHeap, "clear", dheap_clear, 0);
|
654
|
+
rb_define_method(rb_cDHeap, "insert", dheap_insert, 2);
|
325
655
|
rb_define_method(rb_cDHeap, "push", dheap_push, -1);
|
326
656
|
rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
|
327
657
|
rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
|