d_heap 0.1.0 → 0.4.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 +42 -0
- data/Gemfile +6 -2
- data/Gemfile.lock +36 -5
- data/README.md +394 -76
- data/Rakefile +8 -2
- data/benchmarks/push_n.yml +28 -0
- data/benchmarks/push_n_pop_n.yml +31 -0
- data/benchmarks/push_pop.yml +24 -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 +52 -0
- data/docs/benchmarks.txt +443 -0
- data/docs/profile.txt +392 -0
- data/ext/d_heap/d_heap.c +677 -134
- data/ext/d_heap/d_heap.h +29 -18
- data/ext/d_heap/extconf.rb +13 -0
- data/lib/benchmark_driver/runner/ips_zero_fail.rb +120 -0
- data/lib/d_heap.rb +17 -31
- data/lib/d_heap/benchmarks.rb +111 -0
- data/lib/d_heap/benchmarks/benchmarker.rb +113 -0
- data/lib/d_heap/benchmarks/implementations.rb +168 -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 +51 -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,219 +1,762 @@
|
|
1
|
+
#include <float.h>
|
1
2
|
#include "d_heap.h"
|
2
3
|
|
3
|
-
|
4
|
-
ID id_ivar_d;
|
4
|
+
#define SCORE_AS_LONG_DOUBLE 1
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
ID id_cmp; // <=>
|
7
|
+
ID id_abs; // abs
|
8
|
+
|
9
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
10
|
+
#define SCORE long double
|
11
|
+
#else
|
12
|
+
#define SCORE VALUE
|
13
|
+
#endif
|
14
|
+
|
15
|
+
typedef struct dheap_struct {
|
16
|
+
int d;
|
17
|
+
VALUE values;
|
18
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
19
|
+
long size;
|
20
|
+
long capa;
|
21
|
+
SCORE *cscores;
|
22
|
+
#else
|
23
|
+
VALUE scores; // T_ARRAY of comparable objects
|
24
|
+
#endif
|
25
|
+
} dheap_t;
|
26
|
+
|
27
|
+
#define DHEAP_VALUE(heap, idx) RARRAY_AREF((heap)->values, idx)
|
28
|
+
#define DHEAP_IDX_LAST(heap) (DHEAP_SIZE((heap)) - 1)
|
29
|
+
#define DHEAP_IDX_PARENT(heap, idx) (((idx) - 1) / (heap)->d)
|
30
|
+
#define DHEAP_IDX_CHILD0(heap, idx) (((idx) * (heap)->d) + 1)
|
31
|
+
|
32
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
33
|
+
#define DHEAP_SIZE(heap) ((heap)->size)
|
34
|
+
#else
|
35
|
+
#define DHEAP_SIZE(heap) (RARRAY_LEN((heap)->scores))
|
36
|
+
#endif
|
37
|
+
|
38
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
39
|
+
#define DHEAP_SCORE(heap, idx) \
|
40
|
+
(idx < 0 || heap->size <= idx ? (SCORE)0 : \
|
41
|
+
((heap)->cscores[idx]))
|
42
|
+
#else
|
43
|
+
#define DHEAP_SCORE(heap, idx) RARRAY_AREF((heap)->scores, idx)
|
44
|
+
#endif
|
45
|
+
|
46
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
47
|
+
|
48
|
+
#define CMP_LT(a, b) (a < b)
|
49
|
+
#define CMP_LTE(a, b) (a <= b)
|
50
|
+
#define CMP_GT(a, b) (a > b)
|
51
|
+
#define CMP_GTE(a, b) (a >= b)
|
52
|
+
|
53
|
+
#if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
|
54
|
+
#error 'unsigned long long' should fit into 'long double' mantissa
|
55
|
+
#endif
|
56
|
+
|
57
|
+
// copied and modified from ruby's object.c
|
58
|
+
#define FIX2SCORE(x) (long double)FIX2LONG(x)
|
59
|
+
// We could translate a much wider range of values to long double by
|
60
|
+
// implementing a new `rb_big2ldbl(x)` function. But requires reaching into
|
61
|
+
// T_BIGNUM internals.
|
62
|
+
static inline long double
|
63
|
+
BIG2SCORE(VALUE x)
|
64
|
+
{
|
65
|
+
if (RBIGNUM_POSITIVE_P(x)) {
|
66
|
+
unsigned long long ull = rb_big2ull(x);
|
67
|
+
return (long double)ull;
|
68
|
+
} else {
|
69
|
+
unsigned long long ull;
|
70
|
+
long double ldbl;
|
71
|
+
x = rb_funcall(x, id_abs, 0);
|
72
|
+
ull = rb_big2ull(x);
|
73
|
+
ldbl = (long double) ull;
|
74
|
+
return -ldbl;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
#define INT2SCORE(x) \
|
78
|
+
(FIXNUM_P(x) ? FIX2SCORE(x) : BIG2SCORE(x))
|
79
|
+
#define NUM2SCORE(x) \
|
80
|
+
(FIXNUM_P(x) ? FIX2SCORE(x) : \
|
81
|
+
RB_TYPE_P(x, T_BIGNUM) ? BIG2SCORE(x) : \
|
82
|
+
(Check_Type(x, T_FLOAT), (long double)RFLOAT_VALUE(x)))
|
83
|
+
static inline long double
|
84
|
+
RAT2SCORE(VALUE x)
|
85
|
+
{
|
86
|
+
VALUE num = rb_rational_num(x);
|
87
|
+
VALUE den = rb_rational_den(x);
|
88
|
+
return NUM2SCORE(num) / NUM2SCORE(den);
|
89
|
+
}
|
90
|
+
|
91
|
+
/*
|
92
|
+
* Convert both T_FIXNUM and T_FLOAT (and sometimes T_BIGNUM, T_RATIONAL,
|
93
|
+
* String, etc) to SCORE
|
94
|
+
* * with no loss of precision (where possible for Integer and Float),
|
95
|
+
* * raises an exception if
|
96
|
+
* * a positive integer is too large for unsigned long long (should be 64bit)
|
97
|
+
* * a negative integer is too small for signed long long (should be 64bit)
|
98
|
+
* * reduced to long double (should be 80 or 128 bit) if it is Rational
|
99
|
+
* * reduced to double precision if the value is convertable by Float(x)
|
100
|
+
*/
|
101
|
+
static inline long double
|
102
|
+
VAL2SCORE(VALUE score)
|
103
|
+
{
|
104
|
+
// assert that long double can hold 'unsigned long long':
|
105
|
+
// static_assert(sizeof(unsigned long long) * 8 <= LDBL_MANT_DIG);
|
106
|
+
// assert that long double can hold T_FLOAT
|
107
|
+
// static_assert(sizeof(double) <= sizeof(long double));
|
108
|
+
|
109
|
+
switch (TYPE(score)) {
|
110
|
+
case T_FIXNUM:
|
111
|
+
return FIX2SCORE(score);
|
112
|
+
case T_BIGNUM:
|
113
|
+
return BIG2SCORE(score);
|
114
|
+
case T_RATIONAL:
|
115
|
+
return RAT2SCORE(score);
|
116
|
+
default:
|
117
|
+
return (long double)(NUM2DBL(rb_Float(score)));
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
#else
|
122
|
+
|
123
|
+
#define VAL2SCORE(score) (score)
|
124
|
+
|
125
|
+
#define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
|
126
|
+
#define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
|
127
|
+
#define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
|
128
|
+
#define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
|
129
|
+
|
130
|
+
// from internal/compar.h
|
131
|
+
#define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
|
132
|
+
/*
|
133
|
+
* short-circuit evaluation for a few basic types.
|
134
|
+
*
|
135
|
+
* Only Integer, Float, and String are optimized,
|
136
|
+
* and only when both arguments are the same type.
|
137
|
+
*/
|
138
|
+
static inline int
|
139
|
+
optimized_cmp(SCORE a, SCORE b) {
|
140
|
+
if (a == b) // Fixnum equality and object equality
|
141
|
+
return 0;
|
142
|
+
if (FIXNUM_P(a) && FIXNUM_P(b))
|
143
|
+
return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
|
144
|
+
if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
|
145
|
+
{
|
146
|
+
double x, y;
|
147
|
+
x = RFLOAT_VALUE(a);
|
148
|
+
y = RFLOAT_VALUE(b);
|
149
|
+
if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
|
150
|
+
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
151
|
+
}
|
152
|
+
if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
|
153
|
+
return FIX2INT(rb_big_cmp(a, b));
|
154
|
+
if (STRING_P(a) && STRING_P(b))
|
155
|
+
return rb_str_cmp(a, b);
|
156
|
+
|
157
|
+
// give up on an optimized version and just call (a <=> b)
|
158
|
+
return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
|
159
|
+
}
|
160
|
+
|
161
|
+
#endif
|
162
|
+
|
163
|
+
#define DHEAP_Check_d_size(d) do { \
|
164
|
+
if (d < 2) { \
|
165
|
+
rb_raise(rb_eArgError, "DHeap d=%d is too small", d); \
|
166
|
+
} \
|
167
|
+
if (d > DHEAP_MAX_D) { \
|
168
|
+
rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
|
169
|
+
} \
|
170
|
+
} while (0)
|
171
|
+
|
172
|
+
#define DHEAP_Check_Index(index, last_index) do { \
|
173
|
+
if (index < 0) { \
|
174
|
+
rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
|
175
|
+
} \
|
176
|
+
else if (last_index < index) { \
|
177
|
+
rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
|
178
|
+
} \
|
179
|
+
} while (0)
|
180
|
+
|
181
|
+
static void
|
182
|
+
dheap_compact(void *ptr)
|
183
|
+
{
|
184
|
+
dheap_t *heap = ptr;
|
185
|
+
#ifndef SCORE_AS_LONG_DOUBLE
|
186
|
+
if (heap->scores) dheap_gc_location( heap->scores );
|
187
|
+
#endif
|
188
|
+
if (heap->values) dheap_gc_location( heap->values );
|
189
|
+
}
|
190
|
+
|
191
|
+
static void
|
192
|
+
dheap_mark(void *ptr)
|
193
|
+
{
|
194
|
+
dheap_t *heap = ptr;
|
195
|
+
#ifndef SCORE_AS_LONG_DOUBLE
|
196
|
+
if (heap->scores) rb_gc_mark_movable(heap->scores);
|
197
|
+
#endif
|
198
|
+
if (heap->values) rb_gc_mark_movable(heap->values);
|
199
|
+
}
|
200
|
+
|
201
|
+
static void
|
202
|
+
dheap_free(void *ptr)
|
203
|
+
{
|
204
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
205
|
+
dheap_t *heap = ptr;
|
206
|
+
heap->size = 0;
|
207
|
+
if (heap->cscores) {
|
208
|
+
ruby_xfree(heap->cscores);
|
209
|
+
heap->cscores = NULL;
|
210
|
+
}
|
211
|
+
heap->capa = 0;
|
212
|
+
#endif
|
213
|
+
xfree(ptr);
|
214
|
+
}
|
215
|
+
|
216
|
+
static size_t
|
217
|
+
dheap_memsize(const void *ptr)
|
218
|
+
{
|
219
|
+
const dheap_t *heap = ptr;
|
220
|
+
size_t size = 0;
|
221
|
+
size += sizeof(*heap);
|
222
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
223
|
+
size += sizeof(long double) * heap->capa;
|
224
|
+
#endif
|
225
|
+
return size;
|
226
|
+
}
|
227
|
+
|
228
|
+
static const rb_data_type_t dheap_data_type = {
|
229
|
+
"DHeap",
|
230
|
+
{
|
231
|
+
(void (*)(void*))dheap_mark,
|
232
|
+
(void (*)(void*))dheap_free,
|
233
|
+
(size_t (*)(const void *))dheap_memsize,
|
234
|
+
dheap_compact_callback(dheap_compact),
|
235
|
+
},
|
236
|
+
0, 0,
|
237
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
238
|
+
};
|
239
|
+
|
240
|
+
static VALUE
|
241
|
+
dheap_s_alloc(VALUE klass)
|
242
|
+
{
|
243
|
+
VALUE obj;
|
244
|
+
dheap_t *heap;
|
245
|
+
|
246
|
+
obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
|
247
|
+
heap->d = DHEAP_DEFAULT_D;
|
248
|
+
heap->values = Qnil;
|
249
|
+
|
250
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
251
|
+
heap->size = 0;
|
252
|
+
heap->capa = 0;
|
253
|
+
heap->cscores = NULL;
|
254
|
+
#else
|
255
|
+
heap->scores = Qnil;
|
256
|
+
#endif
|
257
|
+
|
258
|
+
return obj;
|
259
|
+
}
|
260
|
+
|
261
|
+
static inline dheap_t *
|
262
|
+
get_dheap_struct(VALUE self)
|
263
|
+
{
|
264
|
+
dheap_t *heap;
|
265
|
+
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
266
|
+
Check_Type(heap->values, T_ARRAY); // ensure it's been initialized
|
267
|
+
return heap;
|
268
|
+
}
|
269
|
+
|
270
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
271
|
+
|
272
|
+
static void
|
273
|
+
dheap_set_capa(dheap_t *heap, long new_capa)
|
274
|
+
{
|
275
|
+
long double *new, *old;
|
276
|
+
// Do nothing if we already have the capacity or are resizing too small
|
277
|
+
if (new_capa <= heap->capa) return;
|
278
|
+
if (new_capa <= heap->size) return;
|
279
|
+
|
280
|
+
// allocate
|
281
|
+
new = ruby_xcalloc(new_capa, sizeof(long double));
|
282
|
+
old = heap->cscores;
|
283
|
+
|
284
|
+
// copy contents
|
285
|
+
if (old) {
|
286
|
+
MEMCPY(new, old, long double, heap->size);
|
287
|
+
ruby_xfree(old);
|
12
288
|
}
|
13
289
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
290
|
+
// set vars
|
291
|
+
heap->cscores = new;
|
292
|
+
heap->capa = new_capa;
|
293
|
+
}
|
294
|
+
|
295
|
+
static void
|
296
|
+
dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
|
297
|
+
{
|
298
|
+
long new_size = heap->size + incr_by;
|
299
|
+
|
300
|
+
// check for overflow of new_size
|
301
|
+
if (DHEAP_MAX_SIZE - incr_by < heap->size)
|
302
|
+
rb_raise(rb_eIndexError, "index %ld too big", new_size);
|
303
|
+
|
304
|
+
// if it existing capacity is too small
|
305
|
+
if (heap->capa < new_size) {
|
306
|
+
// double it...
|
307
|
+
long new_capa = new_size * 2;
|
308
|
+
if (DHEAP_CAPA_INCR_MAX < new_size)
|
309
|
+
new_size = new_size + DHEAP_CAPA_INCR_MAX;
|
310
|
+
// check for overflow of new_capa
|
311
|
+
if (DHEAP_MAX_SIZE / 2 < new_size) new_capa = DHEAP_MAX_SIZE;
|
312
|
+
// cap max incr_by
|
313
|
+
if (heap->capa + DHEAP_CAPA_INCR_MAX < new_capa)
|
314
|
+
new_capa = heap->capa + DHEAP_CAPA_INCR_MAX;
|
315
|
+
|
316
|
+
dheap_set_capa(heap, new_capa);
|
20
317
|
}
|
318
|
+
}
|
319
|
+
|
320
|
+
#endif
|
321
|
+
|
322
|
+
/*
|
323
|
+
* @overload initialize(d = DHeap::DEFAULT_D)
|
324
|
+
* Initialize a _d_-ary min-heap.
|
325
|
+
*
|
326
|
+
* @param d [Integer] maximum number of children per parent
|
327
|
+
*/
|
328
|
+
static VALUE
|
329
|
+
dheap_initialize(int argc, VALUE *argv, VALUE self) {
|
330
|
+
dheap_t *heap;
|
331
|
+
int d;
|
332
|
+
|
333
|
+
rb_check_arity(argc, 0, 1);
|
334
|
+
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
335
|
+
|
336
|
+
d = argc ? NUM2INT(argv[0]) : DHEAP_DEFAULT_D;
|
337
|
+
DHEAP_Check_d_size(d);
|
338
|
+
heap->d = d;
|
339
|
+
|
340
|
+
heap->values = rb_ary_new_capa(DHEAP_DEFAULT_SIZE);
|
341
|
+
|
342
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
343
|
+
dheap_set_capa(heap, DHEAP_DEFAULT_SIZE);
|
344
|
+
#else
|
345
|
+
heap->scores = rb_ary_new_capa(DHEAP_DEFAULT_SIZE);
|
346
|
+
#endif
|
347
|
+
|
348
|
+
return self;
|
349
|
+
}
|
350
|
+
|
351
|
+
/* :nodoc: */
|
352
|
+
static VALUE
|
353
|
+
dheap_initialize_copy(VALUE copy, VALUE orig)
|
354
|
+
{
|
355
|
+
dheap_t *heap_copy;
|
356
|
+
dheap_t *heap_orig = get_dheap_struct(orig);
|
357
|
+
|
358
|
+
rb_check_frozen(copy);
|
359
|
+
TypedData_Get_Struct(copy, dheap_t, &dheap_data_type, heap_copy);
|
21
360
|
|
22
|
-
|
23
|
-
Check_Type(dval, T_FIXNUM); \
|
24
|
-
int d = FIX2INT(dval); \
|
25
|
-
long sift_idx = NUM2LONG(idxval);
|
361
|
+
heap_copy->d = heap_orig->d;
|
26
362
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
363
|
+
heap_copy->values = rb_ary_new();
|
364
|
+
rb_ary_replace(heap_copy->values, heap_orig->values);
|
365
|
+
|
366
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
367
|
+
dheap_set_capa(heap_copy, heap_orig->capa);
|
368
|
+
heap_copy->size = heap_orig->size;
|
369
|
+
if (heap_copy->size)
|
370
|
+
MEMCPY(heap_orig->cscores, heap_copy->cscores, long double, heap_orig->size);
|
371
|
+
#else
|
372
|
+
heap_copy->scores = rb_ary_new();
|
373
|
+
rb_ary_replace(heap_copy->scores, heap_orig->scores);
|
374
|
+
#endif
|
375
|
+
|
376
|
+
return copy;
|
377
|
+
}
|
378
|
+
|
379
|
+
static inline void
|
380
|
+
dheap_assign(dheap_t *heap, long idx, SCORE score, VALUE value)
|
381
|
+
{
|
382
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
383
|
+
heap->cscores[idx] = score;
|
384
|
+
rb_ary_store(heap->values, idx, value);
|
385
|
+
#else
|
386
|
+
rb_ary_store(heap->scores, idx, score);
|
387
|
+
rb_ary_store(heap->values, idx, value);
|
388
|
+
#endif
|
389
|
+
}
|
34
390
|
|
35
391
|
VALUE
|
36
|
-
dheap_ary_sift_up(
|
37
|
-
|
392
|
+
dheap_ary_sift_up(dheap_t *heap, long sift_index) {
|
393
|
+
VALUE sift_value;
|
394
|
+
SCORE sift_score;
|
395
|
+
|
396
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
397
|
+
DHEAP_Check_Index(sift_index, last_index);
|
398
|
+
|
399
|
+
sift_value = DHEAP_VALUE(heap, sift_index);
|
400
|
+
sift_score = DHEAP_SCORE(heap, sift_index);
|
401
|
+
|
38
402
|
// sift it up to where it belongs
|
39
|
-
for (long
|
40
|
-
|
41
|
-
VALUE
|
403
|
+
for (long parent_index; 0 < sift_index; sift_index = parent_index) {
|
404
|
+
SCORE parent_score;
|
405
|
+
VALUE parent_value;
|
406
|
+
|
407
|
+
debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
408
|
+
parent_index = DHEAP_IDX_PARENT(heap, sift_index);
|
409
|
+
parent_score = DHEAP_SCORE(heap, parent_index);
|
42
410
|
|
43
411
|
// parent is smaller: heap is restored
|
44
|
-
if (CMP_LTE(
|
412
|
+
if (CMP_LTE(parent_score, sift_score)) break;
|
45
413
|
|
46
414
|
// parent is larger: swap and continue sifting up
|
47
|
-
|
48
|
-
|
415
|
+
parent_value = DHEAP_VALUE(heap, parent_index);
|
416
|
+
dheap_assign(heap, sift_index, parent_score, parent_value);
|
417
|
+
dheap_assign(heap, parent_index, sift_score, sift_value);
|
49
418
|
}
|
50
|
-
|
419
|
+
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
420
|
+
return LONG2NUM(sift_index);
|
51
421
|
}
|
52
422
|
|
53
|
-
|
54
423
|
VALUE
|
55
|
-
dheap_ary_sift_down(
|
56
|
-
|
424
|
+
dheap_ary_sift_down(dheap_t *heap, long sift_index) {
|
425
|
+
VALUE sift_value;
|
426
|
+
SCORE sift_score;
|
427
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
428
|
+
DHEAP_Check_Index(sift_index, last_index);
|
429
|
+
|
430
|
+
sift_value = DHEAP_VALUE(heap, sift_index);
|
431
|
+
sift_score = DHEAP_SCORE(heap, sift_index);
|
432
|
+
|
57
433
|
// iteratively sift it down to where it belongs
|
58
|
-
for (long
|
434
|
+
for (long child_index; sift_index < last_index; sift_index = child_index) {
|
435
|
+
long child_idx0, last_sibidx;
|
436
|
+
SCORE child_score;
|
437
|
+
VALUE child_value;
|
438
|
+
|
59
439
|
// find first child index, and break if we've reached the last layer
|
60
|
-
|
61
|
-
|
62
|
-
if (
|
63
|
-
|
64
|
-
// find the min child (and its
|
65
|
-
// requires "d" comparisons
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
440
|
+
child_idx0 = child_index = DHEAP_IDX_CHILD0(heap, sift_index);
|
441
|
+
debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
442
|
+
if (last_index < child_idx0) break;
|
443
|
+
|
444
|
+
// find the min child (and its child_index)
|
445
|
+
// requires "d" comparisons to find min child and compare to sift_score
|
446
|
+
last_sibidx = child_idx0 + heap->d - 1;
|
447
|
+
if (last_index < last_sibidx) last_sibidx = last_index;
|
448
|
+
child_score = DHEAP_SCORE(heap, child_idx0);
|
449
|
+
child_index = child_idx0;
|
450
|
+
for (long sibling_index = child_idx0 + 1;
|
451
|
+
sibling_index <= last_sibidx;
|
452
|
+
++sibling_index) {
|
453
|
+
SCORE sibling_score = DHEAP_SCORE(heap, sibling_index);
|
454
|
+
|
455
|
+
if (CMP_LT(sibling_score, child_score)) {
|
456
|
+
child_score = sibling_score;
|
457
|
+
child_index = sibling_index;
|
76
458
|
}
|
77
459
|
}
|
78
460
|
|
79
461
|
// child is larger: heap is restored
|
80
|
-
if (
|
462
|
+
if (CMP_LTE(sift_score, child_score)) break;
|
81
463
|
|
82
464
|
// child is smaller: swap and continue sifting down
|
83
|
-
|
84
|
-
|
465
|
+
child_value = DHEAP_VALUE(heap, child_index);
|
466
|
+
dheap_assign(heap, sift_index, child_score, child_value);
|
467
|
+
dheap_assign(heap, child_index, sift_score, sift_value);
|
85
468
|
}
|
86
|
-
|
469
|
+
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
470
|
+
return LONG2NUM(sift_index);
|
87
471
|
}
|
88
472
|
|
89
473
|
/*
|
90
|
-
*
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
*
|
96
|
-
|
97
|
-
|
474
|
+
* @return [Integer] the number of elements in the heap
|
475
|
+
*/
|
476
|
+
static VALUE
|
477
|
+
dheap_size(VALUE self)
|
478
|
+
{
|
479
|
+
dheap_t *heap = get_dheap_struct(self);
|
480
|
+
long size = DHEAP_SIZE(heap);
|
481
|
+
return LONG2NUM(size);
|
482
|
+
}
|
483
|
+
|
484
|
+
/*
|
485
|
+
* @return [Boolean] is the heap empty?
|
486
|
+
*/
|
487
|
+
static VALUE
|
488
|
+
dheap_empty_p(VALUE self)
|
489
|
+
{
|
490
|
+
dheap_t *heap = get_dheap_struct(self);
|
491
|
+
long size = DHEAP_SIZE(heap);
|
492
|
+
return size == 0 ? Qtrue : Qfalse;
|
493
|
+
}
|
494
|
+
|
495
|
+
/*
|
496
|
+
* @return [Integer] the maximum number of children per parent
|
497
|
+
*/
|
498
|
+
static VALUE
|
499
|
+
dheap_attr_d(VALUE self)
|
500
|
+
{
|
501
|
+
dheap_t *heap = get_dheap_struct(self);
|
502
|
+
return INT2FIX(heap->d);
|
503
|
+
}
|
504
|
+
|
505
|
+
/*
|
506
|
+
* Freezes the heap as well as its underlying array, but does <i>not</i>
|
507
|
+
* deep-freeze the elements in the heap.
|
98
508
|
*
|
509
|
+
* @return [self]
|
99
510
|
*/
|
100
511
|
static VALUE
|
101
|
-
|
102
|
-
|
103
|
-
|
512
|
+
dheap_freeze(VALUE self) {
|
513
|
+
dheap_t *heap = get_dheap_struct(self);
|
514
|
+
ID id_freeze = rb_intern("freeze");
|
515
|
+
rb_funcall(heap->values, id_freeze, 0);
|
516
|
+
#ifndef SCORE_AS_LONG_DOUBLE
|
517
|
+
rb_funcall(heap->scores, id_freeze, 0);
|
518
|
+
#endif
|
519
|
+
return rb_call_super(0, NULL);
|
520
|
+
}
|
521
|
+
|
522
|
+
/* :nodoc: */
|
523
|
+
static VALUE
|
524
|
+
dheap_init_clone(VALUE clone, VALUE orig, VALUE kwfreeze)
|
525
|
+
{
|
526
|
+
dheap_initialize_copy(clone, orig);
|
527
|
+
if (RTEST(kwfreeze) || (kwfreeze == Qnil && OBJ_FROZEN(orig))) {
|
528
|
+
rb_funcall(clone, rb_intern("freeze"), 0);
|
529
|
+
}
|
530
|
+
return clone;
|
104
531
|
}
|
105
532
|
|
106
533
|
/*
|
107
|
-
*
|
108
|
-
*
|
534
|
+
* @overload push(score, value = score)
|
535
|
+
*
|
536
|
+
* Push a value onto heap, using a score to determine sort-order.
|
109
537
|
*
|
110
|
-
*
|
111
|
-
*
|
538
|
+
* Ideally, the score should be a frozen value that can be efficiently compared
|
539
|
+
* to other scores, e.g. an Integer or Float or (maybe) a String
|
112
540
|
*
|
113
|
-
* Time complexity: O(
|
114
|
-
* sorts into the bottom layer (e.g. canceled timers), this can avg O(1).
|
541
|
+
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
115
542
|
*
|
543
|
+
* @param score [#<=>] a value that can be compared to other scores.
|
544
|
+
* @param value [Object] an object that is associated with the score.
|
545
|
+
*
|
546
|
+
* @return [Integer] the index of the value's final position.
|
116
547
|
*/
|
117
548
|
static VALUE
|
118
|
-
|
119
|
-
|
120
|
-
|
549
|
+
dheap_push(int argc, VALUE *argv, VALUE self) {
|
550
|
+
VALUE scr, val;
|
551
|
+
dheap_t *heap;
|
552
|
+
long last_index;
|
553
|
+
rb_check_frozen(self);
|
554
|
+
|
555
|
+
rb_check_arity(argc, 1, 2);
|
556
|
+
heap = get_dheap_struct(self);
|
557
|
+
scr = argv[0];
|
558
|
+
val = argc < 2 ? scr : argv[1];
|
559
|
+
|
560
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
561
|
+
do {
|
562
|
+
long double score_as_ldbl = VAL2SCORE(scr);
|
563
|
+
dheap_ensure_room_for_push(heap, 1);
|
564
|
+
++heap->size;
|
565
|
+
last_index = DHEAP_IDX_LAST(heap);
|
566
|
+
heap->cscores[last_index] = score_as_ldbl;
|
567
|
+
} while (0);
|
568
|
+
#else
|
569
|
+
rb_ary_push((heap)->scores, scr);
|
570
|
+
last_index = DHEAP_IDX_LAST(heap);
|
571
|
+
#endif
|
572
|
+
rb_ary_push((heap)->values, val);
|
573
|
+
|
574
|
+
return dheap_ary_sift_up(heap, last_index);
|
121
575
|
}
|
122
576
|
|
577
|
+
/*
|
578
|
+
* Pushes a comparable value onto the heap.
|
579
|
+
*
|
580
|
+
* The value will be its own score.
|
581
|
+
*
|
582
|
+
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
583
|
+
*
|
584
|
+
* @param value [#<=>] a value that can be compared to other heap members.
|
585
|
+
* @return [self]
|
586
|
+
*/
|
123
587
|
static VALUE
|
124
|
-
|
125
|
-
|
126
|
-
int d = DHEAP_DEFAULT_D;
|
127
|
-
if (argc) {
|
128
|
-
d = NUM2INT(argv[0]);
|
129
|
-
}
|
130
|
-
DHEAP_Check_d_size(d);
|
131
|
-
rb_ivar_set(self, id_ivar_d, INT2FIX(d));
|
132
|
-
rb_ivar_set(self, id_ivar_a, rb_ary_new());
|
588
|
+
dheap_left_shift(VALUE self, VALUE value) {
|
589
|
+
dheap_push(1, &value, self);
|
133
590
|
return self;
|
134
591
|
}
|
135
592
|
|
136
|
-
|
137
|
-
|
593
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
594
|
+
#define DHEAP_DROP_LAST(heap) do { \
|
595
|
+
rb_ary_pop(heap->values); \
|
596
|
+
--heap->size; \
|
597
|
+
} while (0)
|
598
|
+
#else
|
599
|
+
#define DHEAP_DROP_LAST(heap) do { \
|
600
|
+
rb_ary_pop(heap->values); \
|
601
|
+
rb_ary_pop(heap->scores); \
|
602
|
+
} while (0)
|
603
|
+
#endif
|
604
|
+
static inline void
|
605
|
+
dheap_pop_swap_last_and_sift_down(dheap_t *heap, long last_index)
|
606
|
+
{
|
607
|
+
if (last_index == 0) {
|
608
|
+
DHEAP_DROP_LAST(heap);
|
609
|
+
}
|
610
|
+
else
|
611
|
+
{
|
612
|
+
VALUE sift_value = DHEAP_VALUE(heap, last_index);
|
613
|
+
SCORE sift_score = DHEAP_SCORE(heap, last_index);
|
614
|
+
dheap_assign(heap, 0, sift_score, sift_value);
|
615
|
+
DHEAP_DROP_LAST(heap);
|
616
|
+
dheap_ary_sift_down(heap, 0);
|
617
|
+
}
|
618
|
+
}
|
138
619
|
|
139
|
-
|
140
|
-
|
620
|
+
#ifdef SCORE_AS_LONG_DOUBLE
|
621
|
+
#define DHEAP_CLEAR(heap) do { \
|
622
|
+
rb_ary_clear(heap->values); \
|
623
|
+
heap->size = 0; \
|
624
|
+
} while (0)
|
625
|
+
#else
|
626
|
+
#define DHEAP_CLEAR(heap) do { \
|
627
|
+
rb_ary_clear(heap->values); \
|
628
|
+
rb_ary_clear(heap->scores); \
|
629
|
+
} while (0)
|
630
|
+
#endif
|
141
631
|
|
142
632
|
/*
|
143
|
-
*
|
633
|
+
* Returns the next value on the heap to be popped without popping it.
|
144
634
|
*
|
145
|
-
*
|
635
|
+
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
636
|
+
* @return [Object] the next value to be popped without popping it.
|
637
|
+
*/
|
638
|
+
static VALUE
|
639
|
+
dheap_clear(VALUE self) {
|
640
|
+
dheap_t *heap = get_dheap_struct(self);
|
641
|
+
rb_check_frozen(self);
|
642
|
+
if (0 < DHEAP_SIZE(heap)) {
|
643
|
+
DHEAP_CLEAR(heap);
|
644
|
+
}
|
645
|
+
return self;
|
646
|
+
}
|
647
|
+
|
648
|
+
/*
|
649
|
+
* Returns the next value on the heap to be popped without popping it.
|
146
650
|
*
|
651
|
+
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
652
|
+
* @return [Object] the next value to be popped without popping it.
|
147
653
|
*/
|
148
654
|
static VALUE
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
long last_idx = RARRAY_LEN(heap_a) - 1;
|
154
|
-
return dheap_sift_up_s(Qnil, heap_a, heap_d, LONG2NUM(last_idx));
|
655
|
+
dheap_peek(VALUE self) {
|
656
|
+
dheap_t *heap = get_dheap_struct(self);
|
657
|
+
if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
|
658
|
+
return DHEAP_VALUE(heap, 0);
|
155
659
|
}
|
156
660
|
|
157
661
|
/*
|
158
|
-
*
|
662
|
+
* Pops the minimum value from the top of the heap
|
159
663
|
*
|
160
|
-
* Time complexity: O(d log n / log d)
|
664
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
665
|
+
*/
|
666
|
+
static VALUE
|
667
|
+
dheap_pop(VALUE self) {
|
668
|
+
VALUE pop_value;
|
669
|
+
dheap_t *heap = get_dheap_struct(self);
|
670
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
671
|
+
rb_check_frozen(self);
|
672
|
+
|
673
|
+
if (last_index < 0) return Qnil;
|
674
|
+
pop_value = DHEAP_VALUE(heap, 0);
|
675
|
+
|
676
|
+
dheap_pop_swap_last_and_sift_down(heap, last_index);
|
677
|
+
return pop_value;
|
678
|
+
}
|
679
|
+
|
680
|
+
/*
|
681
|
+
* Pops the minimum value only if it is less than or equal to a max score.
|
161
682
|
*
|
162
|
-
*
|
683
|
+
* @param max_score [#to_f] the maximum score to be popped
|
163
684
|
*
|
685
|
+
* @see #pop
|
164
686
|
*/
|
165
687
|
static VALUE
|
166
|
-
|
167
|
-
|
168
|
-
|
688
|
+
dheap_pop_lte(VALUE self, VALUE max_score) {
|
689
|
+
VALUE pop_value;
|
690
|
+
dheap_t *heap = get_dheap_struct(self);
|
691
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
692
|
+
rb_check_frozen(self);
|
693
|
+
|
694
|
+
if (last_index < 0) return Qnil;
|
695
|
+
pop_value = DHEAP_VALUE(heap, 0);
|
696
|
+
|
697
|
+
do {
|
698
|
+
SCORE max = VAL2SCORE(max_score);
|
699
|
+
SCORE pop_score = DHEAP_SCORE(heap, 0);
|
700
|
+
if (max && !CMP_LTE(pop_score, max)) return Qnil;
|
701
|
+
} while (0);
|
702
|
+
|
703
|
+
dheap_pop_swap_last_and_sift_down(heap, last_index);
|
704
|
+
return pop_value;
|
169
705
|
}
|
170
706
|
|
171
707
|
/*
|
172
|
-
* Pops the minimum value
|
173
|
-
* heap property.
|
708
|
+
* Pops the minimum value only if it is less than a max score.
|
174
709
|
*
|
175
|
-
*
|
710
|
+
* @param max_score [#to_f] the maximum score to be popped
|
176
711
|
*
|
712
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
177
713
|
*/
|
178
714
|
static VALUE
|
179
|
-
|
180
|
-
VALUE
|
181
|
-
|
182
|
-
long
|
715
|
+
dheap_pop_lt(VALUE self, VALUE max_score) {
|
716
|
+
VALUE pop_value;
|
717
|
+
dheap_t *heap = get_dheap_struct(self);
|
718
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
719
|
+
rb_check_frozen(self);
|
183
720
|
|
184
|
-
|
185
|
-
|
186
|
-
*/
|
187
|
-
if (last_idx <= 0) return rb_ary_pop(heap_a);
|
721
|
+
if (last_index < 0) return Qnil;
|
722
|
+
pop_value = DHEAP_VALUE(heap, 0);
|
188
723
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
rb_ary_store(heap_a, 0, sift_val);
|
195
|
-
rb_ary_pop(heap_a);
|
196
|
-
dheap_sift_down_s(Qnil, heap_a, heap_d, LONG2NUM(0));
|
724
|
+
do {
|
725
|
+
SCORE max = VAL2SCORE(max_score);
|
726
|
+
SCORE pop_score = DHEAP_SCORE(heap, 0);
|
727
|
+
if (max && !CMP_LT(pop_score, max)) return Qnil;
|
728
|
+
} while (0);
|
197
729
|
|
198
|
-
|
730
|
+
dheap_pop_swap_last_and_sift_down(heap, last_index);
|
731
|
+
return pop_value;
|
199
732
|
}
|
200
733
|
|
201
734
|
void
|
202
735
|
Init_d_heap(void)
|
203
736
|
{
|
204
737
|
id_cmp = rb_intern_const("<=>");
|
205
|
-
|
206
|
-
id_ivar_d = rb_intern_const("d");
|
738
|
+
id_abs = rb_intern_const("abs");
|
207
739
|
|
208
740
|
rb_cDHeap = rb_define_class("DHeap", rb_cObject);
|
741
|
+
rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
|
742
|
+
|
743
|
+
rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
|
744
|
+
rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
|
745
|
+
|
209
746
|
rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
|
210
|
-
rb_define_method(rb_cDHeap, "
|
211
|
-
rb_define_private_method(rb_cDHeap, "
|
747
|
+
rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
|
748
|
+
rb_define_private_method(rb_cDHeap, "__init_clone__", dheap_init_clone, 2);
|
749
|
+
rb_define_method(rb_cDHeap, "freeze", dheap_freeze, 0);
|
212
750
|
|
213
|
-
|
214
|
-
|
751
|
+
rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
|
752
|
+
rb_define_method(rb_cDHeap, "size", dheap_size, 0);
|
753
|
+
rb_define_method(rb_cDHeap, "empty?", dheap_empty_p, 0);
|
754
|
+
rb_define_method(rb_cDHeap, "peek", dheap_peek, 0);
|
215
755
|
|
216
|
-
rb_define_method(rb_cDHeap, "
|
217
|
-
rb_define_method(rb_cDHeap, "
|
218
|
-
rb_define_method(rb_cDHeap, "
|
756
|
+
rb_define_method(rb_cDHeap, "clear", dheap_clear, 0);
|
757
|
+
rb_define_method(rb_cDHeap, "push", dheap_push, -1);
|
758
|
+
rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
|
759
|
+
rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
|
760
|
+
rb_define_method(rb_cDHeap, "pop_lt", dheap_pop_lt, 1);
|
761
|
+
rb_define_method(rb_cDHeap, "pop_lte", dheap_pop_lte, 1);
|
219
762
|
}
|