From 4db0cff16a1b16671384102f1876bebb7f481d89 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 30 May 2017 00:16:52 +0000 Subject: [PATCH 1/5] lib: add statistics for hash tables Adds a function that calculates various statistics on our implementation of a hash table. These are useful for evaluating performance. Signed-off-by: Quentin Young --- configure.ac | 5 ++ lib/command.c | 1 + lib/hash.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/hash.h | 8 +++ 4 files changed, 164 insertions(+) diff --git a/configure.ac b/configure.ac index c47c185bfe..03951503c1 100755 --- a/configure.ac +++ b/configure.ac @@ -1589,6 +1589,11 @@ AM_CONDITIONAL([SNMP], [test "x${SNMP_METHOD}" != "x"]) AC_SUBST(SNMP_LIBS) AC_SUBST(SNMP_CFLAGS) +dnl --------------- +dnl math +dnl --------------- +AC_SEARCH_LIBS([sqrt], [m]) + dnl --------------- dnl dlopen & dlinfo dnl --------------- diff --git a/lib/command.c b/lib/command.c index f019473308..c9d797844e 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2522,6 +2522,7 @@ cmd_init (int terminal) workqueue_cmd_init (); } + hash_cmd_init (); install_element (CONFIG_NODE, &hostname_cmd); install_element (CONFIG_NODE, &no_hostname_cmd); install_element (CONFIG_NODE, &frr_version_defaults_cmd); diff --git a/lib/hash.c b/lib/hash.c index 553a137eb6..210b006c59 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -19,14 +19,21 @@ */ #include +#include #include "hash.h" #include "memory.h" +#include "linklist.h" +#include "termtable.h" +#include "vty.h" +#include "command.h" DEFINE_MTYPE( LIB, HASH, "Hash") DEFINE_MTYPE( LIB, HASH_BACKET, "Hash Bucket") DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index") +static struct list *_hashes; + /* Allocate a new hash. */ struct hash * hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), @@ -43,6 +50,7 @@ hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), hash->hash_key = hash_key; hash->hash_cmp = hash_cmp; hash->count = 0; + hash->name = NULL; return hash; } @@ -282,6 +290,148 @@ hash_clean (struct hash *hash, void (*free_func) (void *)) void hash_free (struct hash *hash) { + hash_unregister (hash); XFREE (MTYPE_HASH_INDEX, hash->index); XFREE (MTYPE_HASH, hash); } + +/** + * Calculates some statistics on the given hash table that can be used to + * evaluate performance. + * + * Summary statistics calculated are: + * + * - Load factor: This is the number of elements in the table divided by the + * number of buckets. Since this hash table implementation uses chaining, + * this value can be greater than 1. This number provides information on how + * 'full' the table is, but does not provide information on how evenly + * distributed the elements are. Notably, a load factor >= 1 does not imply + * that every bucket has an element; with a pathological hash function, all + * elements could be in a single bucket. + * + * - Std. Dev.: This is the standard deviation from the load factor. If the LF + * is the mean of number of elements per bucket, the standard deviation + * measures how much any particular bucket is likely to deviate from the + * mean. As a rule of thumb this number should be less than 2, and ideally + * less than 1 for optimal performance. A number larger than 3 generally + * indicates a poor hash function. + * + * - Max: Number of elements in the most overloaded bucket(s). + * - Min: Number of elements in the most underloaded bucket(s). + * + * - Empty: Number of empty buckets + * - Avg: average number of elements among the set of full buckets (like load factor but without empty buckets) + * + * Total number of buckets is precomputed and resides in h->size. + * Total number of elements is precomputed and resides in h->count. + */ +void +hash_stats (struct hash *h, double *lf, double *stddev, int *max, int *min, int *empty, double *avg) +{ + struct hash_backet *hb; // iteration pointer + struct hash_backet *next; // iteration pointer + unsigned int backets = 0; // total number of items in ht + int buckets[h->size]; // # items per bucket + unsigned int full; // # buckets with items + + *max = *min = *lf = *stddev = *avg = 0; + *empty = h->size; + + if (h->size == 0 || h->count == 0) + return; + + *empty = 0; + + memset (buckets, 0x00, h->size * sizeof (int)); + + /* collect some important info */ + for (unsigned int i = 0; i < h->size; i++) + { + for (hb = h->index[i]; hb; hb = next) + { + buckets[i]++; + next = hb->next; + backets++; + } + *max = MAX (buckets[i], *max); + *min = MIN (buckets[i], *min); + + if (buckets[i] == 0) + *empty += 1; + } + + assert (backets == h->count); + full = h->size - *empty; + + *lf = h->count / (double) h->size; + *avg = h->count / (double) full; + + if (h->count == 0) + return; + + /* compute population stddev */ + for (unsigned int i = 0; i < h->size; i++) { + if (buckets[i] > 0) + *stddev += pow(((double) buckets[i] - *avg), 2.0); + } + + *stddev = sqrt((1.0/h->size) * *stddev); +} + +void +hash_register (struct hash *h, const char *name) +{ + h->name = name; + listnode_add (_hashes, h); +} + +void +hash_unregister (struct hash *h) +{ + listnode_delete (_hashes, h); +} + +DEFUN(show_hash_stats, + show_hash_stats_cmd, + "show hashtable ", + SHOW_STR + "Statistics about critical hash tables\n" + "Statistics about critical hash tables\n") +{ + struct hash *h; + struct listnode *ln; + struct ttable *tt = ttable_new (&ttable_styles[TTSTYLE_BLANK]); + double lf, stddev, avg; + int max, min, empty; + + ttable_add_row (tt, "Hash table|Buckets|Entries|Empty|LF|Mean|SD|Max|Min"); + tt->style.cell.lpad = 1; + tt->style.cell.rpad = 2; + ttable_restyle (tt); + ttable_rowseps (tt, 0, BOTTOM, true, '-'); + + for (ALL_LIST_ELEMENTS_RO (_hashes, ln, h)) + { + if (h->name == NULL) + continue; + + hash_stats (h, &lf, &stddev, &max, &min, &empty, &avg); + ttable_add_row (tt, "%s|%d|%d|%.0f%%|%.2f|%.2f|%.2f|%d|%d", h->name, + h->size, h->count, (empty / (double) h->size)*100, lf, avg, stddev, + max, min); + } + + char *table = ttable_dump (tt, VTY_NEWLINE); + vty_out (vty, "%s%s%s", VTY_NEWLINE, table, VTY_NEWLINE); + XFREE (MTYPE_TMP, table); + ttable_del (tt); + + return CMD_SUCCESS; +} + +void +hash_cmd_init () +{ + _hashes = list_new(); + install_element (ENABLE_NODE, &show_hash_stats_cmd); +} diff --git a/lib/hash.h b/lib/hash.h index bafb35a2a3..15afc249ab 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -64,6 +64,9 @@ struct hash /* Backet alloc. */ unsigned long count; + + /* hash name */ + const char *name; }; extern struct hash *hash_create (unsigned int (*) (void *), @@ -87,4 +90,9 @@ extern void hash_free (struct hash *); extern unsigned int string_hash_make (const char *); +extern void hash_stats (struct hash *, double *, double *, int *, int *, int *, double *); +extern void hash_cmd_init (void); +extern void hash_register (struct hash *, const char *); +extern void hash_unregister (struct hash *); + #endif /* _ZEBRA_HASH_H */ From 6f6f00107e72cc9c01a6604ff514a5b25d52106d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 19 Jun 2017 14:22:26 +0000 Subject: [PATCH 2/5] lib, vtysh: hashtable statistics Adds the ability to name hash tables, and a new cli command that will show various summary statistics for named hash tables. Statistics computed are - load factor - full load factor (see comments) - stddev of full load factor Standard deviation is computed by storing the sum of squares of bucket lengths. This is somewhat susceptible to overflow. On platforms where a double is 32 bits, placing 65535 or more elements into a hash table opens up the potential for overflow, depending on how they are arranged in buckets (which depends on the hash function). For example, placing 65535 elements into one hash bucket would cause ssq overflow, but distributing 40000000 elements evenly among 400000 buckets (100 elements per bucket) would not. These cases are extremely degenerate, so the vague possibility of overflow in an informational command is deemed an acceptable tradeoff for constant time calculation of variance without locks or compromising efficiency of actual table operations. Signed-off-by: Quentin Young --- lib/command.c | 2 +- lib/hash.c | 310 +++++++++++++++++++++++++++++--------------------- lib/hash.h | 26 ++++- vtysh/vtysh.c | 31 +++++ 4 files changed, 232 insertions(+), 137 deletions(-) diff --git a/lib/command.c b/lib/command.c index c9d797844e..77fada1636 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2520,9 +2520,9 @@ cmd_init (int terminal) thread_cmd_init (); workqueue_cmd_init (); + hash_cmd_init (); } - hash_cmd_init (); install_element (CONFIG_NODE, &hostname_cmd); install_element (CONFIG_NODE, &no_hostname_cmd); install_element (CONFIG_NODE, &frr_version_defaults_cmd); diff --git a/lib/hash.c b/lib/hash.c index 210b006c59..832b02cc37 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -27,22 +27,25 @@ #include "termtable.h" #include "vty.h" #include "command.h" +#include "libfrr.h" DEFINE_MTYPE( LIB, HASH, "Hash") DEFINE_MTYPE( LIB, HASH_BACKET, "Hash Bucket") DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index") +pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER; static struct list *_hashes; /* Allocate a new hash. */ struct hash * hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), - int (*hash_cmp) (const void *, const void *)) + int (*hash_cmp) (const void *, const void *), + const char *name) { struct hash *hash; assert ((size & (size-1)) == 0); - hash = XMALLOC (MTYPE_HASH, sizeof (struct hash)); + hash = XCALLOC (MTYPE_HASH, sizeof (struct hash)); hash->index = XCALLOC (MTYPE_HASH_INDEX, sizeof (struct hash_backet *) * size); hash->size = size; @@ -50,7 +53,17 @@ hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), hash->hash_key = hash_key; hash->hash_cmp = hash_cmp; hash->count = 0; - hash->name = NULL; + hash->name = name ? XSTRDUP(MTYPE_HASH, name) : NULL; + hash->stats.empty = hash->size; + + pthread_mutex_lock (&_hashes_mtx); + { + if (!_hashes) + _hashes = list_new(); + + listnode_add (_hashes, hash); + } + pthread_mutex_unlock (&_hashes_mtx); return hash; } @@ -58,9 +71,10 @@ hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), /* Allocate a new hash with default hash size. */ struct hash * hash_create (unsigned int (*hash_key) (void *), - int (*hash_cmp) (const void *, const void *)) + int (*hash_cmp) (const void *, const void *), + const char *name) { - return hash_create_size (HASH_INITIAL_SIZE, hash_key, hash_cmp); + return hash_create_size (HASH_INITIAL_SIZE, hash_key, hash_cmp, name); } /* Utility function for hash_get(). When this function is specified @@ -72,6 +86,15 @@ hash_alloc_intern (void *arg) return arg; } +#define hash_update_ssq(hz, old, new) \ + do { \ + long double res; \ + res = powl(old, 2.0); \ + hz->stats.ssq -= (uint64_t) res;\ + res = powl(new, 2.0); \ + hz->stats.ssq += (uint64_t) res; \ + } while (0); \ + /* Expand hash if the chain length exceeds the threshold. */ static void hash_expand (struct hash *hash) { @@ -83,6 +106,8 @@ static void hash_expand (struct hash *hash) if (new_index == NULL) return; + hash->stats.empty = new_size; + for (i = 0; i < hash->size; i++) for (hb = hash->index[i]; hb; hb = hbnext) { @@ -90,6 +115,19 @@ static void hash_expand (struct hash *hash) hbnext = hb->next; hb->next = new_index[h]; + + int oldlen = hb->next ? hb->next->len : 0; + int newlen = oldlen + 1; + + if (newlen == 1) + hash->stats.empty--; + else + hb->next->len = 0; + + hb->len = newlen; + + hash_update_ssq(hash, oldlen, newlen); + new_index[h] = hb; } @@ -99,19 +137,17 @@ static void hash_expand (struct hash *hash) hash->index = new_index; /* Ideally, new index should have chains half as long as the original. - If expansion didn't help, then not worth expanding again, - the problem is the hash function. */ + * If expansion didn't help, then not worth expanding again, + * the problem is the hash function. */ losers = 0; for (i = 0; i < hash->size; i++) { - unsigned int len = 0; - for (hb = hash->index[i]; hb; hb = hb->next) - { - if (++len > HASH_THRESHOLD/2) - ++losers; - if (len >= HASH_THRESHOLD) - hash->no_expand = 1; - } + unsigned int len = hash->index[i] ? hash->index[i]->len : 0; + + if (len > HASH_THRESHOLD/2) + ++losers; + if (len >= HASH_THRESHOLD) + hash->no_expand = 1; } if (losers > hash->count / 2) @@ -153,12 +189,25 @@ hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) index = key & (hash->size - 1); } - backet = XMALLOC (MTYPE_HASH_BACKET, sizeof (struct hash_backet)); + backet = XCALLOC (MTYPE_HASH_BACKET, sizeof (struct hash_backet)); backet->data = newdata; backet->key = key; backet->next = hash->index[index]; hash->index[index] = backet; hash->count++; + + int oldlen = backet->next ? backet->next->len : 0; + int newlen = oldlen + 1; + + if (newlen == 1) + hash->stats.empty--; + else + backet->next->len = 0; + + backet->len = newlen; + + hash_update_ssq(hash, oldlen, newlen); + return backet->data; } return NULL; @@ -201,11 +250,21 @@ hash_release (struct hash *hash, void *data) { if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) { - if (backet == pp) + int oldlen = hash->index[index]->len; + int newlen = oldlen - 1; + + if (backet == pp) hash->index[index] = backet->next; - else + else pp->next = backet->next; + if (hash->index[index]) + hash->index[index]->len = newlen; + else + hash->stats.empty++; + + hash_update_ssq(hash, oldlen, newlen); + ret = backet->data; XFREE (MTYPE_HASH_BACKET, backet); hash->count--; @@ -283,6 +342,9 @@ hash_clean (struct hash *hash, void (*free_func) (void *)) } hash->index[i] = NULL; } + + hash->stats.ssq = 0; + hash->stats.empty = hash->size; } /* Free hash memory. You may call hash_clean before call this @@ -290,140 +352,128 @@ hash_clean (struct hash *hash, void (*free_func) (void *)) void hash_free (struct hash *hash) { - hash_unregister (hash); + pthread_mutex_lock (&_hashes_mtx); + { + if (_hashes) + { + listnode_delete (_hashes, hash); + if (_hashes->count == 0) + { + list_delete (_hashes); + _hashes = NULL; + } + } + } + pthread_mutex_unlock (&_hashes_mtx); + + if (hash->name) + XFREE (MTYPE_HASH, hash->name); + XFREE (MTYPE_HASH_INDEX, hash->index); XFREE (MTYPE_HASH, hash); } -/** - * Calculates some statistics on the given hash table that can be used to - * evaluate performance. - * - * Summary statistics calculated are: - * - * - Load factor: This is the number of elements in the table divided by the - * number of buckets. Since this hash table implementation uses chaining, - * this value can be greater than 1. This number provides information on how - * 'full' the table is, but does not provide information on how evenly - * distributed the elements are. Notably, a load factor >= 1 does not imply - * that every bucket has an element; with a pathological hash function, all - * elements could be in a single bucket. - * - * - Std. Dev.: This is the standard deviation from the load factor. If the LF - * is the mean of number of elements per bucket, the standard deviation - * measures how much any particular bucket is likely to deviate from the - * mean. As a rule of thumb this number should be less than 2, and ideally - * less than 1 for optimal performance. A number larger than 3 generally - * indicates a poor hash function. - * - * - Max: Number of elements in the most overloaded bucket(s). - * - Min: Number of elements in the most underloaded bucket(s). - * - * - Empty: Number of empty buckets - * - Avg: average number of elements among the set of full buckets (like load factor but without empty buckets) - * - * Total number of buckets is precomputed and resides in h->size. - * Total number of elements is precomputed and resides in h->count. - */ -void -hash_stats (struct hash *h, double *lf, double *stddev, int *max, int *min, int *empty, double *avg) -{ - struct hash_backet *hb; // iteration pointer - struct hash_backet *next; // iteration pointer - unsigned int backets = 0; // total number of items in ht - int buckets[h->size]; // # items per bucket - unsigned int full; // # buckets with items - *max = *min = *lf = *stddev = *avg = 0; - *empty = h->size; - - if (h->size == 0 || h->count == 0) - return; - - *empty = 0; - - memset (buckets, 0x00, h->size * sizeof (int)); - - /* collect some important info */ - for (unsigned int i = 0; i < h->size; i++) - { - for (hb = h->index[i]; hb; hb = next) - { - buckets[i]++; - next = hb->next; - backets++; - } - *max = MAX (buckets[i], *max); - *min = MIN (buckets[i], *min); - - if (buckets[i] == 0) - *empty += 1; - } - - assert (backets == h->count); - full = h->size - *empty; - - *lf = h->count / (double) h->size; - *avg = h->count / (double) full; - - if (h->count == 0) - return; - - /* compute population stddev */ - for (unsigned int i = 0; i < h->size; i++) { - if (buckets[i] > 0) - *stddev += pow(((double) buckets[i] - *avg), 2.0); - } - - *stddev = sqrt((1.0/h->size) * *stddev); -} - -void -hash_register (struct hash *h, const char *name) -{ - h->name = name; - listnode_add (_hashes, h); -} - -void -hash_unregister (struct hash *h) -{ - listnode_delete (_hashes, h); -} +/* CLI commands ------------------------------------------------------------ */ DEFUN(show_hash_stats, show_hash_stats_cmd, - "show hashtable ", + "show hashtable [statistics]", SHOW_STR - "Statistics about critical hash tables\n" - "Statistics about critical hash tables\n") + "Statistics about hash tables\n" + "Statistics about hash tables\n") { struct hash *h; struct listnode *ln; struct ttable *tt = ttable_new (&ttable_styles[TTSTYLE_BLANK]); - double lf, stddev, avg; - int max, min, empty; - ttable_add_row (tt, "Hash table|Buckets|Entries|Empty|LF|Mean|SD|Max|Min"); - tt->style.cell.lpad = 1; - tt->style.cell.rpad = 2; + ttable_add_row (tt, "Hash table|Buckets|Entries|Empty|LF|FLF|SD"); + tt->style.cell.lpad = 2; + tt->style.cell.rpad = 1; + tt->style.corner = '+'; ttable_restyle (tt); ttable_rowseps (tt, 0, BOTTOM, true, '-'); + /* Summary statistics calculated are: + * + * - Load factor: This is the number of elements in the table divided by the + * number of buckets. Since this hash table implementation uses chaining, + * this value can be greater than 1. This number provides information on + * how 'full' the table is, but does not provide information on how evenly + * distributed the elements are. Notably, a load factor >= 1 does not imply + * that every bucket has an element; with a pathological hash function, all + * elements could be in a single bucket. + * + * - Full load factor: this is the number of elements in the table divided by + * the number of buckets that have some elements in them. + * + * - Std. Dev.: This is the standard deviation from the full load factor. If + * the FLF is the mean of number of elements per bucket, the standard + * deviation measures how much any particular bucket is likely to deviate + * from the mean. As a rule of thumb this number should be less than 2, and + * ideally <= 1 for optimal performance. A number larger than 3 generally + * indicates a poor hash function. + */ + + long double lf; // load factor + long double flf; // full load factor + long double var; // overall variance + long double fvar; // full variance + long double stdv; // overall stddev + long double fstdv; // full stddev + + long double x2; // h->count ^ 2 + long double ldc; // (long double) h->count + long double full; // h->size - h->stats.empty + long double ssq; // ssq casted to long double + + pthread_mutex_lock (&_hashes_mtx); for (ALL_LIST_ELEMENTS_RO (_hashes, ln, h)) { - if (h->name == NULL) + if (!h->name) continue; - hash_stats (h, &lf, &stddev, &max, &min, &empty, &avg); - ttable_add_row (tt, "%s|%d|%d|%.0f%%|%.2f|%.2f|%.2f|%d|%d", h->name, - h->size, h->count, (empty / (double) h->size)*100, lf, avg, stddev, - max, min); - } + ssq = (long double) h->stats.ssq; + x2 = pow(h->count, 2.0); + ldc = (long double) h->count; + full = h->size - h->stats.empty; + lf = h->count / (double) h->size; + flf = full ? h->count / (double) (full) : 0; + var = ldc ? (1.0 / ldc) * (h->stats.ssq - x2 / ldc) : 0; + fvar = full ? (1.0 / full) * (h->stats.ssq - x2 / full) : 0; + var = (var < .0001) ? 0 : var; + fvar = (fvar < .0001) ? 0 : fvar; + stdv = sqrtl(var); + fstdv = sqrtl(fvar); + + ttable_add_row (tt, "%s|%d|%ld|%.0f%%|%.2Lf|%.2Lf|%.2Lf", h->name, + h->size, h->count, + (h->stats.empty / (double) h->size)*100, lf, flf, fstdv); + } + pthread_mutex_unlock (&_hashes_mtx); + + /* display header */ + char header[] = "Showing hash table statistics for "; + char underln[sizeof(header) + strlen(frr_protonameinst)]; + memset (underln, '-', sizeof(underln)); + underln[sizeof(underln) - 1] = '\0'; + vty_out (vty, "%s%s%s", header, frr_protonameinst, VTY_NEWLINE); + vty_out (vty, "%s%s", underln, VTY_NEWLINE); + + vty_out (vty, "# allocated: %d%s", _hashes->count, VTY_NEWLINE); + vty_out (vty, "# named: %d%s%s", tt->nrows - 1, VTY_NEWLINE, + VTY_NEWLINE); + + if (tt->nrows > 1) + { + ttable_colseps (tt, 0, RIGHT, true, '|'); + char *table = ttable_dump (tt, VTY_NEWLINE); + vty_out (vty, "%s%s", table, VTY_NEWLINE); + XFREE (MTYPE_TMP, table); + } + else + vty_out (vty, "No named hash tables to display.%s", VTY_NEWLINE); - char *table = ttable_dump (tt, VTY_NEWLINE); - vty_out (vty, "%s%s%s", VTY_NEWLINE, table, VTY_NEWLINE); - XFREE (MTYPE_TMP, table); ttable_del (tt); return CMD_SUCCESS; diff --git a/lib/hash.h b/lib/hash.h index 15afc249ab..9395440acb 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -22,6 +22,7 @@ #define _ZEBRA_HASH_H #include "memory.h" +#include "frratomic.h" DECLARE_MTYPE(HASH) DECLARE_MTYPE(HASH_BACKET) @@ -35,6 +36,10 @@ DECLARE_MTYPE(HASH_BACKET) struct hash_backet { + /* if this backet is the head of the linked listed, len denotes the number of + * elements in the list */ + int len; + /* Linked list. */ struct hash_backet *next; @@ -45,6 +50,14 @@ struct hash_backet void *data; }; +struct hashstats +{ + /* number of empty hash buckets */ + _Atomic int empty; + /* sum of squares of bucket length */ + _Atomic uint64_t ssq; +}; + struct hash { /* Hash backet. */ @@ -65,14 +78,18 @@ struct hash /* Backet alloc. */ unsigned long count; + struct hashstats stats; + /* hash name */ - const char *name; + char *name; }; extern struct hash *hash_create (unsigned int (*) (void *), - int (*) (const void *, const void *)); + int (*) (const void *, const void *), + const char *); extern struct hash *hash_create_size (unsigned int, unsigned int (*) (void *), - int (*) (const void *, const void *)); + int (*) (const void *, const void *), + const char *); extern void *hash_get (struct hash *, void *, void * (*) (void *)); extern void *hash_alloc_intern (void *); @@ -90,9 +107,6 @@ extern void hash_free (struct hash *); extern unsigned int string_hash_make (const char *); -extern void hash_stats (struct hash *, double *, double *, int *, int *, int *, double *); extern void hash_cmd_init (void); -extern void hash_register (struct hash *, const char *); -extern void hash_unregister (struct hash *); #endif /* _ZEBRA_HASH_H */ diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index d10861a668..d95a2d2863 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2103,6 +2103,35 @@ DEFUN (vtysh_show_work_queues_daemon, return ret; } +DEFUN (vtysh_show_hashtable, + vtysh_show_hashtable_cmd, + "show hashtable [statistics]", + SHOW_STR + "Statistics about hash tables\n" + "Statistics about hash tables\n") +{ + char cmd[] = "do show hashtable statistics"; + unsigned long i; + int ret = CMD_SUCCESS; + + vty_out (vty, "%sLoad factor (LF) - average number of elements across all " + "buckets%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, "Full load factor (FLF) - average number of elements " + "across full buckets%s%s", VTY_NEWLINE, VTY_NEWLINE); + + vty_out (vty, "Standard deviation (SD) is calculated for both the LF and FLF%s", VTY_NEWLINE); + vty_out (vty, "and indicates the typical deviation of bucket chain length%s", VTY_NEWLINE); + vty_out (vty, "from the value in the corresponding load factor.%s%s", + VTY_NEWLINE, VTY_NEWLINE); + + for (i = 0; i < array_size(vtysh_client); i++) + if ( vtysh_client[i].fd >= 0 ) { + ret = vtysh_client_execute (&vtysh_client[i], cmd, stdout); + fprintf (stdout, "\n"); + } + return ret; +} + DEFUNSH (VTYSH_ZEBRA, vtysh_link_params, vtysh_link_params_cmd, @@ -3575,6 +3604,8 @@ vtysh_init_vty (void) install_element (VIEW_NODE, &vtysh_show_work_queues_cmd); install_element (VIEW_NODE, &vtysh_show_work_queues_daemon_cmd); + install_element (VIEW_NODE, &vtysh_show_hashtable_cmd); + install_element (VIEW_NODE, &vtysh_show_thread_cmd); /* Logging */ From dfd19ccc3a21b18fb09cecff5d87d20a0adbafe3 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 19 Jun 2017 22:49:44 +0000 Subject: [PATCH 3/5] *: update hash_create(), hash_create_size() Signed-off-by: Quentin Young --- bgpd/bgp_advertise.c | 2 +- bgpd/bgp_aspath.c | 2 +- bgpd/bgp_attr.c | 10 +++++----- bgpd/bgp_community.c | 2 +- bgpd/bgp_ecommunity.c | 2 +- bgpd/bgp_lcommunity.c | 2 +- bgpd/bgp_nexthop.c | 2 +- bgpd/bgp_updgrp.c | 4 ++-- bgpd/bgpd.c | 2 +- lib/command.c | 2 +- lib/distribute.c | 3 ++- lib/frr_pthread.c | 2 +- lib/if_rmap.c | 2 +- lib/qobj.c | 2 +- lib/routemap.c | 6 +++--- lib/thread.c | 2 +- nhrpd/nhrp_cache.c | 2 +- nhrpd/nhrp_peer.c | 2 +- nhrpd/nhrp_vc.c | 2 +- nhrpd/reqid.c | 2 +- pimd/pim_iface.c | 2 +- pimd/pim_igmp.c | 2 +- pimd/pim_msdp.c | 4 ++-- pimd/pim_oil.c | 2 +- pimd/pim_upstream.c | 2 +- pimd/pimd.c | 2 +- tests/lib/test_srcdest_table.c | 2 +- zebra/zebra_mpls.c | 4 ++-- 28 files changed, 38 insertions(+), 37 deletions(-) diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index f005b20183..c44e067732 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -267,7 +267,7 @@ bgp_sync_init (struct peer *peer) BGP_ADV_FIFO_INIT (&sync->withdraw); BGP_ADV_FIFO_INIT (&sync->withdraw_low); peer->sync[afi][safi] = sync; - peer->hash[afi][safi] = hash_create (baa_hash_key, baa_hash_cmp); + peer->hash[afi][safi] = hash_create (baa_hash_key, baa_hash_cmp, NULL); } } diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index f9daeb2ed3..f304b3a1b7 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -2134,7 +2134,7 @@ aspath_cmp (const void *arg1, const void *arg2) void aspath_init (void) { - ashash = hash_create_size (32768, aspath_key_make, aspath_cmp); + ashash = hash_create_size (32768, aspath_key_make, aspath_cmp, NULL); } void diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 6a7ec473b8..d343ce236b 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -215,7 +215,7 @@ cluster_unintern (struct cluster_list *cluster) static void cluster_init (void) { - cluster_hash = hash_create (cluster_hash_key_make, cluster_hash_cmp); + cluster_hash = hash_create (cluster_hash_key_make, cluster_hash_cmp, NULL); } static void @@ -403,9 +403,9 @@ encap_hash_cmp (const void *p1, const void *p2) static void encap_init (void) { - encap_hash = hash_create (encap_hash_key_make, encap_hash_cmp); + encap_hash = hash_create (encap_hash_key_make, encap_hash_cmp, NULL); #if ENABLE_BGP_VNC - vnc_hash = hash_create (encap_hash_key_make, encap_hash_cmp); + vnc_hash = hash_create (encap_hash_key_make, encap_hash_cmp, NULL); #endif } @@ -517,7 +517,7 @@ transit_hash_cmp (const void *p1, const void *p2) static void transit_init (void) { - transit_hash = hash_create (transit_hash_key_make, transit_hash_cmp); + transit_hash = hash_create (transit_hash_key_make, transit_hash_cmp, NULL); } static void @@ -765,7 +765,7 @@ attrhash_cmp (const void *p1, const void *p2) static void attrhash_init (void) { - attrhash = hash_create (attrhash_key_make, attrhash_cmp); + attrhash = hash_create (attrhash_key_make, attrhash_cmp, "BGP Attributes"); } /* diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index bd67829d77..be4cdac0ac 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -686,7 +686,7 @@ void community_init (void) { comhash = hash_create ((unsigned int (*) (void *))community_hash_make, - (int (*) (const void *, const void *))community_cmp); + (int (*) (const void *, const void *))community_cmp, NULL); } void diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index fa1ad813f1..0555d1bbe3 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -284,7 +284,7 @@ ecommunity_cmp (const void *arg1, const void *arg2) void ecommunity_init (void) { - ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp); + ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp, NULL); } void diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index 4a969c8b90..23c19f70ce 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -286,7 +286,7 @@ lcommunity_hash (void) void lcommunity_init (void) { - lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp); + lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp, NULL); } void diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 4216a2d49b..d0c4d2c945 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -140,7 +140,7 @@ void bgp_address_init (struct bgp *bgp) { bgp->address_hash = hash_create (bgp_address_hash_key_make, - bgp_address_hash_cmp); + bgp_address_hash_cmp, NULL); } void diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index b1b3336363..722eed91c0 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -87,7 +87,7 @@ sync_init (struct update_subgroup *subgrp) BGP_ADV_FIFO_INIT (&subgrp->sync->update); BGP_ADV_FIFO_INIT (&subgrp->sync->withdraw); BGP_ADV_FIFO_INIT (&subgrp->sync->withdraw_low); - subgrp->hash = hash_create (baa_hash_key, baa_hash_cmp); + subgrp->hash = hash_create (baa_hash_key, baa_hash_cmp, NULL); /* We use a larger buffer for subgrp->work in the event that: * - We RX a BGP_UPDATE where the attributes alone are just @@ -1559,7 +1559,7 @@ update_bgp_group_init (struct bgp *bgp) AF_FOREACH (afid) bgp->update_groups[afid] = hash_create (updgrp_hash_key_make, - updgrp_hash_cmp); + updgrp_hash_cmp, NULL); } void diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 033a3d194d..65b53be653 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2910,7 +2910,7 @@ bgp_create (as_t *as, const char *name, enum bgp_instance_type inst_type) bgp->peer_self->host = XSTRDUP(MTYPE_BGP_PEER_HOST, "Static announcement"); bgp->peer = list_new (); bgp->peer->cmp = (int (*)(void *, void *)) peer_cmp; - bgp->peerhash = hash_create (peer_hash_key_make, peer_hash_cmp); + bgp->peerhash = hash_create (peer_hash_key_make, peer_hash_cmp, NULL); bgp->group = list_new (); bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp; diff --git a/lib/command.c b/lib/command.c index 77fada1636..de8899687c 100644 --- a/lib/command.c +++ b/lib/command.c @@ -233,7 +233,7 @@ install_node (struct cmd_node *node, // add start node struct cmd_token *token = cmd_token_new (START_TKN, CMD_ATTR_NORMAL, NULL, NULL); graph_new_node (node->cmdgraph, token, (void (*)(void *)) &cmd_token_del); - node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp); + node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp, NULL); } /** diff --git a/lib/distribute.c b/lib/distribute.c index c771f018c2..79d7b18ff5 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -522,7 +522,8 @@ void distribute_list_init (int node) { disthash = hash_create (distribute_hash_make, - (int (*) (const void *, const void *)) distribute_cmp); + (int (*) (const void *, const void *)) + distribute_cmp, NULL); /* vtysh command-extraction doesn't grok install_element(node, ) */ if (node == RIP_NODE) { diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 614c722be1..4b9bed4524 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -55,7 +55,7 @@ void frr_pthread_init() pthread_mutex_lock(&pthread_table_mtx); { pthread_table = - hash_create(pthread_table_hash_key, pthread_table_hash_cmp); + hash_create(pthread_table_hash_key, pthread_table_hash_cmp, NULL); } pthread_mutex_unlock(&pthread_table_mtx); } diff --git a/lib/if_rmap.c b/lib/if_rmap.c index f9c6a55d7b..32bebd67ff 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -316,7 +316,7 @@ if_rmap_reset () void if_rmap_init (int node) { - ifrmaphash = hash_create (if_rmap_hash_make, if_rmap_hash_cmp); + ifrmaphash = hash_create (if_rmap_hash_make, if_rmap_hash_cmp, NULL); if (node == RIPNG_NODE) { } else if (node == RIP_NODE) { install_element (RIP_NODE, &if_rmap_cmd); diff --git a/lib/qobj.c b/lib/qobj.c index 4cf7fbca7b..8fa8163970 100644 --- a/lib/qobj.c +++ b/lib/qobj.c @@ -97,7 +97,7 @@ void qobj_init (void) if (!nodes) { pthread_rwlock_init (&nodes_lock, NULL); - nodes = hash_create (qobj_key, qobj_cmp); + nodes = hash_create (qobj_key, qobj_cmp, NULL); } } diff --git a/lib/routemap.c b/lib/routemap.c index 9eb28888ad..caba8afd71 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1767,7 +1767,7 @@ route_map_dep_hash_alloc(void *p) dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep)); dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); dep_entry->dep_rmap_hash = hash_create(route_map_dep_hash_make_key, - route_map_rmap_hash_cmp); + route_map_rmap_hash_cmp, NULL); dep_entry->this_hash = NULL; return((void *)dep_entry); @@ -2986,11 +2986,11 @@ route_map_init (void) /* Make vector for match and set. */ route_match_vec = vector_init (1); route_set_vec = vector_init (1); - route_map_master_hash = hash_create(route_map_hash_key_make, route_map_hash_cmp); + route_map_master_hash = hash_create(route_map_hash_key_make, route_map_hash_cmp, NULL); for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) route_map_dep_hash[i] = hash_create(route_map_dep_hash_make_key, - route_map_dep_hash_cmp); + route_map_dep_hash_cmp, NULL); cmd_variable_handler_register(rmap_var_handlers); diff --git a/lib/thread.c b/lib/thread.c index 71b0bb2aed..4e72d4c96f 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -388,7 +388,7 @@ thread_master_create (const char *name) rv->cpu_record = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, (int (*) (const void *, const void *)) - cpu_record_hash_cmp); + cpu_record_hash_cmp, NULL); /* Initialize the timer queues */ diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c index 2d92842b5c..bd884bbc51 100644 --- a/nhrpd/nhrp_cache.c +++ b/nhrpd/nhrp_cache.c @@ -81,7 +81,7 @@ struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote struct nhrp_cache key; if (!nifp->cache_hash) { - nifp->cache_hash = hash_create(nhrp_cache_protocol_key, nhrp_cache_protocol_cmp); + nifp->cache_hash = hash_create(nhrp_cache_protocol_key, nhrp_cache_protocol_cmp, NULL); if (!nifp->cache_hash) return NULL; } diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index d9e8627a14..95541b88b6 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -182,7 +182,7 @@ struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *re struct nhrp_vc *vc; if (!nifp->peer_hash) { - nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp); + nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp, NULL); if (!nifp->peer_hash) return NULL; } diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c index 57fb462ab6..a5547a7a7e 100644 --- a/nhrpd/nhrp_vc.c +++ b/nhrpd/nhrp_vc.c @@ -196,7 +196,7 @@ void nhrp_vc_init(void) { size_t i; - nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp); + nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp, NULL); for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) list_init(&childlist_head[i]); } diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c index 24b3199397..61fbfd7795 100644 --- a/nhrpd/reqid.c +++ b/nhrpd/reqid.c @@ -17,7 +17,7 @@ static int nhrp_reqid_cmp(const void *data, const void *key) uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *p, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *)) { if (!p->reqid_hash) { - p->reqid_hash = hash_create(nhrp_reqid_key, nhrp_reqid_cmp); + p->reqid_hash = hash_create(nhrp_reqid_key, nhrp_reqid_cmp, NULL); p->next_request_id = 1; } diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index afaa951724..f4125af9b4 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -180,7 +180,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) pim_ifp->pim_ifchannel_list->cmp = (int (*)(void *, void *)) pim_ifchannel_compare; pim_ifp->pim_ifchannel_hash = hash_create (pim_ifchannel_hash_key, - pim_ifchannel_equal); + pim_ifchannel_equal, NULL); ifp->info = pim_ifp; diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index c883a2c8bb..ae5f365b82 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -822,7 +822,7 @@ static struct igmp_sock *igmp_sock_new(int fd, igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free; igmp->igmp_group_hash = hash_create (igmp_group_hash_key, - igmp_group_hash_equal); + igmp_group_hash_equal, NULL); igmp->fd = fd; igmp->interface = ifp; diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 18e24dae32..71a2869818 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -1570,13 +1570,13 @@ pim_msdp_init(struct thread_master *master) msdp->master = master; msdp->peer_hash = hash_create(pim_msdp_peer_hash_key_make, - pim_msdp_peer_hash_eq); + pim_msdp_peer_hash_eq, NULL); msdp->peer_list = list_new(); msdp->peer_list->del = (void (*)(void *))pim_msdp_peer_free; msdp->peer_list->cmp = (int (*)(void *, void *))pim_msdp_peer_comp; msdp->sa_hash = hash_create(pim_msdp_sa_hash_key_make, - pim_msdp_sa_hash_eq); + pim_msdp_sa_hash_eq, NULL); msdp->sa_list = list_new(); msdp->sa_list->del = (void (*)(void *))pim_msdp_sa_free; msdp->sa_list->cmp = (int (*)(void *, void *))pim_msdp_sa_comp; diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 71ca576d93..66be2be6f5 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -103,7 +103,7 @@ void pim_oil_init (void) { pim_channel_oil_hash = hash_create_size (8192, pim_oil_hash_key, - pim_oil_equal); + pim_oil_equal, NULL); pim_channel_oil_list = list_new(); if (!pim_channel_oil_list) { diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index d7ebdea45d..442cb02a15 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -1760,7 +1760,7 @@ pim_upstream_init (void) pim_upstream_hash_key, pim_upstream_sg_running); pim_upstream_hash = hash_create_size (8192, pim_upstream_hash_key, - pim_upstream_equal); + pim_upstream_equal, NULL); pim_upstream_list = list_new (); pim_upstream_list->del = (void (*)(void *)) pim_upstream_free; diff --git a/pimd/pimd.c b/pimd/pimd.c index ec1fe5b6d0..b1d566f51b 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -250,7 +250,7 @@ pim_instance_init (vrf_id_t vrf_id, afi_t afi) pim->spt.switchover = PIM_SPT_IMMEDIATE; pim->spt.plist = NULL; - pim->rpf_hash = hash_create_size (256, pim_rpf_hash_key, pim_rpf_equal); + pim->rpf_hash = hash_create_size (256, pim_rpf_hash_key, pim_rpf_equal, NULL); if (PIM_DEBUG_ZEBRA) zlog_debug ("%s: NHT rpf hash init ", __PRETTY_FUNCTION__); diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c index 07f60668e7..792e2696e9 100644 --- a/tests/lib/test_srcdest_table.c +++ b/tests/lib/test_srcdest_table.c @@ -140,7 +140,7 @@ test_state_new(void) rv->table = srcdest_table_init(); assert(rv->table); - rv->log = hash_create(log_key, log_cmp); + rv->log = hash_create(log_key, log_cmp, NULL); return rv; } diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index a8e7f5372c..e08ff08cf6 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -2994,8 +2994,8 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) { if (!zvrf) return; - zvrf->slsp_table = hash_create(label_hash, label_cmp); - zvrf->lsp_table = hash_create(label_hash, label_cmp); + zvrf->slsp_table = hash_create(label_hash, label_cmp, NULL); + zvrf->lsp_table = hash_create(label_hash, label_cmp, NULL); zvrf->fec_table[AFI_IP] = route_table_init(); zvrf->fec_table[AFI_IP6] = route_table_init(); zvrf->mpls_flags = 0; From b3db0a22b7ccd094cc6dbae4d0adff0f2db88048 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 23 Jun 2017 05:14:54 +0000 Subject: [PATCH 4/5] lib: use doubles instead of long doubles NetBSD can't take the square root of a long double and we should be fine just using a double here anyway Signed-off-by: Quentin Young --- lib/hash.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/hash.c b/lib/hash.c index 832b02cc37..57cc87be12 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -387,7 +387,7 @@ DEFUN(show_hash_stats, struct listnode *ln; struct ttable *tt = ttable_new (&ttable_styles[TTSTYLE_BLANK]); - ttable_add_row (tt, "Hash table|Buckets|Entries|Empty|LF|FLF|SD"); + ttable_add_row (tt, "Hash table|Buckets|Entries|Empty|LF|SD|FLF|SD"); tt->style.cell.lpad = 2; tt->style.cell.rpad = 1; tt->style.corner = '+'; @@ -407,20 +407,20 @@ DEFUN(show_hash_stats, * - Full load factor: this is the number of elements in the table divided by * the number of buckets that have some elements in them. * - * - Std. Dev.: This is the standard deviation from the full load factor. If - * the FLF is the mean of number of elements per bucket, the standard - * deviation measures how much any particular bucket is likely to deviate - * from the mean. As a rule of thumb this number should be less than 2, and - * ideally <= 1 for optimal performance. A number larger than 3 generally - * indicates a poor hash function. + * - Std. Dev.: This is the standard deviation calculated from the relevant + * load factor. If the load factor is the mean of number of elements per + * bucket, the standard deviation measures how much any particular bucket + * is likely to deviate from the mean. As a rule of thumb this number + * should be less than 2, and ideally <= 1 for optimal performance. A + * number larger than 3 generally indicates a poor hash function. */ - long double lf; // load factor - long double flf; // full load factor - long double var; // overall variance - long double fvar; // full variance - long double stdv; // overall stddev - long double fstdv; // full stddev + double lf; // load factor + double flf; // full load factor + double var; // overall variance + double fvar; // full variance + double stdv; // overall stddev + double fstdv; // full stddev long double x2; // h->count ^ 2 long double ldc; // (long double) h->count @@ -434,21 +434,22 @@ DEFUN(show_hash_stats, continue; ssq = (long double) h->stats.ssq; - x2 = pow(h->count, 2.0); + x2 = powl(h->count, 2.0); ldc = (long double) h->count; full = h->size - h->stats.empty; lf = h->count / (double) h->size; flf = full ? h->count / (double) (full) : 0; - var = ldc ? (1.0 / ldc) * (h->stats.ssq - x2 / ldc) : 0; - fvar = full ? (1.0 / full) * (h->stats.ssq - x2 / full) : 0; + var = ldc ? (1.0 / ldc) * (ssq - x2 / ldc) : 0; + fvar = full ? (1.0 / full) * (ssq - x2 / full) : 0; var = (var < .0001) ? 0 : var; fvar = (fvar < .0001) ? 0 : fvar; - stdv = sqrtl(var); - fstdv = sqrtl(fvar); + stdv = sqrt(var); + fstdv = sqrt(fvar); - ttable_add_row (tt, "%s|%d|%ld|%.0f%%|%.2Lf|%.2Lf|%.2Lf", h->name, + ttable_add_row (tt, "%s|%d|%ld|%.0f%%|%.2lf|%.2lf|%.2lf|%.2lf", h->name, h->size, h->count, - (h->stats.empty / (double) h->size)*100, lf, flf, fstdv); + (h->stats.empty / (double) h->size)*100, lf, stdv, flf, + fstdv); } pthread_mutex_unlock (&_hashes_mtx); From e703a0f3afd3469afdc57994a38245cf2ada4e00 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 30 Jun 2017 16:56:10 +0000 Subject: [PATCH 5/5] lib, vtysh: rebase hashstats Signed-off-by: Quentin Young --- lib/hash.c | 15 +++++++-------- vtysh/vtysh.c | 14 ++++++-------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/hash.c b/lib/hash.c index 57cc87be12..95643bbae0 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -458,22 +458,21 @@ DEFUN(show_hash_stats, char underln[sizeof(header) + strlen(frr_protonameinst)]; memset (underln, '-', sizeof(underln)); underln[sizeof(underln) - 1] = '\0'; - vty_out (vty, "%s%s%s", header, frr_protonameinst, VTY_NEWLINE); - vty_out (vty, "%s%s", underln, VTY_NEWLINE); + vty_outln (vty, "%s%s", header, frr_protonameinst); + vty_outln (vty, "%s", underln); - vty_out (vty, "# allocated: %d%s", _hashes->count, VTY_NEWLINE); - vty_out (vty, "# named: %d%s%s", tt->nrows - 1, VTY_NEWLINE, - VTY_NEWLINE); + vty_outln (vty, "# allocated: %d", _hashes->count); + vty_outln (vty, "# named: %d%s", tt->nrows - 1, VTYNL); if (tt->nrows > 1) { ttable_colseps (tt, 0, RIGHT, true, '|'); - char *table = ttable_dump (tt, VTY_NEWLINE); - vty_out (vty, "%s%s", table, VTY_NEWLINE); + char *table = ttable_dump (tt, VTYNL); + vty_out (vty, "%s%s", table, VTYNL); XFREE (MTYPE_TMP, table); } else - vty_out (vty, "No named hash tables to display.%s", VTY_NEWLINE); + vty_outln (vty, "No named hash tables to display."); ttable_del (tt); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index d95a2d2863..0e04f4bf8e 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2114,15 +2114,13 @@ DEFUN (vtysh_show_hashtable, unsigned long i; int ret = CMD_SUCCESS; - vty_out (vty, "%sLoad factor (LF) - average number of elements across all " - "buckets%s", VTY_NEWLINE, VTY_NEWLINE); - vty_out (vty, "Full load factor (FLF) - average number of elements " - "across full buckets%s%s", VTY_NEWLINE, VTY_NEWLINE); + fprintf (stdout, "\n"); + fprintf (stdout, "Load factor (LF) - average number of elements across all buckets\n"); + fprintf (stdout, "Full load factor (FLF) - average number of elements across full buckets\n\n"); - vty_out (vty, "Standard deviation (SD) is calculated for both the LF and FLF%s", VTY_NEWLINE); - vty_out (vty, "and indicates the typical deviation of bucket chain length%s", VTY_NEWLINE); - vty_out (vty, "from the value in the corresponding load factor.%s%s", - VTY_NEWLINE, VTY_NEWLINE); + fprintf (stdout, "Standard deviation (SD) is calculated for both the LF and FLF\n"); + fprintf (stdout, "and indicates the typical deviation of bucket chain length\n"); + fprintf (stdout, "from the value in the corresponding load factor.\n\n"); for (i = 0; i < array_size(vtysh_client); i++) if ( vtysh_client[i].fd >= 0 ) {