From 732ad076c182b3ad706cd62f8a6694f921e1bb7c Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Tue, 5 Jul 2011 23:47:32 -0400 Subject: [PATCH] rculfhash: implement real hash function Signed-off-by: Mathieu Desnoyers --- rculfhash.c | 20 +++--- tests/test_urcu_hash.c | 155 +++++++++++++++++++++++++++++++++++++---- urcu/rculfhash.h | 15 ++-- 3 files changed, 164 insertions(+), 26 deletions(-) diff --git a/rculfhash.c b/rculfhash.c index a599961..497a9ea 100644 --- a/rculfhash.c +++ b/rculfhash.c @@ -46,7 +46,7 @@ #define dbg_printf(args...) #endif -#define BUCKET_SIZE_RESIZE_THRESHOLD 8 +#define BUCKET_SIZE_RESIZE_THRESHOLD 4 #ifndef max #define max(a, b) ((a) > (b) ? (a) : (b)) @@ -62,7 +62,8 @@ struct rcu_table { struct rcu_ht { struct rcu_table *t; /* shared */ ht_hash_fct hash_fct; - void *hashseed; + ht_compare_fct compare_fct; + unsigned long hash_seed; pthread_mutex_t resize_mutex; /* resize mutex: add/del mutex */ void (*ht_call_rcu)(struct rcu_head *head, void (*func)(struct rcu_head *head)); @@ -314,7 +315,8 @@ void init_table(struct rcu_ht *ht, struct rcu_table *t, } struct rcu_ht *ht_new(ht_hash_fct hash_fct, - void *hashseed, + ht_compare_fct compare_fct, + unsigned long hash_seed, unsigned long init_size, void (*ht_call_rcu)(struct rcu_head *head, void (*func)(struct rcu_head *head))) @@ -323,7 +325,8 @@ struct rcu_ht *ht_new(ht_hash_fct hash_fct, ht = calloc(1, sizeof(struct rcu_ht)); ht->hash_fct = hash_fct; - ht->hashseed = hashseed; + ht->compare_fct = compare_fct; + ht->hash_seed = hash_seed; ht->ht_call_rcu = ht_call_rcu; /* this mutex should not nest in read-side C.S. */ pthread_mutex_init(&ht->resize_mutex, NULL); @@ -336,13 +339,13 @@ struct rcu_ht *ht_new(ht_hash_fct hash_fct, return ht; } -struct rcu_ht_node *ht_lookup(struct rcu_ht *ht, void *key) +struct rcu_ht_node *ht_lookup(struct rcu_ht *ht, void *key, size_t key_len) { struct rcu_table *t; struct rcu_ht_node *node; unsigned long hash, reverse_hash; - hash = ht->hash_fct(ht->hashseed, key); + hash = ht->hash_fct(key, key_len, ht->hash_seed); reverse_hash = bit_reverse_ulong(hash); t = rcu_dereference(ht->t); @@ -354,7 +357,7 @@ struct rcu_ht_node *ht_lookup(struct rcu_ht *ht, void *key) node = NULL; break; } - if (node->key == key) { + if (!ht->compare_fct(node->key, node->key_len, key, key_len)) { if (is_removed(rcu_dereference(node->next))) node = NULL; break; @@ -368,7 +371,7 @@ void ht_add(struct rcu_ht *ht, struct rcu_ht_node *node) { struct rcu_table *t; - node->hash = ht->hash_fct(ht->hashseed, node->key); + node->hash = ht->hash_fct(node->key, node->key_len, ht->hash_seed); node->reverse_hash = bit_reverse_ulong((unsigned long) node->hash); t = rcu_dereference(ht->t); @@ -451,7 +454,6 @@ void _do_ht_resize(struct rcu_ht *ht) memcpy(&new_t->tbl, &old_t->tbl, old_size * sizeof(struct rcu_ht_node *)); init_table(ht, new_t, old_size, new_size - old_size); - new_t->size = new_size; /* Changing table and size atomically wrt lookups */ rcu_assign_pointer(ht->t, new_t); ht->ht_call_rcu(&old_t->head, ht_free_table_cb); diff --git a/tests/test_urcu_hash.c b/tests/test_urcu_hash.c index d7fe4c8..5935656 100644 --- a/tests/test_urcu_hash.c +++ b/tests/test_urcu_hash.c @@ -181,22 +181,148 @@ void rcu_copy_mutex_unlock(void) } } +/* + * Hash function + * Source: http://burtleburtle.net/bob/c/lookup3.c + * Originally Public Domain + */ + +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) + +#define mix(a, b, c) \ +do { \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c, 16); c += b; \ + b -= a; b ^= rot(a, 19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} while (0) + +#define final(a, b, c) \ +{ \ + c ^= b; c -= rot(b, 14); \ + a ^= c; a -= rot(c, 11); \ + b ^= a; b -= rot(a, 25); \ + c ^= b; c -= rot(b, 16); \ + a ^= c; a -= rot(c, 4);\ + b ^= a; b -= rot(a, 14); \ + c ^= b; c -= rot(b, 24); \ +} + +static __attribute__((unused)) +uint32_t hash_u32( + const uint32_t *k, /* the key, an array of uint32_t values */ + size_t length, /* the length of the key, in uint32_ts */ + uint32_t initval) /* the previous hash, or an arbitrary value */ +{ + uint32_t a, b, c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t) length) << 2) + initval; + + /*----------------------------------------- handle most of the key */ + while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a, b, c); + length -= 3; + k += 3; + } + + /*----------------------------------- handle the last 3 uint32_t's */ + switch (length) { /* all the case statements fall through */ + case 3: c += k[2]; + case 2: b += k[1]; + case 1: a += k[0]; + final(a, b, c); + case 0: /* case 0: nothing left to add */ + break; + } + /*---------------------------------------------- report the result */ + return c; +} + +static +void hashword2( + const uint32_t *k, /* the key, an array of uint32_t values */ + size_t length, /* the length of the key, in uint32_ts */ + uint32_t *pc, /* IN: seed OUT: primary hash value */ + uint32_t *pb) /* IN: more seed OUT: secondary hash value */ +{ + uint32_t a, b, c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + *pc; + c += *pb; + + /*----------------------------------------- handle most of the key */ + while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a, b, c); + length -= 3; + k += 3; + } + + /*----------------------------------- handle the last 3 uint32_t's */ + switch (length) { /* all the case statements fall through */ + case 3: c += k[2]; + case 2: b += k[1]; + case 1: a += k[0]; + final(a, b, c); + case 0: /* case 0: nothing left to add */ + break; + } + /*---------------------------------------------- report the result */ + *pc = c; + *pb = b; +} + +#if (CAA_BITS_PER_LONG == 32) static -unsigned long test_hash(void *_hashseed, void *_key) +unsigned long test_hash(void *_key, size_t length, unsigned long seed) { - unsigned long seed = (unsigned long) _hashseed; unsigned long key = (unsigned long) _key; unsigned long v; - v = key ^ seed; - v ^= v << 8; - v ^= v << 16; - v ^= v << 27; - v ^= v >> 8; - v ^= v >> 16; - v ^= v >> 27; + assert(length == sizeof(unsigned long)); + return hash_u32(&v, 1, seed); +} +#else +static +unsigned long test_hash(void *_key, size_t length, unsigned long seed) +{ + union { + uint64_t v64; + uint32_t v32[2]; + } v; + union { + uint64_t v64; + uint32_t v32[2]; + } key; + + assert(length == sizeof(unsigned long)); + v.v64 = (uint64_t) seed; + key.v64 = (uint64_t) _key; + hashword2(key.v32, 2, &v.v32[0], &v.v32[1]); + return v.v64; +} +#endif - return v; +static +unsigned long test_compare(void *key1, size_t key1_len, + void *key2, size_t key2_len) +{ + if (unlikely(key1_len != key2_len)) + return -1; + assert(key1_len == sizeof(unsigned long)); + if (key1 == key2) + return 0; + else + return 1; } void *thr_reader(void *_count) @@ -219,7 +345,8 @@ void *thr_reader(void *_count) for (;;) { rcu_read_lock(); node = ht_lookup(test_ht, - (void *)(unsigned long)(rand_r(&rand_lookup) % RAND_POOL)); + (void *)(unsigned long)(rand_r(&rand_lookup) % RAND_POOL), + sizeof(void *)); if (node == NULL) lookup_fail++; else @@ -276,6 +403,7 @@ void *thr_writer(void *_count) rcu_read_lock(); ht_node_init(node, (void *)(unsigned long)(rand_r(&rand_lookup) % RAND_POOL), + sizeof(void *), (void *) 0x42); ht_add(test_ht, node); rcu_read_unlock(); @@ -284,7 +412,8 @@ void *thr_writer(void *_count) /* May delete */ rcu_read_lock(); node = ht_lookup(test_ht, - (void *)(unsigned long)(rand_r(&rand_lookup) % RAND_POOL)); + (void *)(unsigned long)(rand_r(&rand_lookup) % RAND_POOL), + sizeof(void *)); if (node) ret = ht_remove(test_ht, node); else @@ -424,7 +553,7 @@ int main(int argc, char **argv) tid_writer = malloc(sizeof(*tid_writer) * nr_writers); count_reader = malloc(sizeof(*count_reader) * nr_readers); count_writer = malloc(sizeof(*count_writer) * nr_writers); - test_ht = ht_new(test_hash, (void *) 0x42, + test_ht = ht_new(test_hash, test_compare, 0x42UL, HASH_SIZE, call_rcu); next_aff = 0; diff --git a/urcu/rculfhash.h b/urcu/rculfhash.h index 1aa98c5..94206e3 100644 --- a/urcu/rculfhash.h +++ b/urcu/rculfhash.h @@ -7,6 +7,7 @@ struct rcu_ht_node { struct rcu_ht_node *next; void *key; + size_t key_len; unsigned long hash; unsigned long reverse_hash; unsigned int dummy; @@ -21,12 +22,17 @@ struct rcu_ht; * Ensure reader and writer threads are registered as urcu readers. */ -typedef unsigned long (*ht_hash_fct)(void *hashseed, void *key); +typedef unsigned long (*ht_hash_fct)(void *key, size_t length, + unsigned long seed); +typedef unsigned long (*ht_compare_fct)(void *key1, size_t key1_len, + void *key2, size_t key2_len); static inline -void ht_node_init(struct rcu_ht_node *node, void *key, void *value) +void ht_node_init(struct rcu_ht_node *node, void *key, + size_t key_len, void *value) { node->key = key; + node->key_len = key_len; node->value = value; node->dummy = 0; } @@ -35,7 +41,8 @@ void ht_node_init(struct rcu_ht_node *node, void *key, void *value) * init_size must be power of two. */ struct rcu_ht *ht_new(ht_hash_fct hash_fct, - void *hashseed, + ht_compare_fct compare_fct, + unsigned long hash_seed, unsigned long init_size, void (*ht_call_rcu)(struct rcu_head *head, void (*func)(struct rcu_head *head))); @@ -43,7 +50,7 @@ struct rcu_ht *ht_new(ht_hash_fct hash_fct, int ht_destroy(struct rcu_ht *ht); /* Call with rcu_read_lock held. */ -struct rcu_ht_node *ht_lookup(struct rcu_ht *ht, void *key); +struct rcu_ht_node *ht_lookup(struct rcu_ht *ht, void *key, size_t key_len); /* Call with rcu_read_lock held. */ void ht_add(struct rcu_ht *ht, struct rcu_ht_node *node); -- 2.34.1