diff --git a/pkgs/racket-test-core/tests/racket/hash.rktl b/pkgs/racket-test-core/tests/racket/hash.rktl index 41c46ad6d5..d917d2b934 100644 --- a/pkgs/racket-test-core/tests/racket/hash.rktl +++ b/pkgs/racket-test-core/tests/racket/hash.rktl @@ -212,6 +212,27 @@ (test-hash-iters-generic lst3 lst4) (test-hash-iters-specific lst3 lst4)) + +;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Use keys that are a multile of a power of 2 to +; get "almost" collisions that force the hash table +; to use a deeper tree. + +(let () + (define vals (for/list ([j (in-range 100)]) (add1 j))) + (define sum-vals (for/sum ([v (in-list vals)]) v)) + (for ([shift (in-range 150)]) + (define keys (for/list ([j (in-range 100)]) + (arithmetic-shift j shift))) + ; test first the weak table to ensure the keys are not collected + (define ht/weak (make-weak-hash (map cons keys vals))) + (define sum-ht/weak (for/sum ([v (in-weak-hash-values ht/weak)]) v)) + (define ht/mut (make-hash (map cons keys vals))) + (define sum-ht/mut (for/sum ([v (in-mutable-hash-values ht/mut)]) v)) + (define ht/immut (make-immutable-hash (map cons keys vals))) + (define sum-ht/immut (for/sum ([v (in-immutable-hash-values ht/immut)]) v)) + (test #t = sum-vals sum-ht/weak sum-ht/mut sum-ht/immut))) + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (let () diff --git a/racket/src/racket/src/hash.c b/racket/src/racket/src/hash.c index 5ca9112fad..7955075b38 100644 --- a/racket/src/racket/src/hash.c +++ b/racket/src/racket/src/hash.c @@ -2738,80 +2738,189 @@ Scheme_Object *scheme_hash_tree_next_pos(Scheme_Hash_Tree *tree, mzlonglong pos) # define HAMT_TRAVERSE_NEXT(i) ((i)+1) #endif +#define mzHAMT_MAX_INDEX_LEVEL 4 /* For the compressed form of the index */ + /* instead of returning a pos, these unsafe iteration ops */ /* return a view into the tree consisting of a: */ /* - subtree */ /* - subtree index */ /* - stack of parent subtrees and indices */ +/* For small hashes, it uses a compressed representation as fixnums */ +/* - (#h i0) --> (+ (<< 1 5) i0) */ +/* - (#h i1 #h i0) --> (+ (<< 1 10) (<< i1 5) i0) */ +/* - (#h i2 #h i1 #h i0) --> (+ (<< 1 15) (<< i2 10) (<< i1 5) i0) */ +/* - ... */ /* This speeds up performance of immutable hash table iteration. */ /* These unsafe ops currently do not support REVERSE_HASH_TABLE_ORDER */ /* to avoid unneeded popcount computations */ Scheme_Object *scheme_unsafe_hash_tree_start(Scheme_Hash_Tree *ht) { - Scheme_Object *stack = scheme_null; - int i; + Scheme_Object *stack; + int j, i, i_n[mzHAMT_MAX_INDEX_LEVEL], level; + Scheme_Hash_Tree *ht_n[mzHAMT_MAX_INDEX_LEVEL]; ht = resolve_placeholder(ht); - if (0 == ht->count) + if (!ht->count) return scheme_false; i = hamt_popcount(ht->bitmap)-1; - - while (1) { - if (HASHTR_SUBTREEP(ht->els[i]) - || HASHTR_COLLISIONP(ht->els[i])) { - stack = /* go down tree but save return point */ - scheme_make_pair((Scheme_Object *)ht, - scheme_make_pair(scheme_make_integer(i), - stack)); - ht = (Scheme_Hash_Tree *)ht->els[i]; - i = hamt_popcount(ht->bitmap)-1; + level = 0; + + while ((HASHTR_SUBTREEP(ht->els[i]) + || HASHTR_COLLISIONP(ht->els[i]))) { + /* go down tree but save return point */ + if (level == -1) { + stack = scheme_make_pair((Scheme_Object *)ht, + scheme_make_pair(scheme_make_integer(i), + stack)); + } else if (level < mzHAMT_MAX_INDEX_LEVEL) { + ht_n[level] = ht; + i_n[level] = i; + level++; } else { - return scheme_make_pair((Scheme_Object *)ht, - scheme_make_pair(scheme_make_integer(i), - stack)); + stack = scheme_null; + for (j = 0; j < mzHAMT_MAX_INDEX_LEVEL; j++) { + stack = scheme_make_pair((Scheme_Object *)ht_n[j], + scheme_make_pair(scheme_make_integer(i_n[j]), + stack)); + } + stack = scheme_make_pair((Scheme_Object *)ht, + scheme_make_pair(scheme_make_integer(i), + stack)); + level = -1; } + ht = (Scheme_Hash_Tree *)ht->els[i]; + i = hamt_popcount(ht->bitmap)-1; + } + + if (level == -1) { + stack = scheme_make_pair((Scheme_Object *)ht, + scheme_make_pair(scheme_make_integer(i), + stack)); + return stack; + } else { + i = (1<= 0 ; j--) { + i = (i<= (1<<(2*mzHAMT_LOG_WORD_SIZE))) { + subtree = (Scheme_Hash_Tree *)subtree->els[i & ((1<> mzHAMT_LOG_WORD_SIZE; + } + i = i & ((1<= (1<<(2*mzHAMT_LOG_WORD_SIZE))) { + ht_n[level] = ht; + i_n[level] = i & ((1<els[i_n[level]]; + i = i >> mzHAMT_LOG_WORD_SIZE; + level++; + } + i = i & ((1<els[i]) - || HASHTR_COLLISIONP(ht->els[i])) { - stack = /* go down tree but save return point */ - scheme_make_pair((Scheme_Object *)ht, - scheme_make_pair(scheme_make_integer(i), - stack)); - ht = (Scheme_Hash_Tree *)ht->els[i]; - i = hamt_popcount(ht->bitmap); - } else { - return scheme_make_pair((Scheme_Object *)ht, - scheme_make_pair(scheme_make_integer(i), - stack)); + } else { /* go to next node */ + i--; + if (!(HASHTR_SUBTREEP(ht->els[i]) + || HASHTR_COLLISIONP(ht->els[i]))) { + if (level == -1) { + stack = scheme_make_pair((Scheme_Object *)ht, + scheme_make_pair(scheme_make_integer(i), + stack)); + return stack; + } else { + i = (1<= 0 ; j--) { + i = (i<els[i]; + i = hamt_popcount(ht->bitmap); } } } - return NULL; } XFORM_NONGCING static void hamt_at_index(Scheme_Hash_Tree *ht, mzlonglong pos, diff --git a/racket/src/racket/src/list.c b/racket/src/racket/src/list.c index 477a7fd843..3a8b1cb698 100644 --- a/racket/src/racket/src/list.c +++ b/racket/src/racket/src/list.c @@ -4118,30 +4118,34 @@ Scheme_Object *unsafe_scheme_hash_tree_iterate_next(int argc, Scheme_Object *arg if (SCHEME_NP_CHAPERONEP(o)) o = SCHEME_CHAPERONE_VAL(o); - return scheme_unsafe_hash_tree_next(argv[1]); + return scheme_unsafe_hash_tree_next((Scheme_Hash_Tree *)o, argv[1]); } Scheme_Object *unsafe_scheme_hash_tree_iterate_key(int argc, Scheme_Object *argv[]) { Scheme_Object *obj = argv[0], *args = argv[1], *key; - Scheme_Hash_Tree *subtree = (Scheme_Hash_Tree *)SCHEME_CAR(args); - int i = SCHEME_INT_VAL(SCHEME_CADR(args)); + Scheme_Hash_Tree *subtree; + int i; + + scheme_unsafe_hash_tree_subtree(obj, args, &subtree, &i); key = subtree->els[i]; if (SCHEME_NP_CHAPERONEP(obj)) - return chaperone_hash_key("unsafe-weak-hash-iterate-key", obj, key); + return chaperone_hash_key("unsafe-immutable-hash-iterate-key", obj, key); else return key; } Scheme_Object *unsafe_scheme_hash_tree_iterate_value(int argc, Scheme_Object *argv[]) { Scheme_Object *obj = argv[0], *args = argv[1]; - Scheme_Hash_Tree *subtree = (Scheme_Hash_Tree *)SCHEME_CAR(args); - int i = SCHEME_INT_VAL(SCHEME_CADR(args)); + Scheme_Hash_Tree *subtree; + int i; + + scheme_unsafe_hash_tree_subtree(obj, args, &subtree, &i); if (SCHEME_NP_CHAPERONEP(obj)) { Scheme_Object *chap_key, *chap_val; - chaperone_hash_key_value("unsafe-weak-hash-iterate-value", - obj, subtree->els[i], &chap_key, &chap_val, 0); + chaperone_hash_key_value("unsafe-immutable-hash-iterate-value", + obj, subtree->els[i], &chap_key, &chap_val, 0); return chap_val; } else { int popcount; @@ -4151,15 +4155,17 @@ Scheme_Object *unsafe_scheme_hash_tree_iterate_value(int argc, Scheme_Object *ar } Scheme_Object *unsafe_scheme_hash_tree_iterate_pair(int argc, Scheme_Object *argv[]) { - Scheme_Object *obj = argv[0], *args = argv[1]; - Scheme_Hash_Tree *subtree = (Scheme_Hash_Tree *)SCHEME_CAR(args); - int i = SCHEME_INT_VAL(SCHEME_CADR(args)); - Scheme_Object *key = subtree->els[i]; + Scheme_Object *obj = argv[0], *args = argv[1], *key; + Scheme_Hash_Tree *subtree; + int i; + + scheme_unsafe_hash_tree_subtree(obj, args, &subtree, &i); + key = subtree->els[i]; if (SCHEME_NP_CHAPERONEP(obj)) { Scheme_Object *chap_key, *chap_val; - chaperone_hash_key_value("unsafe-weak-hash-iterate-pair", - obj, subtree->els[i], &chap_key, &chap_val, 0); + chaperone_hash_key_value("unsafe-immutable-hash-iterate-pair", + obj, subtree->els[i], &chap_key, &chap_val, 0); return scheme_make_pair(chap_key, chap_val); } else { Scheme_Object *val; @@ -4171,14 +4177,16 @@ Scheme_Object *unsafe_scheme_hash_tree_iterate_pair(int argc, Scheme_Object *arg } Scheme_Object *unsafe_scheme_hash_tree_iterate_key_value(int argc, Scheme_Object *argv[]) { - Scheme_Object *obj = argv[0], *args = argv[1], *res[2]; - Scheme_Hash_Tree *subtree = (Scheme_Hash_Tree *)SCHEME_CAR(args); - int i = SCHEME_INT_VAL(SCHEME_CADR(args)); - Scheme_Object *key = subtree->els[i]; + Scheme_Object *obj = argv[0], *args = argv[1], *key, *res[2]; + Scheme_Hash_Tree *subtree; + int i; + + scheme_unsafe_hash_tree_subtree(obj, args, &subtree, &i); + key = subtree->els[i]; if (SCHEME_NP_CHAPERONEP(obj)) { - chaperone_hash_key_value("unsafe-weak-hash-iterate-pair", - obj, subtree->els[i], &res[0], &res[1], 0); + chaperone_hash_key_value("unsafe-immutable-hash-iterate-pair", + obj, subtree->els[i], &res[0], &res[1], 0); } else { Scheme_Object *val; int popcount; diff --git a/racket/src/racket/src/schpriv.h b/racket/src/racket/src/schpriv.h index adc01b96d7..39c25051a8 100644 --- a/racket/src/racket/src/schpriv.h +++ b/racket/src/racket/src/schpriv.h @@ -966,7 +966,9 @@ Scheme_Object *scheme_hash_tree_get(Scheme_Hash_Tree *tree, Scheme_Object *key); XFORM_NONGCING Scheme_Object *scheme_eq_hash_tree_get(Scheme_Hash_Tree *tree, Scheme_Object *key); XFORM_NONGCING mzlonglong scheme_hash_tree_next(Scheme_Hash_Tree *tree, mzlonglong pos); Scheme_Object *scheme_unsafe_hash_tree_start(Scheme_Hash_Tree *ht); -Scheme_Object *scheme_unsafe_hash_tree_next(Scheme_Object *args); +XFORM_NONGCING void scheme_unsafe_hash_tree_subtree(Scheme_Object *obj, Scheme_Object *args, + Scheme_Hash_Tree **_subtree, int *_i); +Scheme_Object *scheme_unsafe_hash_tree_next(Scheme_Hash_Tree *ht, Scheme_Object *args); Scheme_Object *scheme_hash_tree_next_pos(Scheme_Hash_Tree *tree, mzlonglong pos); XFORM_NONGCING int scheme_hash_tree_index(Scheme_Hash_Tree *tree, mzlonglong pos, Scheme_Object **_key, Scheme_Object **_val); int scheme_hash_tree_equal(Scheme_Hash_Tree *t1, Scheme_Hash_Tree *t2);