Replaced linked_list_t usage in hashtable_t with custom list implementation.

With this change inserting elements into a hashtable_t object is now
nearly as fast as inserting them into a linked_list_t object, whereas
before it was up to seven times slower.  Additionally, the memory
footprint of a hashtable is now significantly smaller.  The lookup
performance is also nearly doubled.
This commit is contained in:
Tobias Brunner 2011-05-23 18:35:21 +02:00
parent c74ece334d
commit 8af0177189
1 changed files with 92 additions and 124 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2008-2010 Tobias Brunner * Copyright (C) 2008-2011 Tobias Brunner
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@ -13,7 +13,6 @@
* for more details. * for more details.
*/ */
#include <utils/linked_list.h>
#include "hashtable.h" #include "hashtable.h"
@ -40,12 +39,17 @@ struct pair_t {
* Cached hash (used in case of a resize). * Cached hash (used in case of a resize).
*/ */
u_int hash; u_int hash;
/**
* Next pair in an overflow list.
*/
pair_t *next;
}; };
/** /**
* Creates an empty pair object. * Creates an empty pair object.
*/ */
pair_t *pair_create(void *key, void *value, u_int hash) static inline pair_t *pair_create(void *key, void *value, u_int hash)
{ {
pair_t *this; pair_t *this;
@ -93,7 +97,7 @@ struct private_hashtable_t {
/** /**
* The actual table. * The actual table.
*/ */
linked_list_t **table; pair_t **table;
/** /**
* The hashing function. * The hashing function.
@ -131,21 +135,14 @@ struct private_enumerator_t {
/** /**
* current pair * current pair
*/ */
pair_t *pair; pair_t *current;
/** /**
* enumerator for the current row * previous pair (used by remove_at)
*/ */
enumerator_t *current; pair_t *prev;
};
/** };
* Compare a pair in a list with the given key.
*/
static inline bool pair_equals(pair_t *pair, private_hashtable_t *this, void *key)
{
return this->equals(key, pair->key);
}
/** /**
* This function returns the next-highest power of two for the given number. * This function returns the next-highest power of two for the given number.
@ -175,7 +172,7 @@ static void init_hashtable(private_hashtable_t *this, u_int capacity)
this->mask = this->capacity - 1; this->mask = this->capacity - 1;
this->load_factor = 0.75; this->load_factor = 0.75;
this->table = calloc(this->capacity, sizeof(linked_list_t*)); this->table = calloc(this->capacity, sizeof(pair_t*));
} }
/** /**
@ -183,7 +180,7 @@ static void init_hashtable(private_hashtable_t *this, u_int capacity)
*/ */
static void rehash(private_hashtable_t *this) static void rehash(private_hashtable_t *this)
{ {
linked_list_t **old_table; pair_t **old_table;
u_int row, old_capacity; u_int row, old_capacity;
if (this->capacity >= MAX_CAPACITY) if (this->capacity >= MAX_CAPACITY)
@ -198,29 +195,17 @@ static void rehash(private_hashtable_t *this)
for (row = 0; row < old_capacity; row++) for (row = 0; row < old_capacity; row++)
{ {
enumerator_t *enumerator; pair_t *pair, *next;
linked_list_t *list, *new_list;
pair_t *pair;
u_int new_row; u_int new_row;
list = old_table[row]; pair = old_table[row];
if (list) while (pair)
{ { /* insert pair at the front of new bucket*/
enumerator = list->create_enumerator(list); next = pair->next;
while (enumerator->enumerate(enumerator, &pair)) new_row = pair->hash & this->mask;
{ pair->next = this->table[new_row];
new_row = pair->hash & this->mask; this->table[new_row] = pair;
pair = next;
list->remove_at(list, enumerator);
new_list = this->table[new_row];
if (!new_list)
{
new_list = this->table[new_row] = linked_list_create();
}
new_list->insert_last(new_list, pair);
}
enumerator->destroy(enumerator);
list->destroy(list);
} }
} }
free(old_table); free(old_table);
@ -230,38 +215,28 @@ METHOD(hashtable_t, put, void*,
private_hashtable_t *this, void *key, void *value) private_hashtable_t *this, void *key, void *value)
{ {
void *old_value = NULL; void *old_value = NULL;
linked_list_t *list; pair_t *pair;
u_int hash; u_int hash, row;
u_int row;
hash = this->hash(key); hash = this->hash(key);
row = hash & this->mask; row = hash & this->mask;
list = this->table[row]; pair = this->table[row];
if (list) while (pair)
{ { /* search existing bucket for key */
enumerator_t *enumerator; if (this->equals(key, pair->key))
pair_t *pair;
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &pair))
{ {
if (pair_equals(pair, this, key)) old_value = pair->value;
{ pair->value = value;
old_value = pair->value; pair->key = key;
pair->value = value; break;
pair->key = key;
break;
}
} }
enumerator->destroy(enumerator); pair = pair->next;
} }
else if (!pair)
{ { /* insert at the front of bucket */
list = this->table[row] = linked_list_create(); pair = pair_create(key, value, hash);
} pair->next = this->table[row];
if (!old_value) this->table[row] = pair;
{
list->insert_last(list, pair_create(key, value, hash));
this->count++; this->count++;
} }
if (this->count >= this->capacity * this->load_factor) if (this->count >= this->capacity * this->load_factor)
@ -275,17 +250,17 @@ METHOD(hashtable_t, get, void*,
private_hashtable_t *this, void *key) private_hashtable_t *this, void *key)
{ {
void *value = NULL; void *value = NULL;
linked_list_t *list;
pair_t *pair; pair_t *pair;
list = this->table[this->hash(key) & this->mask]; pair = this->table[this->hash(key) & this->mask];
if (list) while (pair)
{ {
if (list->find_first(list, (linked_list_match_t)pair_equals, if (this->equals(key, pair->key))
(void**)&pair, this, key) == SUCCESS)
{ {
value = pair->value; value = pair->value;
break;
} }
pair = pair->next;
} }
return value; return value;
} }
@ -294,27 +269,30 @@ METHOD(hashtable_t, remove_, void*,
private_hashtable_t *this, void *key) private_hashtable_t *this, void *key)
{ {
void *value = NULL; void *value = NULL;
linked_list_t *list; pair_t *pair, *prev = NULL;
u_int row;
list = this->table[this->hash(key) & this->mask]; row = this->hash(key) & this->mask;
if (list) pair = this->table[row];
while (pair)
{ {
enumerator_t *enumerator; if (this->equals(key, pair->key))
pair_t *pair;
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &pair))
{ {
if (pair_equals(pair, this, key)) if (prev)
{ {
list->remove_at(list, enumerator); prev->next = pair->next;
value = pair->value;
this->count--;
free(pair);
break;
} }
else
{
this->table[row] = pair->next;
}
value = pair->value;
this->count--;
free(pair);
break;
} }
enumerator->destroy(enumerator); prev = pair;
pair = pair->next;
} }
return value; return value;
} }
@ -324,14 +302,18 @@ METHOD(hashtable_t, remove_at, void,
{ {
if (enumerator->table == this && enumerator->current) if (enumerator->table == this && enumerator->current)
{ {
linked_list_t *list; pair_t *current = enumerator->current;
list = this->table[enumerator->row]; if (enumerator->prev)
if (list)
{ {
list->remove_at(list, enumerator->current); enumerator->prev->next = current->next;
free(enumerator->pair);
this->count--;
} }
else
{
this->table[enumerator->row] = current->next;
}
enumerator->current = enumerator->prev;
free(current);
this->count--;
} }
} }
@ -346,48 +328,32 @@ METHOD(enumerator_t, enumerate, bool,
{ {
while (this->row < this->table->capacity) while (this->row < this->table->capacity)
{ {
this->prev = this->current;
if (this->current) if (this->current)
{ {
if (this->current->enumerate(this->current, &this->pair)) this->current = this->current->next;
{
if (key)
{
*key = this->pair->key;
}
if (value)
{
*value = this->pair->value;
}
return TRUE;
}
this->current->destroy(this->current);
this->current = NULL;
} }
else else
{ {
linked_list_t *list; this->current = this->table->table[this->row];
list = this->table->table[this->row]; }
if (list) if (this->current)
{
if (key)
{ {
this->current = list->create_enumerator(list); *key = this->current->key;
continue;
} }
if (value)
{
*value = this->current->value;
}
return TRUE;
} }
this->row++; this->row++;
} }
return FALSE; return FALSE;
} }
METHOD(enumerator_t, enumerator_destroy, void,
private_enumerator_t *this)
{
if (this->current)
{
this->current->destroy(this->current);
}
free(this);
}
METHOD(hashtable_t, create_enumerator, enumerator_t*, METHOD(hashtable_t, create_enumerator, enumerator_t*,
private_hashtable_t *this) private_hashtable_t *this)
{ {
@ -396,7 +362,7 @@ METHOD(hashtable_t, create_enumerator, enumerator_t*,
INIT(enumerator, INIT(enumerator,
.enumerator = { .enumerator = {
.enumerate = (void*)_enumerate, .enumerate = (void*)_enumerate,
.destroy = (void*)_enumerator_destroy, .destroy = (void*)free,
}, },
.table = this, .table = this,
); );
@ -407,15 +373,17 @@ METHOD(hashtable_t, create_enumerator, enumerator_t*,
METHOD(hashtable_t, destroy, void, METHOD(hashtable_t, destroy, void,
private_hashtable_t *this) private_hashtable_t *this)
{ {
linked_list_t *list; pair_t *pair, *next;
u_int row; u_int row;
for (row = 0; row < this->capacity; row++) for (row = 0; row < this->capacity; row++)
{ {
list = this->table[row]; pair = this->table[row];
if (list) while (pair)
{ {
list->destroy_function(list, free); next = pair->next;
free(pair);
pair = next;
} }
} }
free(this->table); free(this->table);