bootstrap
view church/runtime/hashtable.state @ 835:f172f7b54064
add code to cache the slot-count for object allocations in a hashtable (turns out to be slower than walking the class hierarchy)
add opts for comparisons and branches
unroll loop in allocator, remove recursive calls
add opts for comparisons and branches
unroll loop in allocator, remove recursive calls
| author | John Leuner <jewel@subvert-the-dominant-paradigm.net> |
|---|---|
| date | Mon Sep 12 21:36:42 2011 +0200 (8 months ago) |
| parents | f1cc298bae7b |
| children | f5417ff321cb |
line source
1 (include "church/runtime/church_types.state")
2 (include "church/runtime/church_syntax.state")
5 (external-function |state-gc-alloc|)
6 (external-function |state-gc-alloc-and-zero|)
7 (external-function state-abort)
8 (external-function church-cons)
10 ; new hash table
12 (define church-hashtable-allocate (lambda (size)
13 (church-assert (church-> size 0) "size must be greater than 0")
14 (if (not (= (band size (- size 1)) 0))
15 (let ((highest-bit (bsr size))
16 (new-size (bshl 1 (+ highest-bit 1))))
17 ; (call-c-extern printf "given size %lu, new size %lu
18 ;" size new-size)
19 (set! size new-size) ))
20 ; size must be a power of 2
21 (church-assert (church-= (band size (- size 1)) 0) "hash table size must be a power of 2")
22 ; hash table is 8 words, plus 2 * size words
23 (let ((ht-alloc-size (+ (* 8 CHURCH_WORD_SIZE) (* size (* 2 CHURCH_WORD_SIZE))))
24 (ht (state-gc-alloc-and-zero ht-alloc-size)))
25 ; clear memory
26 (call-c-extern memset ht 0 ht-alloc-size)
28 (set-hashtable-size! ht size)
29 (set-hashtable-count! ht 0)
32 ; (do ((i 0 (+ i 1)))
33 ; (< i (hashtable-size ht))
34 ; (if (not (= (deref (hashtable-table ht) (* 8 i)) 0))
35 ; (begin
36 ; (call-c-extern printf "ht %p
37 ;" ht)
38 ; (state-abort "dirty hashtable"))))
41 ht)))
43 ;(define church-hashtable-set-functions (lambda (ht hash-fn comp-fn)
44 ; (set-hashtable-hash-function! ht hash-fn)
45 ; (set-hashtable-comp-function! ht comp-fn)
46 ; ht))
47 ;; set hash and compare function
48 (define church-hashtable-set-functions (lambda (ht hash-fn1 hash-fn2 comp-fn)
49 (set-hashtable-hash-function1! ht hash-fn1)
50 (set-hashtable-hash-function2! ht hash-fn2)
51 (set-hashtable-comp-function! ht comp-fn)
52 ht))
54 (define church-hashtable-has-key? (lambda (ht key)
55 (let ((hash-table (hashtable-table ht))
56 (hash-table-size (hashtable-size ht))
57 (comp-fn (hashtable-comp-function ht))
58 (h ((hashtable-hash-function1 ht) key))
59 (index (* (mod h hash-table-size) 2))
60 (probe-step ((hashtable-hash-function2 ht) key)))
61 (tagbody
62 check
63 (let ((lookup (deref hash-table (* index CHURCH_WORD_SIZE))))
64 (if (= lookup 0)
65 (return TAG_NIL)
66 )
67 (if (= (comp-fn lookup key) 0)
68 (return TAG_TRUE)
69 ))
70 (set! h (+ h probe-step))
71 ; mask out high bit
72 (set! h (band h (band -1 (bnot (bshl 1 31)))))
73 (set! index (* (mod h hash-table-size) 2))
74 (go check)))))
76 (define church-hashtable-lookup-key (lambda (ht key default-value)
77 (let ((hash-table (hashtable-table ht))
78 (hash-table-size (hashtable-size ht))
79 (hash-fn (hashtable-hash-function1 ht))
80 (comp-fn (hashtable-comp-function ht))
81 (h (hash-fn key))
82 (index (* (mod h hash-table-size) 2))
83 (probe-step ((hashtable-hash-function2 ht) key)))
84 (tagbody
85 check
86 ; (call-c-extern printf "find_hash_entry at index %lu
87 ;" base-index)
88 (let ((lookup (deref hash-table (* index CHURCH_WORD_SIZE))))
89 (if (= lookup 0)
90 (return default-value)
91 )
92 (if (= (comp-fn lookup key) 0)
93 (return (deref hash-table (* (+ index 1) CHURCH_WORD_SIZE)))
94 ))
95 (set! h (+ h probe-step))
96 ; mask out high bit
97 (set! h (band h (band -1 (bnot (bshl 1 31)))))
98 (set! index (* (mod h hash-table-size) 2))
99 (go check)))
100 (state-abort "failed find hash entry")))
102 (define church-hashtable-add-or-replace (lambda (ht key value)
103 ; (call-c-extern printf "church-hashtable-add-or-replace %p size %lu count %lu
104 ;" ht (hashtable-size ht) (hashtable-count ht))
105 (let ((new-count (church-hashtable-add-or-replace-helper ht key value)))
106 (if (> new-count 0)
107 (begin
108 (set-hashtable-count! ht (+ (hashtable-count ht) 1))
109 ; (call-c-extern printf "ht %p count %lu size %lu
110 ;" ht (hashtable-count ht) (hashtable-size ht))
111 ; check if we should grow
112 (if (> (/ (* (hashtable-count ht) 100) (hashtable-size ht)) 60)
113 (let ((newsize (* (hashtable-size ht) 2))
114 (new-ht (church-hashtable-allocate newsize)))
115 ; copy the old functions into the new table
116 (church-hashtable-set-functions new-ht (hashtable-hash-function1 ht) (hashtable-hash-function2 ht) (hashtable-comp-function ht))
117 (church-hashtable-rehash-from-old-table ht new-ht)
118 (return new-ht))
119 ht))
120 ht))))
122 (define church-hashtable-add-or-replace-helper (lambda (ht key value)
123 (let ((hash-table (hashtable-table ht))
124 (hash-table-size (hashtable-size ht))
125 (hash-fn (hashtable-hash-function1 ht))
126 (comp-fn (hashtable-comp-function ht))
127 (h (hash-fn key))
128 (index (* (mod h hash-table-size) 2))
129 (probe-step ((hashtable-hash-function2 ht) key)))
130 (tagbody
131 check
132 ; (call-c-extern printf "ht %p h %p index %lu, key %p value %p probe-step %p
133 ;" ht h index key value probe-step)
134 (let ((lookup (deref hash-table (* index CHURCH_WORD_SIZE))))
135 (if (= lookup 0)
136 ;add a new entry
137 (begin
138 (set-long! hash-table (* index CHURCH_WORD_SIZE) key)
139 (set-long! hash-table (* (+ index 1) CHURCH_WORD_SIZE) value)
140 (return 1))) ; 1 for new entry
141 (if (= (comp-fn lookup key) 0)
142 ; overwrite current entry
143 (begin
144 ; (call-c-extern printf "h %p index %lu at index %lu, overwrite %p %p %lu
145 ;" h index index lookup key (comp-fn lookup key))
146 (set-long! hash-table (* index CHURCH_WORD_SIZE) key)
147 (set-long! hash-table (* (+ index 1) CHURCH_WORD_SIZE) value)
148 (return 0)))) ; 0 for replacement of old entry
149 (set! h (+ h probe-step))
150 ; mask out high bit
151 (set! h (band h (band -1 (bnot (bshl 1 31)))))
152 (set! index (* (mod h hash-table-size) 2))
153 (go check)))
154 (state-abort "failed find hash entry")))
156 (define church-hashtable-rehash-from-old-table (lambda (ht new-ht)
157 (let ((hash-table (hashtable-table ht))
158 (hash-table-size (hashtable-size ht)))
159 (let ((counter 0)
160 (base-index 0))
161 (tagbody
162 check
163 (if (< counter hash-table-size)
164 (let ((key (deref hash-table (* base-index CHURCH_WORD_SIZE)))
165 (value (deref hash-table (* (+ base-index 1) CHURCH_WORD_SIZE))))
166 (if (not (= key 0))
167 (begin
168 (church-hashtable-add-or-replace-helper new-ht key value)
169 (set-hashtable-count! new-ht (+ (hashtable-count new-ht) 1)))
170 )
171 (set! base-index (+ base-index 2)))
172 (go end))
173 (set! counter (+ counter 1))
174 (go check)
175 end)))))
177 (external-function church-reverse!)
178 (define church-hashtable-keys (lambda (ht)
179 (let ((hash-table (hashtable-table ht))
180 (hash-table-size (hashtable-size ht))
181 (counter 0)
182 (base-index 0)
183 (keys TAG_NIL))
184 (tagbody
185 check
186 (if (< counter hash-table-size)
187 (let ((key (deref hash-table (* base-index CHURCH_WORD_SIZE))))
188 (if (not (= key 0))
189 (push key keys))
190 (set! base-index (+ base-index 2)))
191 (go end))
192 (set! counter (+ counter 1))
193 (go check)
194 end)
195 (church-reverse! keys))))
197 ; hash functions
199 (define church_hash_string_function (lambda (str)
200 (let ((h 0))
201 (tagbody
202 check
203 (if (= (get-byte str 0) 0)
204 (go done)
205 (begin
206 (set! h (+ (* h 31) (get-byte str 0)))
207 (set! str (+ str 1))
208 (go check)))
209 done)
210 (bshr (bshl h 1) 1) ; mask out any sign bit
211 )))
213 (define church_second_hash_string_function (lambda (str)
214 (let ((hash 0))
215 (tagbody
216 check
217 (if (= (get-byte str 0) 0)
218 (go done)
219 (begin
220 (set! hash (+ hash (get-byte str 0)))
221 (set! hash (+ hash (bshl hash 10)))
222 (set! hash (bxor hash (bshr hash 6)))
223 (set! str (+ str 1))
224 ))
225 done)
226 (set! hash (+ hash (bshl hash 3)))
227 (set! hash (bxor hash (bshr hash 11)))
228 (set! hash (+ hash (bshl hash 15)))
229 ; our result must always be odd to be relatively prime to the size of table (multiple of 2)
230 (bor
231 (bshr (bshl hash 1) 1) ; mask out any sign bit
232 1))))
234 (define church_compare_string_function (lambda (a b)
235 ; (call-c-extern printf "comparing strings %p %p '%s' '%s'
236 ;" a b a b)
237 (call-c-extern strcmp a b)))
239 (define church_default_hash_function (lambda (key1)
240 (let ((p 16777619)
241 (h 2166136261))
242 ; unroll 4 times for each byte in the key
243 (set! h (* p (bxor h (band key1 #xff))))
244 (set! key1 (bshr key1 8))
245 (set! h (* p (bxor h (band key1 #xff))))
246 (set! key1 (bshr key1 8))
247 (set! h (* p (bxor h (band key1 #xff))))
248 (set! key1 (bshr key1 8))
249 (set! h (* p (bxor h (band key1 #xff))))
251 (set! h (+ h (bshl h 13)))
252 (set! h (bxor h (bshr h 7)))
253 (set! h (+ h (bshl h 3)))
254 (set! h (bxor h (bshr h 17)))
255 (set! h (+ h (bshl h 5)))
256 (band h (band -1 (bnot (bshl 1 31))))
257 )))
260 (define church_default_second_hash_function (lambda (key1)
261 (let ((hash 0))
262 (do ((i 0 (+ i 1)))
263 (< i 4)
264 (set! hash (+ hash (band key1 #xff)))
265 (set! hash (+ hash (bshl hash 10)))
266 (set! hash (bxor hash (bshr hash 6)))
267 (set! key1 (bshr key1 8))
268 )
269 (set! hash (+ hash (bshl hash 3)))
270 (set! hash (bxor hash (bshr hash 11)))
271 (set! hash (+ hash (bshl hash 15)))
272 ; our result must always be odd to be relatively prime to the size of table (multiple of 2)
273 (bor
274 (band hash (band -1 (bnot (bshl 1 31))))
275 1))))
277 (define church_default_compare_function (lambda (a b)
278 (- a b)))
280 (external-function church_profiled_apply1)
281 (external-function church_profiled_apply2)
282 (external-function state-init-constant)
283 (external-function state-alloc-constant-vector)
285 (define church_equal_hash_function (lambda (obj)
286 (apply 'hash obj)))
287 (define church_equal_compare_function (lambda (a b)
288 (let ((result (apply 'equal? a b)))
289 (if (= result TAG_TRUE)
290 0
291 (- b a)))))
294 ; two key hashtable
296 ; new hash table
298 (define church-two-key-hashtable-allocate (lambda (size)
299 (church-assert (church-> size 0) "size must be greater than 0")
300 (if (not (= (band size (- size 1)) 0))
301 (let ((highest-bit (bsr size))
302 (new-size (bshl 1 (+ highest-bit 1))))
303 ; (call-c-extern printf "church-two-key-hashtable-allocate given size %lu, new size %lu
304 ;" size new-size)
305 (set! size new-size)))
306 ; size must be a power of 2
307 (church-assert (church-= (band size (- size 1)) 0) "hash table size must be a power of 2")
308 ; hash table is 8 words, plus 4 * size words
309 (let ((ht-alloc-size (+ (* 8 CHURCH_WORD_SIZE) (* size (* 4 CHURCH_WORD_SIZE))))
310 (ht (state-gc-alloc-and-zero ht-alloc-size)))
311 (if (= ht 0)
312 (state-abort "hash table alloc failed"))
313 ; clear memory
314 (call-c-extern memset ht 0 ht-alloc-size)
315 (set-hashtable-size! ht size)
316 (set-hashtable-count! ht 0)
317 ; 0 probes
318 ; (set-long! ht (* 7 CHURCH_WORD_SIZE) 0)
319 ht)))
321 ;(define church-two-key-hashtable-print-stats (lambda (ht)
322 ; (call-c-extern printf "two hash-table count %lu probes %lu, avg probes %lu
323 ;" (hashtable-count ht) (deref ht (* 7 CHURCH_WORD_SIZE)) (/ (deref ht (* 7 CHURCH_WORD_SIZE)) (hashtable-count ht) ))))
326 (define church-two-key-hashtable-has-keys? (lambda (ht key1 key2)
327 (state-abort "has keys")
328 (let ((hash-table (hashtable-table ht))
329 (hash-table-size (hashtable-size ht))
330 (hash-fn (hashtable-hash-function ht))
331 (comp-fn (hashtable-comp-function ht))
332 (h (bor (hash-fn key1) (hash-fn key2)))
333 (index (mod h hash-table-size))
334 ; align to 4 word boundary
335 (base-index (band (* index 4) -4))
336 (orig-index base-index))
337 (tagbody
338 check
339 (let ((lookup1 (deref hash-table (* base-index CHURCH_WORD_SIZE)))
340 (lookup2 (deref hash-table (* (+ base-index 1) CHURCH_WORD_SIZE))))
341 (if (and (= lookup1 0)
342 (= lookup2 0))
343 (return TAG_NIL))
344 (if (and (= (comp-fn lookup1 key1) 0)
345 (= (comp-fn lookup2 key2) 0))
346 (return TAG_TRUE)))
347 (set! base-index (+ base-index 4))
348 ; check if we have wrappend around the top of the table
349 (if (= base-index (* hash-table-size 4))
350 (set! base-index 0))
351 (go check)))))
353 ; return a pointer to the result
354 ; 0 if not found
355 (define church-two-key-hashtable-lookup-keys (lambda (ht key1 key2)
356 (let ((hash-table (hashtable-table ht))
357 (hash-table-size (hashtable-size ht))
358 (h (church-two-key-hash-function key1 key2))
359 (index (* (mod h hash-table-size) 4))
360 (probe-step (church-two-key-second-hash-function key1 key2)))
361 (tagbody
362 check
363 ; note that the first key cannot be a fixnum
364 (let ((l1 (deref hash-table (* index CHURCH_WORD_SIZE)) 0))
365 (if (= l1 0)
366 (return 0))
367 (if (= l1 key1)
368 (if (= (deref hash-table (* (+ index 1) CHURCH_WORD_SIZE)) key2)
369 (return (+ hash-table (* (+ index 2) CHURCH_WORD_SIZE))))))
370 (set! h (+ h probe-step))
371 ; mask out high bit
372 (set! h (band h (band -1 (bnot (bshl 1 31)))))
373 (set! index (* (mod h hash-table-size) 4))
374 (go check)))
375 (state-abort "failed find hash entry")))
378 (define church-two-key-hashtable-add-or-replace (lambda (ht key1 key2 value1 value2)
379 (let ((new-count (church-two-key-hashtable-add-or-replace-helper ht key1 key2 value1 value2)))
380 (if (> new-count 0)
381 (begin
382 (set-hashtable-count! ht (+ (hashtable-count ht) 1))
383 ; (call-c-extern printf "after add two-key ht %p count %lu size %lu
384 ;" ht (hashtable-count ht) (hashtable-size ht))
385 ; check if we should grow
386 (if (> (/ (* (hashtable-count ht) 100) (hashtable-size ht)) 60)
387 (let ((newsize (* (hashtable-size ht) 2))
388 (new-ht (church-two-key-hashtable-allocate newsize)))
389 ;(call-c-extern printf "growing two key ht %p count %lu size %lu
390 ;" ht (hashtable-count ht) newsize)
391 ; copy the old functions into the new table
392 (church-hashtable-set-functions new-ht (hashtable-hash-function1 ht) (hashtable-hash-function2 ht) (hashtable-comp-function ht))
393 (church-two-key-hashtable-rehash-from-old-table ht new-ht)
394 (return new-ht))
395 ht))
396 ht))))
398 (define church-two-key-hashtable-add-or-replace-helper (lambda (ht key1 key2 value1 value2)
399 (let ((hash-table (hashtable-table ht))
400 (hash-table-size (hashtable-size ht))
401 (h (church-two-key-hash-function key1 key2))
402 (index (* (mod h hash-table-size) 4))
403 (probe-step (church-two-key-second-hash-function key1 key2)))
404 ; (call-c-extern printf "add key1 %p key2 %p, h %p index %lu at index %lu (%p)
405 ;" key1 key2 h index index index)
406 (tagbody
407 check
409 (let ((l1 (deref hash-table (* index CHURCH_WORD_SIZE)) 0))
410 (if (= l1 0)
411 (if (= (deref hash-table (* (+ index 1) CHURCH_WORD_SIZE)) 0)
412 (begin
413 (set-long! hash-table (* index CHURCH_WORD_SIZE) key1)
414 (set-long! hash-table (* (+ index 1) CHURCH_WORD_SIZE) key2)
415 (set-long! hash-table (* (+ index 2) CHURCH_WORD_SIZE) value1)
416 (set-long! hash-table (* (+ index 3) CHURCH_WORD_SIZE) value2)
417 (return 1) ; new entry
418 )))
419 (if (= l1 key1)
420 (if (= (deref hash-table (* (+ index 1) CHURCH_WORD_SIZE)) key2)
421 (begin
422 (set-long! hash-table (* index CHURCH_WORD_SIZE) key1)
423 (set-long! hash-table (* (+ index 1) CHURCH_WORD_SIZE) key2)
424 (set-long! hash-table (* (+ index 2) CHURCH_WORD_SIZE) value1)
425 (set-long! hash-table (* (+ index 3) CHURCH_WORD_SIZE) value2)
426 (return 0) ; new entry
427 ))))
428 ; (call-c-extern printf "add key1 %p key2 %p, h %p index %lu at index %lu (%p) PROBING
429 ;" key1 key2 h index index index)
430 ; count the number of probes
431 ; (set-long! ht (* 7 CHURCH_WORD_SIZE) (+ 1 (deref ht (* 7 CHURCH_WORD_SIZE))))
432 (set! h (+ h probe-step))
433 ; mask out high bit
434 (set! h (band h (band -1 (bnot (bshl 1 31)))))
435 (set! index (mod h hash-table-size))
436 (go check)))
437 (state-abort "failed find hash entry")))
439 (define church-two-key-hashtable-rehash-from-old-table (lambda (ht new-ht)
440 (let ((hash-table (hashtable-table ht))
441 (hash-table-size (hashtable-size ht)))
442 (let ((counter 0)
443 (base-index 0))
444 (tagbody
445 check
446 (if (< counter hash-table-size)
447 (let ((key1 (deref hash-table (* base-index CHURCH_WORD_SIZE)))
448 (key2 (deref hash-table (* (+ base-index 1) CHURCH_WORD_SIZE)))
449 (value1 (deref hash-table (* (+ base-index 2) CHURCH_WORD_SIZE) ))
450 (value2 (deref hash-table (* (+ base-index 3) CHURCH_WORD_SIZE) )) (value1 (deref hash-table (* (+ base-index 2) CHURCH_WORD_SIZE) )))
451 (if (not (and (= key1 0)
452 (= key2 0)))
453 (begin
454 (church-two-key-hashtable-add-or-replace-helper new-ht key1 key2 value1 value2)
455 (set-hashtable-count! new-ht (+ (hashtable-count new-ht) 1)))
456 )
457 (set! base-index (+ base-index 4)))
458 (go end))
459 (set! counter (+ counter 1))
460 (go check)
461 end)))))
463 ; Modified FNV from Pluto Scarab, http://home.comcast.net/~bretm/hash/6.html
465 (define church-two-key-hash-function (lambda (key1 key2)
466 (let ((p 16777619)
467 (h 2166136261))
468 (do ((i 0 (+ i 1)))
469 (< i 4)
470 (set! h (* p (bxor h (band key1 #xff))))
471 ; (call-c-extern printf "hash1 %p key1 %p
472 ;" h key1)
473 (set! key1 (bshr key1 8)))
474 (do ((i 0 (+ i 1)))
475 (< i 4)
476 (set! h (* p (bxor h (band key2 #xff))))
477 ; (call-c-extern printf "hash1 %p key2 %p
478 ;" h key2)
479 (set! key2 (bshr key2 8)))
480 (set! h (+ h (bshl h 13)))
481 (set! h (bxor h (bshr h 7)))
482 (set! h (+ h (bshl h 3)))
483 (set! h (bxor h (bshr h 17)))
484 (set! h (+ h (bshl h 5)))
485 (band h (band -1 (bnot (bshl 1 31)))) )))
488 ; Jenkins hash function from wikipedia / dr. dobbs
490 (define church-two-key-second-hash-function (lambda (key1 key2)
491 (let ((hash 0))
492 (do ((i 0 (+ i 1)))
493 (< i 4)
494 (set! hash (+ hash (band key1 #xff)))
495 (set! hash (+ hash (bshl hash 10)))
496 (set! hash (bxor hash (bshr hash 6)))
497 (set! key1 (bshr key1 8))
498 )
499 (do ((i 0 (+ i 1)))
500 (< i 4)
501 (set! hash (+ hash (band key2 #xff)))
502 (set! hash (+ hash (bshl hash 10)))
503 (set! hash (bxor hash (bshr hash 6)))
504 (set! key2 (bshr key1 8))
505 )
506 (set! hash (+ hash (bshl hash 3)))
507 (set! hash (bxor hash (bshr hash 11)))
508 (set! hash (+ hash (bshl hash 15)))
509 ; our result must always be odd to be relatively prime to the size of table (multiple of 2)
510 (bor
511 (band hash (band -1 (bnot (bshl 1 31))))
512 1))))
