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-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 ()

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)
#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<<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)) */
Scheme_Object *scheme_unsafe_hash_tree_next(Scheme_Object *args)
/* args is a (cons subtree (cons subtree-index stack-of-parents))
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);
int i = SCHEME_INT_VAL(SCHEME_CADR(args));
Scheme_Object *stack = SCHEME_CDDR(args);
Scheme_Hash_Tree *subtree;
int i;
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 *\/ */
while(1) {
while (1) {
if (!i) { /* pop up the tree */
if (SCHEME_NULLP(stack)) {
return scheme_false;
if (level == -1) {
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 {
ht = (Scheme_Hash_Tree *)SCHEME_CAR(stack);
i = SCHEME_INT_VAL(SCHEME_CADR(stack));
stack = SCHEME_CDDR(stack);
level--;
ht = ht_n[level];
i = i_n[level];
}
} else {
i -= 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);
} 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<<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);
}
} 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,

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);
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;

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 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);