Special-case size-1 sequential hopscotch hash table

Rather than allocate a HH_HOP_RANGE large array of buckets, allocate
just 1 if the initial size is 1, then jump to HH_HOP_RANGE as soon as a
second element is added to the table.  There are quite a few cases where
hash tables are created where there never be more than 1 (or even 0)
elements in the table (e.g., a writer without readers, a reader for a
keyless topic).

Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
Erik Boasson 2019-06-24 10:05:00 +02:00 committed by eboasson
parent 0b8fd9fcc0
commit 0e888eb2ec
2 changed files with 55 additions and 29 deletions

View file

@ -366,11 +366,11 @@ struct whc *whc_new (int is_transient_local, uint32_t hdepth, uint32_t tldepth)
#if USE_EHH #if USE_EHH
whc->seq_hash = ddsrt_ehh_new (sizeof (struct whc_seq_entry), 32, whc_seq_entry_hash, whc_seq_entry_eq); whc->seq_hash = ddsrt_ehh_new (sizeof (struct whc_seq_entry), 32, whc_seq_entry_hash, whc_seq_entry_eq);
#else #else
whc->seq_hash = ddsrt_hh_new (32, whc_node_hash, whc_node_eq); whc->seq_hash = ddsrt_hh_new (1, whc_node_hash, whc_node_eq);
#endif #endif
if (whc->idxdepth > 0) if (whc->idxdepth > 0)
whc->idx_hash = ddsrt_hh_new (32, whc_idxnode_hash_key, whc_idxnode_eq_key); whc->idx_hash = ddsrt_hh_new (1, whc_idxnode_hash_key, whc_idxnode_eq_key);
else else
whc->idx_hash = NULL; whc->idx_hash = NULL;

View file

@ -39,10 +39,16 @@ struct ddsrt_hh {
static void ddsrt_hh_init (struct ddsrt_hh *rt, uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals) static void ddsrt_hh_init (struct ddsrt_hh *rt, uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals)
{ {
uint32_t size = HH_HOP_RANGE; uint32_t size;
uint32_t i; uint32_t i;
while (size < init_size) { /* degenerate case to minimize memory use */
size *= 2; if (init_size == 1) {
size = 1;
} else {
size = HH_HOP_RANGE;
while (size < init_size) {
size *= 2;
}
} }
rt->hash = hash; rt->hash = hash;
rt->equals = equals; rt->equals = equals;
@ -125,34 +131,54 @@ static uint32_t ddsrt_hh_find_closer_free_bucket (struct ddsrt_hh *rt, uint32_t
static void ddsrt_hh_resize (struct ddsrt_hh *rt) static void ddsrt_hh_resize (struct ddsrt_hh *rt)
{ {
struct ddsrt_hh_bucket *bs1; if (rt->size == 1) {
uint32_t i, idxmask0, idxmask1; assert (rt->size == 1);
assert (rt->buckets[0].hopinfo == 1);
assert (rt->buckets[0].data != NULL);
bs1 = ddsrt_malloc (2 * rt->size * sizeof (*rt->buckets)); rt->size = HH_HOP_RANGE;
const uint32_t hash = rt->hash (rt->buckets[0].data);
const uint32_t idxmask = rt->size - 1;
const uint32_t start_bucket = hash & idxmask;
for (i = 0; i < 2 * rt->size; i++) { struct ddsrt_hh_bucket *newbs = ddsrt_malloc (rt->size * sizeof (*newbs));
bs1[i].hopinfo = 0; for (uint32_t i = 0; i < rt->size; i++) {
bs1[i].data = NULL; newbs[i].hopinfo = 0;
} newbs[i].data = NULL;
idxmask0 = rt->size - 1;
idxmask1 = 2 * rt->size - 1;
for (i = 0; i < rt->size; i++) {
void *data = rt->buckets[i].data;
if (data) {
const uint32_t hash = rt->hash (data);
const uint32_t old_start_bucket = hash & idxmask0;
const uint32_t new_start_bucket = hash & idxmask1;
const uint32_t dist = (i >= old_start_bucket) ? (i - old_start_bucket) : (rt->size + i - old_start_bucket);
const uint32_t newb = (new_start_bucket + dist) & idxmask1;
assert (dist < HH_HOP_RANGE);
bs1[new_start_bucket].hopinfo |= 1u << dist;
bs1[newb].data = data;
} }
} newbs[start_bucket] = rt->buckets[0];
ddsrt_free (rt->buckets);
rt->buckets = newbs;
} else {
struct ddsrt_hh_bucket *bs1;
uint32_t i, idxmask0, idxmask1;
ddsrt_free (rt->buckets); bs1 = ddsrt_malloc (2 * rt->size * sizeof (*rt->buckets));
rt->size *= 2;
rt->buckets = bs1; for (i = 0; i < 2 * rt->size; i++) {
bs1[i].hopinfo = 0;
bs1[i].data = NULL;
}
idxmask0 = rt->size - 1;
idxmask1 = 2 * rt->size - 1;
for (i = 0; i < rt->size; i++) {
void *data = rt->buckets[i].data;
if (data) {
const uint32_t hash = rt->hash (data);
const uint32_t old_start_bucket = hash & idxmask0;
const uint32_t new_start_bucket = hash & idxmask1;
const uint32_t dist = (i >= old_start_bucket) ? (i - old_start_bucket) : (rt->size + i - old_start_bucket);
const uint32_t newb = (new_start_bucket + dist) & idxmask1;
assert (dist < HH_HOP_RANGE);
bs1[new_start_bucket].hopinfo |= 1u << dist;
bs1[newb].data = data;
}
}
ddsrt_free (rt->buckets);
rt->size *= 2;
rt->buckets = bs1;
}
} }
int ddsrt_hh_add (struct ddsrt_hh * __restrict rt, const void * __restrict data) int ddsrt_hh_add (struct ddsrt_hh * __restrict rt, const void * __restrict data)