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