diff options
Diffstat (limited to 'lib/hash.c')
| -rw-r--r-- | lib/hash.c | 695 |
1 files changed, 340 insertions, 355 deletions
diff --git a/lib/hash.c b/lib/hash.c index 7c355b2d83..7adbd908d8 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -29,343 +29,323 @@ #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") +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 *), - const char *name) +struct hash *hash_create_size(unsigned int size, + unsigned int (*hash_key)(void *), + int (*hash_cmp)(const void *, const void *), + const char *name) { - struct hash *hash; - - assert ((size & (size-1)) == 0); - hash = XCALLOC (MTYPE_HASH, sizeof (struct hash)); - hash->index = XCALLOC (MTYPE_HASH_INDEX, - sizeof (struct hash_backet *) * size); - hash->size = size; - hash->no_expand = 0; - hash->hash_key = hash_key; - hash->hash_cmp = hash_cmp; - hash->count = 0; - 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; + struct hash *hash; + + assert((size & (size - 1)) == 0); + hash = XCALLOC(MTYPE_HASH, sizeof(struct hash)); + hash->index = + XCALLOC(MTYPE_HASH_INDEX, sizeof(struct hash_backet *) * size); + hash->size = size; + hash->no_expand = 0; + hash->hash_key = hash_key; + hash->hash_cmp = hash_cmp; + hash->count = 0; + 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; } /* Allocate a new hash with default hash size. */ -struct hash * -hash_create (unsigned int (*hash_key) (void *), - int (*hash_cmp) (const void *, const void *), - const char *name) +struct hash *hash_create(unsigned int (*hash_key)(void *), + int (*hash_cmp)(const void *, const void *), + const char *name) { - return hash_create_size (HASH_INITIAL_SIZE, hash_key, hash_cmp, name); + return hash_create_size(HASH_INITIAL_SIZE, hash_key, hash_cmp, name); } /* Utility function for hash_get(). When this function is specified as alloc_func, return arugment as it is. This function is used for intern already allocated value. */ -void * -hash_alloc_intern (void *arg) +void *hash_alloc_intern(void *arg) { - return arg; + return arg; } -#define hash_update_ssq(hz, old, new) \ - atomic_fetch_add_explicit(&hz->stats.ssq, (new + old)*(new - old),\ - memory_order_relaxed); +#define hash_update_ssq(hz, old, new) \ + atomic_fetch_add_explicit(&hz->stats.ssq, (new + old) * (new - old), \ + memory_order_relaxed); /* Expand hash if the chain length exceeds the threshold. */ -static void hash_expand (struct hash *hash) +static void hash_expand(struct hash *hash) { - unsigned int i, new_size, losers; - struct hash_backet *hb, *hbnext, **new_index; + unsigned int i, new_size, losers; + struct hash_backet *hb, *hbnext, **new_index; - new_size = hash->size * 2; - new_index = XCALLOC(MTYPE_HASH_INDEX, sizeof(struct hash_backet *) * new_size); - if (new_index == NULL) - return; + new_size = hash->size * 2; + new_index = XCALLOC(MTYPE_HASH_INDEX, + sizeof(struct hash_backet *) * new_size); + if (new_index == NULL) + return; - hash->stats.empty = new_size; + hash->stats.empty = new_size; - for (i = 0; i < hash->size; i++) - for (hb = hash->index[i]; hb; hb = hbnext) - { - unsigned int h = hb->key & (new_size - 1); + for (i = 0; i < hash->size; i++) + for (hb = hash->index[i]; hb; hb = hbnext) { + unsigned int h = hb->key & (new_size - 1); - hbnext = hb->next; - hb->next = new_index[h]; + hbnext = hb->next; + hb->next = new_index[h]; - int oldlen = hb->next ? hb->next->len : 0; - int newlen = oldlen + 1; + int oldlen = hb->next ? hb->next->len : 0; + int newlen = oldlen + 1; - if (newlen == 1) - hash->stats.empty--; - else - hb->next->len = 0; + if (newlen == 1) + hash->stats.empty--; + else + hb->next->len = 0; - hb->len = newlen; + hb->len = newlen; - hash_update_ssq(hash, oldlen, newlen); + hash_update_ssq(hash, oldlen, newlen); - new_index[h] = hb; - } + new_index[h] = hb; + } - /* Switch to new table */ - XFREE(MTYPE_HASH_INDEX, hash->index); - hash->size = new_size; - hash->index = new_index; + /* Switch to new table */ + XFREE(MTYPE_HASH_INDEX, hash->index); + hash->size = new_size; + 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. */ - losers = 0; - for (i = 0; i < hash->size; i++) - { - unsigned int len = hash->index[i] ? hash->index[i]->len : 0; + /* 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. */ + losers = 0; + for (i = 0; i < hash->size; i++) { + 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 (len > HASH_THRESHOLD / 2) + ++losers; + if (len >= HASH_THRESHOLD) + hash->no_expand = 1; + } - if (losers > hash->count / 2) - hash->no_expand = 1; + if (losers > hash->count / 2) + hash->no_expand = 1; } /* Lookup and return hash backet in hash. If there is no corresponding hash backet and alloc_func is specified, create new hash backet. */ -void * -hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) +void *hash_get(struct hash *hash, void *data, void *(*alloc_func)(void *)) { - unsigned int key; - unsigned int index; - void *newdata; - unsigned int len; - struct hash_backet *backet; - - key = (*hash->hash_key) (data); - index = key & (hash->size - 1); - len = 0; - - for (backet = hash->index[index]; backet != NULL; backet = backet->next) - { - if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) - return backet->data; - ++len; - } - - if (alloc_func) - { - newdata = (*alloc_func) (data); - if (newdata == NULL) - return NULL; - - if (len > HASH_THRESHOLD && !hash->no_expand) - { - hash_expand (hash); - index = key & (hash->size - 1); + unsigned int key; + unsigned int index; + void *newdata; + unsigned int len; + struct hash_backet *backet; + + key = (*hash->hash_key)(data); + index = key & (hash->size - 1); + len = 0; + + for (backet = hash->index[index]; backet != NULL; + backet = backet->next) { + if (backet->key == key && (*hash->hash_cmp)(backet->data, data)) + return backet->data; + ++len; } - 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++; + if (alloc_func) { + newdata = (*alloc_func)(data); + if (newdata == NULL) + return NULL; - int oldlen = backet->next ? backet->next->len : 0; - int newlen = oldlen + 1; + if (len > HASH_THRESHOLD && !hash->no_expand) { + hash_expand(hash); + index = key & (hash->size - 1); + } - if (newlen == 1) - hash->stats.empty--; - else - backet->next->len = 0; + 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++; - backet->len = newlen; + int oldlen = backet->next ? backet->next->len : 0; + int newlen = oldlen + 1; - hash_update_ssq(hash, oldlen, newlen); + if (newlen == 1) + hash->stats.empty--; + else + backet->next->len = 0; - return backet->data; - } - return NULL; + backet->len = newlen; + + hash_update_ssq(hash, oldlen, newlen); + + return backet->data; + } + return NULL; } /* Hash lookup. */ -void * -hash_lookup (struct hash *hash, void *data) +void *hash_lookup(struct hash *hash, void *data) { - return hash_get (hash, data, NULL); + return hash_get(hash, data, NULL); } /* Simple Bernstein hash which is simple and fast for common case */ -unsigned int string_hash_make (const char *str) +unsigned int string_hash_make(const char *str) { - unsigned int hash = 0; + unsigned int hash = 0; - while (*str) - hash = (hash * 33) ^ (unsigned int) *str++; + while (*str) + hash = (hash * 33) ^ (unsigned int)*str++; - return hash; + return hash; } /* This function release registered value from specified hash. When release is successfully finished, return the data pointer in the hash backet. */ -void * -hash_release (struct hash *hash, void *data) +void *hash_release(struct hash *hash, void *data) { - void *ret; - unsigned int key; - unsigned int index; - struct hash_backet *backet; - struct hash_backet *pp; - - key = (*hash->hash_key) (data); - index = key & (hash->size - 1); - - for (backet = pp = hash->index[index]; backet; backet = backet->next) - { - if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) - { - int oldlen = hash->index[index]->len; - int newlen = oldlen - 1; - - if (backet == pp) - hash->index[index] = backet->next; - 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--; - return ret; + void *ret; + unsigned int key; + unsigned int index; + struct hash_backet *backet; + struct hash_backet *pp; + + key = (*hash->hash_key)(data); + index = key & (hash->size - 1); + + for (backet = pp = hash->index[index]; backet; backet = backet->next) { + if (backet->key == key + && (*hash->hash_cmp)(backet->data, data)) { + int oldlen = hash->index[index]->len; + int newlen = oldlen - 1; + + if (backet == pp) + hash->index[index] = backet->next; + 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--; + return ret; + } + pp = backet; } - pp = backet; - } - return NULL; + return NULL; } /* Iterator function for hash. */ -void -hash_iterate (struct hash *hash, - void (*func) (struct hash_backet *, void *), void *arg) +void hash_iterate(struct hash *hash, void (*func)(struct hash_backet *, void *), + void *arg) { - unsigned int i; - struct hash_backet *hb; - struct hash_backet *hbnext; - - for (i = 0; i < hash->size; i++) - for (hb = hash->index[i]; hb; hb = hbnext) - { - /* get pointer to next hash backet here, in case (*func) - * decides to delete hb by calling hash_release - */ - hbnext = hb->next; - (*func) (hb, arg); - } + unsigned int i; + struct hash_backet *hb; + struct hash_backet *hbnext; + + for (i = 0; i < hash->size; i++) + for (hb = hash->index[i]; hb; hb = hbnext) { + /* get pointer to next hash backet here, in case (*func) + * decides to delete hb by calling hash_release + */ + hbnext = hb->next; + (*func)(hb, arg); + } } /* Iterator function for hash. */ -void -hash_walk (struct hash *hash, - int (*func) (struct hash_backet *, void *), void *arg) +void hash_walk(struct hash *hash, int (*func)(struct hash_backet *, void *), + void *arg) { - unsigned int i; - struct hash_backet *hb; - struct hash_backet *hbnext; - int ret = HASHWALK_CONTINUE; - - for (i = 0; i < hash->size; i++) - { - for (hb = hash->index[i]; hb; hb = hbnext) - { - /* get pointer to next hash backet here, in case (*func) - * decides to delete hb by calling hash_release - */ - hbnext = hb->next; - ret = (*func) (hb, arg); - if (ret == HASHWALK_ABORT) - return; + unsigned int i; + struct hash_backet *hb; + struct hash_backet *hbnext; + int ret = HASHWALK_CONTINUE; + + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hbnext) { + /* get pointer to next hash backet here, in case (*func) + * decides to delete hb by calling hash_release + */ + hbnext = hb->next; + ret = (*func)(hb, arg); + if (ret == HASHWALK_ABORT) + return; + } } - } } /* Clean up hash. */ -void -hash_clean (struct hash *hash, void (*free_func) (void *)) +void hash_clean(struct hash *hash, void (*free_func)(void *)) { - unsigned int i; - struct hash_backet *hb; - struct hash_backet *next; + unsigned int i; + struct hash_backet *hb; + struct hash_backet *next; - for (i = 0; i < hash->size; i++) - { - for (hb = hash->index[i]; hb; hb = next) - { - next = hb->next; - - if (free_func) - (*free_func) (hb->data); + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = next) { + next = hb->next; + + if (free_func) + (*free_func)(hb->data); - XFREE (MTYPE_HASH_BACKET, hb); - hash->count--; + XFREE(MTYPE_HASH_BACKET, hb); + hash->count--; + } + hash->index[i] = NULL; } - hash->index[i] = NULL; - } - hash->stats.ssq = 0; - hash->stats.empty = hash->size; + hash->stats.ssq = 0; + hash->stats.empty = hash->size; } /* Free hash memory. You may call hash_clean before call this function. */ -void -hash_free (struct hash *hash) +void hash_free(struct hash *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); + 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); } @@ -378,111 +358,116 @@ DEFUN(show_hash_stats, "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]); - - 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 = '+'; - 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 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. - */ - - 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 - long double full; // h->size - h->stats.empty - long double ssq; // ssq casted to long double - - pthread_mutex_lock (&_hashes_mtx); - if (!_hashes) - { - pthread_mutex_unlock (&_hashes_mtx); - vty_out (vty, "No hash tables in use.\n"); - return CMD_SUCCESS; - } - - for (ALL_LIST_ELEMENTS_RO (_hashes, ln, h)) - { - if (!h->name) - continue; - - ssq = (long double) h->stats.ssq; - 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) * (ssq - x2 / ldc) : 0; - fvar = full ? (1.0 / full) * (ssq - x2 / full) : 0; - var = (var < .0001) ? 0 : var; - fvar = (fvar < .0001) ? 0 : fvar; - stdv = sqrt(var); - fstdv = sqrt(fvar); - - 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, stdv, 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\n", header, frr_protonameinst); - vty_out (vty, "%s\n", underln); - - vty_out (vty, "# allocated: %d\n", _hashes->count); - vty_out (vty, "# named: %d\n\n", tt->nrows - 1); - - if (tt->nrows > 1) - { - ttable_colseps (tt, 0, RIGHT, true, '|'); - char *table = ttable_dump (tt, "\n"); - vty_out (vty, "%s\n", table); - XFREE (MTYPE_TMP, table); - } - else - vty_out (vty, "No named hash tables to display.\n"); - - ttable_del (tt); - - return CMD_SUCCESS; + struct hash *h; + struct listnode *ln; + struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + + 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 = '+'; + 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 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. + */ + + 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 + long double full; // h->size - h->stats.empty + long double ssq; // ssq casted to long double + + pthread_mutex_lock(&_hashes_mtx); + if (!_hashes) { + pthread_mutex_unlock(&_hashes_mtx); + vty_out(vty, "No hash tables in use.\n"); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO(_hashes, ln, h)) { + if (!h->name) + continue; + + ssq = (long double)h->stats.ssq; + 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) * (ssq - x2 / ldc) : 0; + fvar = full ? (1.0 / full) * (ssq - x2 / full) : 0; + var = (var < .0001) ? 0 : var; + fvar = (fvar < .0001) ? 0 : fvar; + stdv = sqrt(var); + fstdv = sqrt(fvar); + + 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, + stdv, 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\n", header, frr_protonameinst); + vty_out(vty, "%s\n", underln); + + vty_out(vty, "# allocated: %d\n", _hashes->count); + vty_out(vty, "# named: %d\n\n", tt->nrows - 1); + + if (tt->nrows > 1) { + ttable_colseps(tt, 0, RIGHT, true, '|'); + char *table = ttable_dump(tt, "\n"); + vty_out(vty, "%s\n", table); + XFREE(MTYPE_TMP, table); + } else + vty_out(vty, "No named hash tables to display.\n"); + + ttable_del(tt); + + return CMD_SUCCESS; } -void -hash_cmd_init () +void hash_cmd_init() { - install_element (ENABLE_NODE, &show_hash_stats_cmd); + install_element(ENABLE_NODE, &show_hash_stats_cmd); } |
