hashtable: Optionally collect and report profiling data

This commit is contained in:
Tobias Brunner 2020-04-24 10:56:50 +02:00
parent 87ceaefe2f
commit 31e6ca78df
1 changed files with 94 additions and 0 deletions

View File

@ -16,6 +16,10 @@
#include "hashtable.h"
#include <utils/chunk.h>
#include <utils/debug.h>
#ifdef HASHTABLE_PROFILER
#include <utils/backtrace.h>
#endif
/** The minimum size of the hash table (MUST be a power of 2) */
#define MIN_SIZE 8
@ -114,8 +118,64 @@ struct private_hashtable_t {
* Alternative comparison function.
*/
hashtable_cmp_t cmp;
#ifdef HASHTABLE_PROFILER
/**
* Some stats to profile lookups in the table
*/
struct {
size_t count;
size_t probes;
size_t longest;
} success, failure;
/**
* Stats on the memory usage of the table
*/
struct {
size_t count;
size_t size;
} max;
/**
* Keep track of where the hash table was created
*/
backtrace_t *backtrace;
#endif
};
#ifdef HASHTABLE_PROFILER
#define lookup_start() \
u_int _lookup_probes = 0;
#define lookup_probing() \
_lookup_probes++;
#define _lookup_done(table, result) \
table->result.count++; \
table->result.probes += _lookup_probes; \
table->result.longest = max(table->result.longest, _lookup_probes);
#define lookup_success(table) _lookup_done(table, success);
#define lookup_failure(table) _lookup_done(table, failure);
#define profile_size(table) \
table->max.size = max(table->max.size, table->size);
#define profile_count(table) \
table->max.count = max(table->max.count, table->count);
#else
#define lookup_start(...) {}
#define lookup_probing(...) {}
#define lookup_success(...) {}
#define lookup_failure(...) {}
#define profile_size(...) {}
#define profile_count(...) {}
#endif
typedef struct private_enumerator_t private_enumerator_t;
/**
@ -212,6 +272,7 @@ static void init_hashtable(private_hashtable_t *this, u_int size)
size = max(MIN_SIZE, min(size, MAX_SIZE));
this->size = get_nearest_powerof2(size);
this->mask = this->size - 1;
profile_size(this);
this->table = calloc(this->size, sizeof(pair_t*));
}
@ -294,9 +355,12 @@ static inline pair_t *find_key(private_hashtable_t *this, const void *key,
*out_hash = hash;
}
lookup_start();
pair = this->table[hash & this->mask];
while (pair)
{
lookup_probing();
/* when keys are ordered, we compare all items so we can abort earlier
* even if the hash does not match, but only as long as we don't
* have a callback */
@ -324,6 +388,14 @@ static inline pair_t *find_key(private_hashtable_t *this, const void *key,
{
*out_prev = prev;
}
if (pair)
{
lookup_success(this);
}
else
{
lookup_failure(this);
}
return pair;
}
@ -360,6 +432,7 @@ METHOD(hashtable_t, put, void*,
this->table[hash & this->mask] = pair;
}
this->count++;
profile_count(this);
}
return old_value;
}
@ -490,6 +563,23 @@ static void destroy_internal(private_hashtable_t *this,
pair_t *pair, *next;
u_int row;
#ifdef HASHTABLE_PROFILER
if (this->success.count || this->failure.count)
{
fprintf(stderr, "%zu elements [max. %zu], %zu buckets [%zu], %zu "
"successful / %zu failed lookups, %.4f [%zu] / %.4f "
"[%zu] avg. probes in table created at:",
this->count, this->max.count, this->size, this->max.size,
this->success.count, this->failure.count,
(double)this->success.probes/this->success.count,
this->success.longest,
(double)this->failure.probes/this->failure.count,
this->failure.longest);
this->backtrace->log(this->backtrace, stderr, TRUE);
}
this->backtrace->destroy(this->backtrace);
#endif
for (row = 0; row < this->size; row++)
{
pair = this->table[row];
@ -545,6 +635,10 @@ static private_hashtable_t *hashtable_create_internal(hashtable_hash_t hash,
init_hashtable(this, size);
#ifdef HASHTABLE_PROFILER
this->backtrace = backtrace_create(3);
#endif
return this;
}