special cases for small hashes in unsafe_scheme_hash_tree_iterate_*

The iterator saves the return points in a list. For small immutable hashes,
encode the values in the list in the bits of a fixnum to avoid allocations.
This commit is contained in:
Gustavo Massaccesi 2016-02-13 19:43:10 -03:00
parent 7d90b27524
commit 5ef3a53002
4 changed files with 204 additions and 64 deletions

View File

@ -212,6 +212,27 @@
(test-hash-iters-generic lst3 lst4) (test-hash-iters-generic lst3 lst4)
(test-hash-iters-specific 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 () (let ()

View File

@ -2738,80 +2738,189 @@ Scheme_Object *scheme_hash_tree_next_pos(Scheme_Hash_Tree *tree, mzlonglong pos)
# define HAMT_TRAVERSE_NEXT(i) ((i)+1) # define HAMT_TRAVERSE_NEXT(i) ((i)+1)
#endif #endif
#define mzHAMT_MAX_INDEX_LEVEL 4 /* For the compressed form of the index */
/* instead of returning a pos, these unsafe iteration ops */ /* instead of returning a pos, these unsafe iteration ops */
/* return a view into the tree consisting of a: */ /* return a view into the tree consisting of a: */
/* - subtree */ /* - subtree */
/* - subtree index */ /* - subtree index */
/* - stack of parent subtrees and indices */ /* - 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. */ /* This speeds up performance of immutable hash table iteration. */
/* These unsafe ops currently do not support REVERSE_HASH_TABLE_ORDER */ /* These unsafe ops currently do not support REVERSE_HASH_TABLE_ORDER */
/* to avoid unneeded popcount computations */ /* to avoid unneeded popcount computations */
Scheme_Object *scheme_unsafe_hash_tree_start(Scheme_Hash_Tree *ht) Scheme_Object *scheme_unsafe_hash_tree_start(Scheme_Hash_Tree *ht)
{ {
Scheme_Object *stack = scheme_null; Scheme_Object *stack;
int i; int j, i, i_n[mzHAMT_MAX_INDEX_LEVEL], level;
Scheme_Hash_Tree *ht_n[mzHAMT_MAX_INDEX_LEVEL];
ht = resolve_placeholder(ht); ht = resolve_placeholder(ht);
if (0 == ht->count) if (!ht->count)
return scheme_false; return scheme_false;
i = hamt_popcount(ht->bitmap)-1; i = hamt_popcount(ht->bitmap)-1;
level = 0;
while (1) {
if (HASHTR_SUBTREEP(ht->els[i]) while ((HASHTR_SUBTREEP(ht->els[i])
|| HASHTR_COLLISIONP(ht->els[i])) { || HASHTR_COLLISIONP(ht->els[i]))) {
stack = /* go down tree but save return point */ /* go down tree but save return point */
scheme_make_pair((Scheme_Object *)ht, if (level == -1) {
scheme_make_pair(scheme_make_integer(i), stack = scheme_make_pair((Scheme_Object *)ht,
stack)); scheme_make_pair(scheme_make_integer(i),
ht = (Scheme_Hash_Tree *)ht->els[i]; stack));
i = hamt_popcount(ht->bitmap)-1; } else if (level < mzHAMT_MAX_INDEX_LEVEL) {
ht_n[level] = ht;
i_n[level] = i;
level++;
} else { } else {
return scheme_make_pair((Scheme_Object *)ht, stack = scheme_null;
scheme_make_pair(scheme_make_integer(i), for (j = 0; j < mzHAMT_MAX_INDEX_LEVEL; j++) {
stack)); 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<<mzHAMT_LOG_WORD_SIZE) + i;
for (j = level-1; j >= 0 ; j--) {
i = (i<<mzHAMT_LOG_WORD_SIZE) + i_n[j];
}
return scheme_make_integer(i);
} }
return NULL;
} }
/* args is a (cons subtree (cons subtree-index stack-of-parents)) */ /* args is a (cons subtree (cons subtree-index stack-of-parents))
Scheme_Object *scheme_unsafe_hash_tree_next(Scheme_Object *args) or the comppressed representation as a fixnum */
XFORM_NONGCING void scheme_unsafe_hash_tree_subtree(Scheme_Object *obj, Scheme_Object *args,
Scheme_Hash_Tree **_subtree, int *_i)
{ {
Scheme_Hash_Tree *ht = (Scheme_Hash_Tree *)SCHEME_CAR(args); Scheme_Hash_Tree *subtree;
int i = SCHEME_INT_VAL(SCHEME_CADR(args)); int i;
Scheme_Object *stack = SCHEME_CDDR(args);
if (SCHEME_PAIRP(args)) {
subtree = (Scheme_Hash_Tree *)SCHEME_CAR(args);
i = SCHEME_INT_VAL(SCHEME_CADR(args));
} else {
if (SCHEME_NP_CHAPERONEP(obj))
subtree = (Scheme_Hash_Tree *)SCHEME_CHAPERONE_VAL(obj);
else
subtree = (Scheme_Hash_Tree *)obj;
i = SCHEME_INT_VAL(args);
while (i >= (1<<(2*mzHAMT_LOG_WORD_SIZE))) {
subtree = (Scheme_Hash_Tree *)subtree->els[i & ((1<<mzHAMT_LOG_WORD_SIZE)-1)];
i = i >> mzHAMT_LOG_WORD_SIZE;
}
i = i & ((1<<mzHAMT_LOG_WORD_SIZE)-1);
}
*_subtree = subtree;
*_i =i;
}
/* args is a (cons subtree (cons subtree-index stack-of-parents))
or the comppressed representation as a fixnum */
Scheme_Object *scheme_unsafe_hash_tree_next(Scheme_Hash_Tree *ht, Scheme_Object *args)
{
Scheme_Object *stack;
int j, i, i_n[mzHAMT_MAX_INDEX_LEVEL], level;
Scheme_Hash_Tree *ht_n[mzHAMT_MAX_INDEX_LEVEL];
if (SCHEME_PAIRP(args)) {
ht = (Scheme_Hash_Tree *)SCHEME_CAR(args);
i = SCHEME_INT_VAL(SCHEME_CADR(args));
stack = SCHEME_CDDR(args);
level = -1; /* -1 = too big */
} else {
i = SCHEME_INT_VAL(args);
level = 0;
while (i >= (1<<(2*mzHAMT_LOG_WORD_SIZE))) {
ht_n[level] = ht;
i_n[level] = i & ((1<<mzHAMT_LOG_WORD_SIZE)-1);
ht = (Scheme_Hash_Tree *)ht->els[i_n[level]];
i = i >> mzHAMT_LOG_WORD_SIZE;
level++;
}
i = i & ((1<<mzHAMT_LOG_WORD_SIZE)-1);
}
/* ht = resolve_placeholder(ht); /\* only check this in iterate-first *\/ */ /* ht = resolve_placeholder(ht); /\* only check this in iterate-first *\/ */
while(1) { while (1) {
if (!i) { /* pop up the tree */ if (!i) { /* pop up the tree */
if (SCHEME_NULLP(stack)) { if (level == -1) {
return scheme_false; ht = (Scheme_Hash_Tree *)SCHEME_CAR(stack);
i = SCHEME_INT_VAL(SCHEME_CADR(stack));
stack = SCHEME_CDDR(stack);
if (SCHEME_NULLP(stack))
level = 0;
} else if (!level) {
return scheme_false;
} else { } else {
ht = (Scheme_Hash_Tree *)SCHEME_CAR(stack); level--;
i = SCHEME_INT_VAL(SCHEME_CADR(stack)); ht = ht_n[level];
stack = SCHEME_CDDR(stack); i = i_n[level];
} }
} else { } else { /* go to next node */
i -= 1; i--;
if (HASHTR_SUBTREEP(ht->els[i]) if (!(HASHTR_SUBTREEP(ht->els[i])
|| HASHTR_COLLISIONP(ht->els[i])) { || HASHTR_COLLISIONP(ht->els[i]))) {
stack = /* go down tree but save return point */ if (level == -1) {
scheme_make_pair((Scheme_Object *)ht, stack = scheme_make_pair((Scheme_Object *)ht,
scheme_make_pair(scheme_make_integer(i), scheme_make_pair(scheme_make_integer(i),
stack)); stack));
ht = (Scheme_Hash_Tree *)ht->els[i]; return stack;
i = hamt_popcount(ht->bitmap); } else {
} else { i = (1<<mzHAMT_LOG_WORD_SIZE) + i;
return scheme_make_pair((Scheme_Object *)ht, for (j = level-1; j >= 0 ; j--) {
scheme_make_pair(scheme_make_integer(i), i = (i<<mzHAMT_LOG_WORD_SIZE) + i_n[j];
stack)); }
return scheme_make_integer(i);
}
} else { /* 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 {
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);
} }
} }
} }
return NULL;
} }
XFORM_NONGCING static void hamt_at_index(Scheme_Hash_Tree *ht, mzlonglong pos, XFORM_NONGCING static void hamt_at_index(Scheme_Hash_Tree *ht, mzlonglong pos,

View File

@ -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); 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 *unsafe_scheme_hash_tree_iterate_key(int argc, Scheme_Object *argv[])
{ {
Scheme_Object *obj = argv[0], *args = argv[1], *key; Scheme_Object *obj = argv[0], *args = argv[1], *key;
Scheme_Hash_Tree *subtree = (Scheme_Hash_Tree *)SCHEME_CAR(args); Scheme_Hash_Tree *subtree;
int i = SCHEME_INT_VAL(SCHEME_CADR(args)); int i;
scheme_unsafe_hash_tree_subtree(obj, args, &subtree, &i);
key = subtree->els[i]; key = subtree->els[i];
if (SCHEME_NP_CHAPERONEP(obj)) 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 else
return key; return key;
} }
Scheme_Object *unsafe_scheme_hash_tree_iterate_value(int argc, Scheme_Object *argv[]) Scheme_Object *unsafe_scheme_hash_tree_iterate_value(int argc, Scheme_Object *argv[])
{ {
Scheme_Object *obj = argv[0], *args = argv[1]; Scheme_Object *obj = argv[0], *args = argv[1];
Scheme_Hash_Tree *subtree = (Scheme_Hash_Tree *)SCHEME_CAR(args); Scheme_Hash_Tree *subtree;
int i = SCHEME_INT_VAL(SCHEME_CADR(args)); int i;
scheme_unsafe_hash_tree_subtree(obj, args, &subtree, &i);
if (SCHEME_NP_CHAPERONEP(obj)) { if (SCHEME_NP_CHAPERONEP(obj)) {
Scheme_Object *chap_key, *chap_val; Scheme_Object *chap_key, *chap_val;
chaperone_hash_key_value("unsafe-weak-hash-iterate-value", chaperone_hash_key_value("unsafe-immutable-hash-iterate-value",
obj, subtree->els[i], &chap_key, &chap_val, 0); obj, subtree->els[i], &chap_key, &chap_val, 0);
return chap_val; return chap_val;
} else { } else {
int popcount; 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 *unsafe_scheme_hash_tree_iterate_pair(int argc, Scheme_Object *argv[])
{ {
Scheme_Object *obj = argv[0], *args = argv[1]; Scheme_Object *obj = argv[0], *args = argv[1], *key;
Scheme_Hash_Tree *subtree = (Scheme_Hash_Tree *)SCHEME_CAR(args); Scheme_Hash_Tree *subtree;
int i = SCHEME_INT_VAL(SCHEME_CADR(args)); int i;
Scheme_Object *key = subtree->els[i];
scheme_unsafe_hash_tree_subtree(obj, args, &subtree, &i);
key = subtree->els[i];
if (SCHEME_NP_CHAPERONEP(obj)) { if (SCHEME_NP_CHAPERONEP(obj)) {
Scheme_Object *chap_key, *chap_val; Scheme_Object *chap_key, *chap_val;
chaperone_hash_key_value("unsafe-weak-hash-iterate-pair", chaperone_hash_key_value("unsafe-immutable-hash-iterate-pair",
obj, subtree->els[i], &chap_key, &chap_val, 0); obj, subtree->els[i], &chap_key, &chap_val, 0);
return scheme_make_pair(chap_key, chap_val); return scheme_make_pair(chap_key, chap_val);
} else { } else {
Scheme_Object *val; 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 *unsafe_scheme_hash_tree_iterate_key_value(int argc, Scheme_Object *argv[])
{ {
Scheme_Object *obj = argv[0], *args = argv[1], *res[2]; Scheme_Object *obj = argv[0], *args = argv[1], *key, *res[2];
Scheme_Hash_Tree *subtree = (Scheme_Hash_Tree *)SCHEME_CAR(args); Scheme_Hash_Tree *subtree;
int i = SCHEME_INT_VAL(SCHEME_CADR(args)); int i;
Scheme_Object *key = subtree->els[i];
scheme_unsafe_hash_tree_subtree(obj, args, &subtree, &i);
key = subtree->els[i];
if (SCHEME_NP_CHAPERONEP(obj)) { if (SCHEME_NP_CHAPERONEP(obj)) {
chaperone_hash_key_value("unsafe-weak-hash-iterate-pair", chaperone_hash_key_value("unsafe-immutable-hash-iterate-pair",
obj, subtree->els[i], &res[0], &res[1], 0); obj, subtree->els[i], &res[0], &res[1], 0);
} else { } else {
Scheme_Object *val; Scheme_Object *val;
int popcount; int popcount;

View File

@ -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 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); 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_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); 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); 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); int scheme_hash_tree_equal(Scheme_Hash_Tree *t1, Scheme_Hash_Tree *t2);