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