Commit 34fb708e authored by Richard Gobeille's avatar Richard Gobeille
Browse files

engine: optimize hash tables

parent 3ccb932f
......@@ -4,6 +4,8 @@
#ifndef hash_h_
#define hash_h_
#include "libdivide.h"
#ifdef __cplusplus
extern "C" {
#endif
......@@ -18,9 +20,19 @@ typedef struct hashitem // size is 12/24 bytes.
struct hashitem *next;
} hashitem_t;
typedef struct
{
uint32_t v;
libdivide::libdivide_u32_t d;
} hashprime_t;
typedef struct
{
int32_t size;
union
{
uint32_t size;
hashprime_t prime;
};
hashitem_t **items;
} hashtable_t;
......@@ -36,6 +48,12 @@ static inline uint32_t hash_getcode(const char *s)
return h;
}
static FORCE_INLINE uint32_t hash_getbucket(hashtable_t const *t, const char *s)
{
uint32_t const h = hash_getcode(s);
return h - libdivide::libdivide_u32_do(h, &t->prime.d) * t->prime.v;
}
void hash_init(hashtable_t *t);
void hash_loop(hashtable_t *t, void (*func)(const char *, intptr_t));
void hash_free(hashtable_t *t);
......@@ -58,7 +76,11 @@ typedef struct inthashitem
typedef struct
{
inthashitem_t *items;
uint32_t count;
union
{
uint32_t count;
hashprime_t prime;
};
} inthashtable_t;
// djb3 algorithm
......@@ -72,6 +94,12 @@ static inline uint32_t inthash_getcode(intptr_t key)
return h;
}
static FORCE_INLINE uint32_t inthash_getbucket(inthashtable_t const *t, intptr_t key)
{
uint32_t const h = inthash_getcode(key);
return h - libdivide::libdivide_u32_do(h, &t->prime.d) * t->prime.v;
}
void inthash_init(inthashtable_t *t);
void inthash_loop(inthashtable_t const *t, void (*func)(intptr_t, intptr_t));
void inthash_free(inthashtable_t *t);
......
......@@ -3,10 +3,53 @@
#include "hash.h"
#include "baselayer.h"
// vaguely inspired by https://databasearchitects.blogspot.com/2020/01/all-hash-table-sizes-you-will-ever-need.html
// all things considered, I would have just used that implementation if MSVC had any support for __int128
static constexpr uint64_t primes[] = {
11ull, 13ull, 17ull, 19ull, 37ull, 41ull, 43ull, 59ull, 67ull, 73ull, 79ull, 83ull, 109ull, 113ull,
131ull, 139ull, 149ull, 157ull, 163ull, 173ull, 191ull, 211ull, 227ull, 241ull, 257ull, 269ull, 283ull, 307ull,
331ull, 349ull, 373ull, 397ull, 419ull, 439ull, 461ull, 499ull, 523ull, 557ull, 587ull, 617ull, 647ull, 691ull,
727ull, 769ull, 809ull, 877ull, 941ull, 997ull, 1049ull, 1103ull, 1163ull, 1223ull, 1283ull, 1367ull, 1439ull, 1523ull,
1609ull, 1693ull, 1783ull, 1871ull, 1987ull, 2087ull, 2203ull, 2333ull, 2459ull, 2591ull, 2719ull, 2857ull, 2999ull, 3163ull,
3323ull, 3491ull, 3671ull, 3863ull, 4091ull, 4297ull, 4513ull, 4751ull, 4987ull, 5237ull, 5501ull, 5791ull, 6079ull, 6397ull,
6733ull, 7079ull, 7433ull, 7877ull, 8269ull, 8681ull, 9127ull, 9587ull, 10067ull, 10589ull, 11117ull, 11677ull, 12263ull, 12893ull,
13537ull, 14221ull, 14947ull, 15727ull, 16519ull, 17351ull, 18217ull, 19139ull, 20101ull, 21107ull, 22189ull, 23297ull, 24473ull, 25717ull,
27011ull, 28387ull, 29819ull, 31321ull, 32887ull, 34537ull, 36263ull, 38083ull, 39989ull, 41999ull, 44101ull, 46307ull, 48623ull, 51059ull,
53611ull, 56333ull, 59149ull, 62119ull, 65239ull, 68501ull, 71933ull, 75533ull, 79309ull, 83273ull, 87481ull, 91867ull, 96461ull, 101287ull,
106357ull, 111697ull, 117281ull, 123191ull, 129379ull, 135851ull, 142657ull, 149791ull, 157279ull, 165161ull, 173431ull, 182101ull, 191227ull, 200789ull,
210827ull, 221399ull, 232499ull, 244157ull, 256369ull, 269189ull, 282661ull, 296797ull, 311677ull, 327263ull, 343627ull, 360817ull, 378869ull, 397811ull,
417719ull, 438611ull, 460543ull, 483611ull, 507803ull, 533213ull, 559877ull, 587891ull, 617311ull, 648181ull, 680597ull, 714673ull, 750413ull, 787981ull,
827389ull, 868771ull, 912211ull, 957821ull, 1005761ull, 1056049ull,
};
static uint64_t hash_findprime(uint64_t desiredSize)
{
if (desiredSize >= primes[ARRAY_SIZE(primes)-1])
{
initprintf("hash_findprime(): size %llu requested, expand prime table\n", desiredSize);
return primes[ARRAY_SIZE(primes)-1];
}
uint64_t lower = 0, upper = ARRAY_SIZE(primes)-1;
while (lower != upper)
{
uint64_t middle = lower + ((upper - lower) >> 1);
if (primes[middle] < desiredSize)
lower = middle + 1;
else if (primes[middle] > desiredSize)
upper = middle;
else
return primes[middle];
}
return primes[lower];
}
void hash_init(hashtable_t *t)
{
hash_free(t);
t->items = (hashitem_t **) Xaligned_calloc(16, t->size, sizeof(hashitem_t));
t->prime.v = hash_findprime(t->size);
t->prime.d = libdivide::libdivide_u32_gen(t->prime.v);
t->items = (hashitem_t **) Xaligned_calloc(16, t->prime.v, sizeof(hashitem_t));
}
void hash_loop(hashtable_t *t, void(*func)(const char *, intptr_t))
......@@ -14,7 +57,7 @@ void hash_loop(hashtable_t *t, void(*func)(const char *, intptr_t))
if (t->items == nullptr)
return;
for (native_t i=0; i < t->size; i++)
for (unsigned i=0; i < t->prime.v; i++)
for (auto item = t->items[i]; item != nullptr; item = item->next)
func(item->string, item->key);
}
......@@ -24,7 +67,7 @@ void hash_free(hashtable_t *t)
if (t->items == nullptr)
return;
int remaining = t->size - 1;
int remaining = t->prime.v - 1;
do
{
......@@ -48,7 +91,7 @@ void hash_add(hashtable_t *t, const char *s, intptr_t key, int32_t replace)
#ifdef DEBUGGINGAIDS
Bassert(t->items != nullptr);
#endif
uint32_t const code = hash_getcode(s) % t->size;
uint32_t const code = hash_getbucket(t,s);
auto cur = t->items[code];
if (!cur)
......@@ -88,7 +131,7 @@ void hash_delete(hashtable_t *t, const char *s)
#ifdef DEBUGGINGAIDS
Bassert(t->items != nullptr);
#endif
uint32_t const code = hash_getcode(s) % t->size;
uint32_t const code = hash_getbucket(t,s);
auto cur = t->items[code];
if (!cur)
......@@ -120,7 +163,7 @@ intptr_t hash_find(const hashtable_t * const t, char const * const s)
#ifdef DEBUGGINGAIDS
Bassert(t->items != nullptr);
#endif
auto cur = t->items[hash_getcode(s) % t->size];
auto cur = t->items[hash_getbucket(t,s)];
if (!cur)
return -1;
......@@ -138,7 +181,7 @@ intptr_t hash_findcase(const hashtable_t * const t, char const * const s)
#ifdef DEBUGGINGAIDS
Bassert(t->items != nullptr);
#endif
auto cur = t->items[hash_getcode(s) % t->size];
auto cur = t->items[hash_getbucket(t,s)];
if (!cur)
return -1;
......@@ -157,7 +200,9 @@ void inthash_free(inthashtable_t *t) { ALIGNED_FREE_AND_NULL(t->items); }
void inthash_init(inthashtable_t *t)
{
inthash_free(t);
t->items = (inthashitem_t *) Xaligned_calloc(16, t->count, sizeof(inthashitem_t));
t->prime.v = hash_findprime(t->count);
t->prime.d = libdivide::libdivide_u32_gen(t->prime.v);
t->items = (inthashitem_t *) Xaligned_calloc(16, t->prime.v, sizeof(inthashitem_t));
}
void inthash_loop(inthashtable_t const *t, void(*func)(intptr_t, intptr_t))
......@@ -165,7 +210,7 @@ void inthash_loop(inthashtable_t const *t, void(*func)(intptr_t, intptr_t))
if (t->items == nullptr)
return;
for (auto *item = t->items, *const items_end = t->items + t->count; item < items_end; ++item)
for (auto *item = t->items, *const items_end = t->items + t->prime.v; item < items_end; ++item)
func(item->key, item->value);
}
......@@ -175,7 +220,7 @@ void inthash_add(inthashtable_t *t, intptr_t key, intptr_t value, int32_t replac
#ifdef DEBUGGINGAIDS
Bassert(t->items != nullptr);
#endif
auto seeker = t->items + inthash_getcode(key) % t->count;
auto seeker = t->items + inthash_getbucket(t,key);
if (seeker->collision == nullptr)
{
......@@ -205,9 +250,13 @@ void inthash_add(inthashtable_t *t, intptr_t key, intptr_t value, int32_t replac
}
auto tail = seeker;
int ofs;
do
tail = t->items + (tail - t->items + 1) % t->count;
{
ofs = (tail - t->items + 1);
tail = t->items + ofs - libdivide::libdivide_u32_do(ofs, &t->prime.d) * t->prime.v;
}
while (tail->collision != nullptr && tail != seeker);
if (EDUKE32_PREDICT_FALSE(tail == seeker))
......@@ -224,7 +273,7 @@ void inthash_delete(inthashtable_t *t, intptr_t key)
#ifdef DEBUGGINGAIDS
Bassert(t->items != nullptr);
#endif
auto seeker = t->items + inthash_getcode(key) % t->count;
auto seeker = t->items + inthash_getbucket(t,key);
if (seeker->collision == nullptr || seeker->key == key)
{
......@@ -251,7 +300,7 @@ intptr_t inthash_find(inthashtable_t const *t, intptr_t key)
#ifdef DEBUGGINGAIDS
Bassert(t->items != nullptr);
#endif
auto seeker = t->items + inthash_getcode(key) % t->count;
auto seeker = t->items + inthash_getbucket(t,key);
if (seeker->collision == nullptr)
return -1;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment