From e4fd746f7102d63409d8ca5ff30dfb2b2e359a44 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sat, 10 Nov 2001 01:13:29 +0000 Subject: [PATCH] this adds some stats to hash tables and a command to get at them for commands and aliases --- include/QF/hash.h | 5 +++ libs/util/cmd.c | 15 +++++++- libs/util/hash.c | 96 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 107 insertions(+), 9 deletions(-) diff --git a/include/QF/hash.h b/include/QF/hash.h index 9cb87494a..b4481fc52 100644 --- a/include/QF/hash.h +++ b/include/QF/hash.h @@ -120,4 +120,9 @@ unsigned long Hash_String (const char *str); */ void **Hash_GetList (hashtab_t *tab); +/* + dump statistics about the hash table +*/ +void Hash_Stats (hashtab_t *tab); + #endif // __hash_h diff --git a/libs/util/cmd.c b/libs/util/cmd.c index dc5b39ec6..d3bfd755d 100644 --- a/libs/util/cmd.c +++ b/libs/util/cmd.c @@ -949,6 +949,15 @@ Cmd_Help_f (void) Sys_Printf ("variable/command not found\n"); } +void +Cmd_Hash_Stats_f (void) +{ + Sys_Printf ("alias hash table:\n"); + Hash_Stats (cmd_alias_hash); + Sys_Printf ("command hash table:\n"); + Hash_Stats (cmd_hash); +} + static void cmd_alias_free (void *_a, void *unused) { @@ -981,8 +990,8 @@ cmd_get_key (void *c, void *unused) void Cmd_Init_Hash (void) { - cmd_hash = Hash_NewTable (1021, cmd_get_key, 0, 0); - cmd_alias_hash = Hash_NewTable (1021, cmd_alias_get_key, cmd_alias_free, + cmd_hash = Hash_NewTable (16381, cmd_get_key, 0, 0); + cmd_alias_hash = Hash_NewTable (16381, cmd_alias_get_key, cmd_alias_free, 0); } @@ -1005,6 +1014,8 @@ Cmd_Init (void) Cmd_AddCommand ("cmdlist", Cmd_CmdList_f, "List all commands"); Cmd_AddCommand ("help", Cmd_Help_f, "Display help for a command or " "variable"); + Cmd_AddCommand ("cmd_hash_stats", Cmd_Hash_Stats_f, "Display statistics " + "alias and command hash tables"); cmd_warncmd = Cvar_Get ("cmd_warncmd", "0", CVAR_NONE, NULL, "Toggles the " "display of error messages for unknown commands"); cmd_highchars = Cvar_Get ("cmd_highchars", "0", CVAR_NONE, NULL, "Toggles availability of special " diff --git a/libs/util/hash.c b/libs/util/hash.c index 2daa088de..39817eba4 100644 --- a/libs/util/hash.c +++ b/libs/util/hash.c @@ -1,5 +1,5 @@ /* - hash.h + hash.c hash tables @@ -37,6 +37,7 @@ static const char rcsid[] = # include #endif +#include #include // should be sys/types.h, but bc is stupid #include "QF/hash.h" @@ -52,6 +53,7 @@ struct hashlink_s { struct hashtab_s { size_t tab_size; + unsigned int size_bits; size_t num_ele; void *user_data; int (*compare)(void*,void*,void*); @@ -102,6 +104,30 @@ compare (void *a, void *b, void *data) return a == b; } +static inline int +get_index (unsigned long hash, size_t size, size_t bits) +{ +#if 0 + unsigned long mask = ~0UL << bits; + unsigned long extract; + + size -= 1; + for (extract = (hash & mask) >> bits; + extract; + extract = (hash & mask) >> bits) { + hash &= ~mask; + hash ^= extract; + } while (extract); + if (hash > size) { + extract = hash - size; + hash = size - (extract >> 1); + } + return hash; +#else + return hash % size; +#endif +} + hashtab_t * Hash_NewTable (int tsize, const char *(*gk)(void*,void*), void (*f)(void*,void*), void *ud) @@ -114,6 +140,11 @@ Hash_NewTable (int tsize, const char *(*gk)(void*,void*), tab->get_key = gk; tab->free_ele = f; + while (tsize) { + tab->size_bits++; + tsize = ((unsigned int) tsize) >> 1; + } + tab->get_hash = get_hash; tab->compare = compare; return tab; @@ -157,7 +188,7 @@ int Hash_Add (hashtab_t *tab, void *ele) { unsigned long h = Hash_String (tab->get_key(ele, tab->user_data)); - size_t ind = h % tab->tab_size; + size_t ind = get_index (h, tab->tab_size, tab->size_bits); struct hashlink_s *lnk = malloc (sizeof (struct hashlink_s)); if (!lnk) @@ -176,7 +207,7 @@ int Hash_AddElement (hashtab_t *tab, void *ele) { unsigned long h = tab->get_hash (ele, tab->user_data); - size_t ind = h % tab->tab_size; + size_t ind = get_index (h, tab->tab_size, tab->size_bits); struct hashlink_s *lnk = malloc (sizeof (struct hashlink_s)); if (!lnk) @@ -195,7 +226,7 @@ void * Hash_Find (hashtab_t *tab, const char *key) { unsigned long h = Hash_String (key); - size_t ind = h % tab->tab_size; + size_t ind = get_index (h, tab->tab_size, tab->size_bits); struct hashlink_s *lnk = tab->tab[ind]; while (lnk) { @@ -210,7 +241,7 @@ void * Hash_FindElement (hashtab_t *tab, void *ele) { unsigned long h = tab->get_hash (ele, tab->user_data); - size_t ind = h % tab->tab_size; + size_t ind = get_index (h, tab->tab_size, tab->size_bits); struct hashlink_s *lnk = tab->tab[ind]; while (lnk) { @@ -225,7 +256,7 @@ void * Hash_Del (hashtab_t *tab, const char *key) { unsigned long h = Hash_String (key); - size_t ind = h % tab->tab_size; + size_t ind = get_index (h, tab->tab_size, tab->size_bits); struct hashlink_s *lnk = tab->tab[ind]; void *data; @@ -248,7 +279,7 @@ void * Hash_DelElement (hashtab_t *tab, void *ele) { unsigned long h = tab->get_hash (ele, tab->user_data); - size_t ind = h % tab->tab_size; + size_t ind = get_index (h, tab->tab_size, tab->size_bits); struct hashlink_s *lnk = tab->tab[ind]; void *data; @@ -287,3 +318,54 @@ Hash_GetList (hashtab_t *tab) *l++ = 0; return list; } + +static inline double +sqr (double x) +{ + return x * x; +} + +void +Hash_Stats (hashtab_t *tab) +{ + int *lengths = calloc (tab->tab_size, sizeof (int)); + int chains = 0; + int i; + int min_length = tab->num_ele; + int max_length = 0; + + if (!lengths) { + Sys_Printf ("Hash_Stats: memory alloc error\n"); + return; + } + + for (i = 0; i < tab->tab_size; i++) { + struct hashlink_s *lnk = tab->tab[i]; + + while (lnk) { + lengths[i]++; + lnk = lnk->next; + } + if (lengths[i]) { + min_length = min (min_length, lengths[i]); + max_length = max (max_length, lengths[i]); + chains++; + } + } + Sys_Printf ("%d elements\n", tab->num_ele); + Sys_Printf ("%d / %d chains\n", chains, tab->tab_size); + if (chains) { + double average = (double) tab->num_ele / chains; + double variance = 0; + Sys_Printf ("%d minium chain length\n", min_length); + Sys_Printf ("%d maximum chain length\n", max_length); + Sys_Printf ("%.3g average chain length\n", average); + for (i = 0; i < tab->tab_size; i++) { + if (lengths[i]) + variance += sqr (lengths[i] - average); + } + variance /= chains; + Sys_Printf ("%.3g variance, %.3g standard deviation\n", + variance, sqrt (variance)); + } +}