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:
parent
c74ece334d
commit
8af0177189
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue