From 23505d2b846c0ff46ed591206715349374a45ee4 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 24 Apr 2008 01:37:47 +0000 Subject: [PATCH] Slightly more cache-friendly hashing (WIP) --- src/common/kernel/eressea.c | 43 +++---------------- src/common/kernel/eressea.h | 11 +++-- src/common/kernel/unit.c | 86 +++++++++++++++++++++++++++++++++++++ src/common/kernel/unit.h | 63 ++++++++++++++------------- 4 files changed, 130 insertions(+), 73 deletions(-) diff --git a/src/common/kernel/eressea.c b/src/common/kernel/eressea.c index 8d5ff5dfd..0e20b54ed 100644 --- a/src/common/kernel/eressea.c +++ b/src/common/kernel/eressea.c @@ -633,40 +633,6 @@ shipspeed (const ship * sh, const unit * u) return k; } -/* erwartete Anzahl Einheiten x 2 */ -#define UMAXHASH 199999 -unit *unithash[UMAXHASH]; - -void -uhash (unit * u) -{ - assert(!u->nexthash || !"unit ist bereits gehasht"); - u->nexthash = unithash[u->no % UMAXHASH]; - unithash[u->no % UMAXHASH] = u; -} - -void -uunhash (unit * u) -{ - unit ** x = &(unithash[u->no % UMAXHASH]); - while (*x && *x!=u) x = &(*x)->nexthash; - assert(*x || !"unit nicht gefunden"); - *x = u->nexthash; - u->nexthash=NULL; -} - -unit * -ufindhash (int i) -{ - assert(i>=0); - if (i>=0) { - unit * u = unithash[i % UMAXHASH]; - while (u && u->no!=i) u = u->nexthash; - return u; - } - return NULL; -} - #define FMAXHASH 2039 faction * factionhash[FMAXHASH]; @@ -1438,7 +1404,10 @@ findunitr (const region * r, int n) unit *findunit(int n) { - return findunitg(n, NULL); + if (n <= 0) { + return NULL; + } + return ufindhash(n); } unit * @@ -1446,12 +1415,12 @@ findunitg (int n, const region * hint) { /* Abfangen von Syntaxfehlern. */ - if(n <= 0) + if (n <= 0) return NULL; /* findunit global! */ hint = 0; - return ufindhash (n); + return ufindhash(n); } unit * diff --git a/src/common/kernel/eressea.h b/src/common/kernel/eressea.h index e64bf6176..f45188c75 100644 --- a/src/common/kernel/eressea.h +++ b/src/common/kernel/eressea.h @@ -39,8 +39,12 @@ extern "C" { #define ALLIED(f1, f2) (f1==f2 || (f1->alliance && f1->alliance==f2->alliance)) +/* for some good prime numbers, check http://www.math.niu.edu/~rusin/known-math/98/pi_x */ #ifndef MAXREGIONS -# define MAXREGIONS 262139 /* must be prime for hashing. 262139=last<2^18 */ +# define MAXREGIONS 262139 /* must be prime for hashing. */ +#endif +#ifndef MAXUNITS +# define MAXUNITS 524287 /* must be prime for hashing. */ #endif #define MONSTER_FACTION 0 /* Die Partei, in der die Monster sind. */ @@ -274,11 +278,6 @@ void changeblockchaos(void); extern struct region *firstregion(struct faction * f); extern struct region *lastregion(struct faction * f); -void inituhash(void); -void uhash(struct unit * u); -void uunhash(struct unit * u); -struct unit *ufindhash(int i); - void fhash(struct faction * f); void funhash(struct faction * f); diff --git a/src/common/kernel/unit.c b/src/common/kernel/unit.c index 5a3e1473a..b572889db 100644 --- a/src/common/kernel/unit.c +++ b/src/common/kernel/unit.c @@ -67,6 +67,92 @@ attrib_type at_creator = { /* Rest ist NULL; temporäres, nicht alterndes Attribut */ }; +#define UMAXHASH MAXUNITS +static unit * unithash[UMAXHASH]; +static unit * delmarker = (unit*)unithash; /* a funny hack */ + +#define HASH_STATISTICS 1 +#if HASH_STATISTICS +static int hash_requests; +static int hash_misses; +#endif + +/* benchmark: + * jenkins_hash: 5.25 misses/hit + * wang_hash: 5.33 misses/hit + */ + +INLINE_FUNCTION unsigned int jenkins_hash(unsigned int a) +{ + a = (a+0x7ed55d16) + (a<<12); + a = (a^0xc761c23c) ^ (a>>19); + a = (a+0x165667b1) + (a<<5); + a = (a+0xd3a2646c) ^ (a<<9); + a = (a+0xfd7046c5) + (a<<3); + a = (a^0xb55a4f09) ^ (a>>16); + return a; +} + +INLINE_FUNCTION unsigned int wang_hash(unsigned int a) +{ + a = ~a + (a << 15); // a = (a << 15) - a - 1; + a = a ^ (a >> 12); + a = a + (a << 2); + a = a ^ (a >> 4); + a = a * 2057; // a = (a + (a << 3)) + (a << 11); + a = a ^ (a >> 16); + return a; +} + +#define HASH1(a, m) (jenkins_hash(a) % m) +#define HASH2(a, m) 1 +/* +#define HASH1(a, m) ((a) % m) +#define HASH2(a, m) (m - 2 - a % (m-2)) +*/ + +void +uhash(unit * u) +{ + int key = HASH1(u->no, UMAXHASH), gk = HASH2(u->no, UMAXHASH); + while (unithash[key]!=NULL && unithash[key]!=delmarker && unithash[key]!=u) { + key = (key + gk) % UMAXHASH; + } + assert(unithash[key]!=u || !"trying to add the same unit twice"); + unithash[key] = u; +} + +void +uunhash(unit * u) +{ + int key = HASH1(u->no, UMAXHASH), gk = HASH2(u->no, UMAXHASH); + while (unithash[key]!=NULL && unithash[key]!=u) { + key = (key + gk) % UMAXHASH; + } + assert(unithash[key]==u || !"trying to remove a unit that is not hashed"); + unithash[key] = delmarker; +} + +unit * +ufindhash(int uid) +{ + assert(uid>=0); +#if HASH_STATISTICS + ++hash_requests; +#endif + if (uid>=0) { + int key = HASH1(uid, UMAXHASH), gk = HASH2(uid, UMAXHASH); + while (unithash[key]!=NULL && (unithash[key]==delmarker || unithash[key]->no!=uid)) { + key = (key + gk) % UMAXHASH; +#if HASH_STATISTICS + ++hash_misses; +#endif + } + return unithash[key]; + } + return NULL; +} + #define DMAXHASH 7919 typedef struct dead { struct dead * nexthash; diff --git a/src/common/kernel/unit.h b/src/common/kernel/unit.h index e9384b0b3..a448f1586 100644 --- a/src/common/kernel/unit.h +++ b/src/common/kernel/unit.h @@ -76,48 +76,47 @@ extern int countheroes(const struct faction * f); #endif typedef struct unit { - struct unit *next; /* needs to be first entry, for region's unitlist */ - struct unit *nexthash; - struct unit *nextF; /* nächste Einheit der Partei */ - struct region *region; + struct unit *next; /* needs to be first entry, for region's unitlist */ + struct unit *nextF; /* nächste Einheit der Partei */ + struct region *region; int no; int hp; - char *name; - char *display; - struct faction *faction; - struct building *building; - struct ship *ship; + char *name; + char *display; + struct faction *faction; + struct building *building; + struct ship *ship; unsigned short number; short age; - /* skill data */ - short skill_size; - struct skill *skills; - struct item * items; - struct reservation { - struct reservation * next; - const struct resource_type * type; - int value; - } * reservations; + /* skill data */ + short skill_size; + struct skill *skills; + struct item * items; + struct reservation { + struct reservation * next; + const struct resource_type * type; + int value; + } * reservations; - /* orders */ - struct order * orders; - struct order * thisorder; + /* orders */ + struct order * orders; + struct order * thisorder; #ifdef LASTORDER - struct order * lastorder; + struct order * lastorder; #else struct order * old_orders; #endif - /* race and illusionary race */ - const struct race * race; - const struct race * irace; + /* race and illusionary race */ + const struct race * race; + const struct race * irace; - unsigned int flags; - struct attrib * attribs; - status_t status; - int n; /* enno: attribut? */ - int wants; /* enno: attribut? */ + unsigned int flags; + struct attrib * attribs; + status_t status; + int n; /* enno: attribut? */ + int wants; /* enno: attribut? */ } unit; typedef struct unit_list { @@ -217,6 +216,10 @@ extern void stripunit(struct unit * u); extern void name_unit(struct unit *u); extern struct unit * create_unit(struct region * r1, struct faction * f, int number, const struct race * rc, int id, const char * dname, struct unit *creator); +extern void uhash(struct unit * u); +extern void uunhash(struct unit * u); +extern struct unit *ufindhash(int i); + extern struct attrib_type at_creator; #ifdef __cplusplus }