GHash: split nodes array into seperate arrays.

This reduces memory requirements by 1/6 on 64-bit machines since
no padding is needed.  It also puts less strain on the memory
allocator since we no longer need one giant slab of memory.

https://bugzilla.gnome.org/show_bug.cgi?id=644437
This commit is contained in:
Morten Welinder 2011-04-27 10:39:56 -04:00 committed by Matthias Clasen
parent 4d8e64e6e8
commit 6e45153ef7

View File

@ -158,19 +158,6 @@
#define HASH_IS_TOOMBSTONE(h_) ((h_) == 1) #define HASH_IS_TOOMBSTONE(h_) ((h_) == 1)
#define HASH_IS_REAL(h_) ((h_) >= 2) #define HASH_IS_REAL(h_) ((h_) >= 2)
typedef struct _GHashNode GHashNode;
struct _GHashNode
{
gpointer key;
gpointer value;
/* If key_hash == 0, node is not in use
* If key_hash == 1, node is a tombstone
* If key_hash >= 2, node contains data */
guint key_hash;
};
struct _GHashTable struct _GHashTable
{ {
gint size; gint size;
@ -178,7 +165,11 @@ struct _GHashTable
guint mask; guint mask;
gint nnodes; gint nnodes;
gint noccupied; /* nnodes + tombstones */ gint noccupied; /* nnodes + tombstones */
GHashNode *nodes;
gpointer *keys;
guint *hashes;
gpointer *values;
GHashFunc hash_func; GHashFunc hash_func;
GEqualFunc key_equal_func; GEqualFunc key_equal_func;
volatile gint ref_count; volatile gint ref_count;
@ -289,7 +280,7 @@ g_hash_table_set_shift_from_size (GHashTable *hash_table, gint size)
* @hash_table: our #GHashTable * @hash_table: our #GHashTable
* @key: the key to lookup against (may be %NULL) * @key: the key to lookup against (may be %NULL)
* @hash_return: optional key hash return location * @hash_return: optional key hash return location
* Return value: index of the described #GHashNode * Return value: index of the described node
* *
* Performs a lookup in the hash table. * Performs a lookup in the hash table.
* *
@ -306,7 +297,6 @@ static inline guint
g_hash_table_lookup_node (GHashTable *hash_table, g_hash_table_lookup_node (GHashTable *hash_table,
gconstpointer key) gconstpointer key)
{ {
GHashNode *node;
guint node_index; guint node_index;
guint hash_value; guint hash_value;
guint step = 0; guint step = 0;
@ -316,23 +306,24 @@ g_hash_table_lookup_node (GHashTable *hash_table,
hash_value = 2; hash_value = 2;
node_index = hash_value % hash_table->mod; node_index = hash_value % hash_table->mod;
node = &hash_table->nodes [node_index];
while (!HASH_IS_UNUSED (node->key_hash)) while (!HASH_IS_UNUSED (hash_table->hashes[node_index]))
{ {
/* We first check if our full hash values /* We first check if our full hash values
* are equal so we can avoid calling the full-blown * are equal so we can avoid calling the full-blown
* key equality function in most cases. * key equality function in most cases.
*/ */
if (node->key_hash == hash_value) if (hash_table->hashes[node_index] == hash_value)
{ {
gpointer node_key = hash_table->keys[node_index];
if (hash_table->key_equal_func) if (hash_table->key_equal_func)
{ {
if (hash_table->key_equal_func (node->key, key)) if (hash_table->key_equal_func (node_key, key))
break; break;
} }
else if (node->key == key) else if (node_key == key)
{ {
break; break;
} }
@ -341,7 +332,6 @@ g_hash_table_lookup_node (GHashTable *hash_table,
step++; step++;
node_index += step; node_index += step;
node_index &= hash_table->mask; node_index &= hash_table->mask;
node = &hash_table->nodes [node_index];
} }
return node_index; return node_index;
@ -352,7 +342,7 @@ g_hash_table_lookup_node (GHashTable *hash_table,
* @hash_table: our #GHashTable * @hash_table: our #GHashTable
* @key: the key to lookup against * @key: the key to lookup against
* @hash_return: key hash return location * @hash_return: key hash return location
* Return value: index of the described #GHashNode * Return value: index of the described node
* *
* Performs a lookup in the hash table, preserving extra information * Performs a lookup in the hash table, preserving extra information
* usually needed for insertion. * usually needed for insertion.
@ -374,7 +364,6 @@ g_hash_table_lookup_node_for_insertion (GHashTable *hash_table,
gconstpointer key, gconstpointer key,
guint *hash_return) guint *hash_return)
{ {
GHashNode *node;
guint node_index; guint node_index;
guint hash_value; guint hash_value;
guint first_tombstone; guint first_tombstone;
@ -388,28 +377,31 @@ g_hash_table_lookup_node_for_insertion (GHashTable *hash_table,
*hash_return = hash_value; *hash_return = hash_value;
node_index = hash_value % hash_table->mod; node_index = hash_value % hash_table->mod;
node = &hash_table->nodes [node_index];
while (!HASH_IS_UNUSED (node->key_hash)) while (!HASH_IS_UNUSED (hash_table->hashes[node_index]))
{ {
guint node_hash = hash_table->hashes[node_index];
/* We first check if our full hash values /* We first check if our full hash values
* are equal so we can avoid calling the full-blown * are equal so we can avoid calling the full-blown
* key equality function in most cases. * key equality function in most cases.
*/ */
if (node->key_hash == hash_value) if (node_hash == hash_value)
{ {
gpointer node_key = hash_table->keys[node_index];
if (hash_table->key_equal_func) if (hash_table->key_equal_func)
{ {
if (hash_table->key_equal_func (node->key, key)) if (hash_table->key_equal_func (node_key, key))
return node_index; return node_index;
} }
else if (node->key == key) else if (node_key == key)
{ {
return node_index; return node_index;
} }
} }
else if (HASH_IS_TOOMBSTONE (node->key_hash) && !have_tombstone) else if (HASH_IS_TOOMBSTONE (node_hash) && !have_tombstone)
{ {
first_tombstone = node_index; first_tombstone = node_index;
have_tombstone = TRUE; have_tombstone = TRUE;
@ -418,7 +410,6 @@ g_hash_table_lookup_node_for_insertion (GHashTable *hash_table,
step++; step++;
node_index += step; node_index += step;
node_index &= hash_table->mask; node_index &= hash_table->mask;
node = &hash_table->nodes [node_index];
} }
if (have_tombstone) if (have_tombstone)
@ -441,21 +432,21 @@ g_hash_table_lookup_node_for_insertion (GHashTable *hash_table,
*/ */
static void static void
g_hash_table_remove_node (GHashTable *hash_table, g_hash_table_remove_node (GHashTable *hash_table,
GHashNode *node, int i,
gboolean notify) gboolean notify)
{ {
if (notify && hash_table->key_destroy_func) if (notify && hash_table->key_destroy_func)
hash_table->key_destroy_func (node->key); hash_table->key_destroy_func (hash_table->keys[i]);
if (notify && hash_table->value_destroy_func) if (notify && hash_table->value_destroy_func)
hash_table->value_destroy_func (node->value); hash_table->value_destroy_func (hash_table->values[i]);
/* Erect tombstone */ /* Erect tombstone */
node->key_hash = 1; hash_table->hashes[i] = 1;
/* Be GC friendly */ /* Be GC friendly */
node->key = NULL; hash_table->keys[i] = NULL;
node->value = NULL; hash_table->values[i] = NULL;
hash_table->nnodes--; hash_table->nnodes--;
} }
@ -483,15 +474,13 @@ g_hash_table_remove_all_nodes (GHashTable *hash_table,
{ {
for (i = 0; i < hash_table->size; i++) for (i = 0; i < hash_table->size; i++)
{ {
GHashNode *node = &hash_table->nodes [i]; if (HASH_IS_REAL (hash_table->hashes[i]))
if (HASH_IS_REAL (node->key_hash))
{ {
if (hash_table->key_destroy_func != NULL) if (hash_table->key_destroy_func != NULL)
hash_table->key_destroy_func (node->key); hash_table->key_destroy_func (hash_table->keys[i]);
if (hash_table->value_destroy_func != NULL) if (hash_table->value_destroy_func != NULL)
hash_table->value_destroy_func (node->value); hash_table->value_destroy_func (hash_table->values[i]);
} }
} }
} }
@ -499,7 +488,9 @@ g_hash_table_remove_all_nodes (GHashTable *hash_table,
/* We need to set node->key_hash = 0 for all nodes - might as well be GC /* We need to set node->key_hash = 0 for all nodes - might as well be GC
* friendly and clear everything * friendly and clear everything
*/ */
memset (hash_table->nodes, 0, hash_table->size * sizeof (GHashNode)); memset (hash_table->hashes, 0, hash_table->size * sizeof (guint));
memset (hash_table->keys, 0, hash_table->size * sizeof (gpointer));
memset (hash_table->values, 0, hash_table->size * sizeof (gpointer));
hash_table->nnodes = 0; hash_table->nnodes = 0;
hash_table->noccupied = 0; hash_table->noccupied = 0;
@ -521,41 +512,50 @@ g_hash_table_remove_all_nodes (GHashTable *hash_table,
static void static void
g_hash_table_resize (GHashTable *hash_table) g_hash_table_resize (GHashTable *hash_table)
{ {
GHashNode *new_nodes; gpointer *new_keys;
gpointer *new_values;
guint *new_hashes;
gint old_size; gint old_size;
gint i; gint i;
old_size = hash_table->size; old_size = hash_table->size;
g_hash_table_set_shift_from_size (hash_table, hash_table->nnodes * 2); g_hash_table_set_shift_from_size (hash_table, hash_table->nnodes * 2);
new_nodes = g_new0 (GHashNode, hash_table->size); new_keys = g_new0 (gpointer, hash_table->size);
new_values = g_new0 (gpointer, hash_table->size);
new_hashes = g_new0 (guint, hash_table->size);
for (i = 0; i < old_size; i++) for (i = 0; i < old_size; i++)
{ {
GHashNode *node = &hash_table->nodes [i]; guint node_hash = hash_table->hashes[i];
GHashNode *new_node;
guint hash_val; guint hash_val;
guint step = 0; guint step = 0;
if (!HASH_IS_REAL (node->key_hash)) if (!HASH_IS_REAL (node_hash))
continue; continue;
hash_val = node->key_hash % hash_table->mod; hash_val = node_hash % hash_table->mod;
new_node = &new_nodes [hash_val];
while (!HASH_IS_UNUSED (new_node->key_hash)) while (!HASH_IS_UNUSED (new_hashes[hash_val]))
{ {
step++; step++;
hash_val += step; hash_val += step;
hash_val &= hash_table->mask; hash_val &= hash_table->mask;
new_node = &new_nodes [hash_val];
} }
*new_node = *node; new_hashes[hash_val] = hash_table->hashes[i];
new_keys[hash_val] = hash_table->keys[i];
new_values[hash_val] = hash_table->values[i];
} }
g_free (hash_table->nodes); g_free (hash_table->keys);
hash_table->nodes = new_nodes; g_free (hash_table->values);
g_free (hash_table->hashes);
hash_table->keys = new_keys;
hash_table->values = new_values;
hash_table->hashes = new_hashes;
hash_table->noccupied = hash_table->nnodes; hash_table->noccupied = hash_table->nnodes;
} }
@ -643,7 +643,9 @@ g_hash_table_new_full (GHashFunc hash_func,
#endif #endif
hash_table->key_destroy_func = key_destroy_func; hash_table->key_destroy_func = key_destroy_func;
hash_table->value_destroy_func = value_destroy_func; hash_table->value_destroy_func = value_destroy_func;
hash_table->nodes = g_new0 (GHashNode, hash_table->size); hash_table->keys = g_new0 (gpointer, hash_table->size);
hash_table->values = g_new0 (gpointer, hash_table->size);
hash_table->hashes = g_new0 (guint, hash_table->size);
return hash_table; return hash_table;
} }
@ -705,7 +707,6 @@ g_hash_table_iter_next (GHashTableIter *iter,
gpointer *value) gpointer *value)
{ {
RealIter *ri = (RealIter *) iter; RealIter *ri = (RealIter *) iter;
GHashNode *node;
gint position; gint position;
g_return_val_if_fail (iter != NULL, FALSE); g_return_val_if_fail (iter != NULL, FALSE);
@ -724,15 +725,13 @@ g_hash_table_iter_next (GHashTableIter *iter,
ri->position = position; ri->position = position;
return FALSE; return FALSE;
} }
node = &ri->hash_table->nodes [position];
} }
while (!HASH_IS_REAL (node->key_hash)); while (!HASH_IS_REAL (ri->hash_table->hashes[position]));
if (key != NULL) if (key != NULL)
*key = node->key; *key = ri->hash_table->keys[position];
if (value != NULL) if (value != NULL)
*value = node->value; *value = ri->hash_table->values[position];
ri->position = position; ri->position = position;
return TRUE; return TRUE;
@ -766,7 +765,7 @@ iter_remove_or_steal (RealIter *ri, gboolean notify)
g_return_if_fail (ri->position >= 0); g_return_if_fail (ri->position >= 0);
g_return_if_fail (ri->position < ri->hash_table->size); g_return_if_fail (ri->position < ri->hash_table->size);
g_hash_table_remove_node (ri->hash_table, &ri->hash_table->nodes [ri->position], notify); g_hash_table_remove_node (ri->hash_table, ri->position, notify);
#ifndef G_DISABLE_ASSERT #ifndef G_DISABLE_ASSERT
ri->version++; ri->version++;
@ -856,7 +855,9 @@ g_hash_table_unref (GHashTable *hash_table)
if (g_atomic_int_exchange_and_add (&hash_table->ref_count, -1) - 1 == 0) if (g_atomic_int_exchange_and_add (&hash_table->ref_count, -1) - 1 == 0)
{ {
g_hash_table_remove_all_nodes (hash_table, TRUE); g_hash_table_remove_all_nodes (hash_table, TRUE);
g_free (hash_table->nodes); g_free (hash_table->keys);
g_free (hash_table->values);
g_free (hash_table->hashes);
g_slice_free (GHashTable, hash_table); g_slice_free (GHashTable, hash_table);
} }
} }
@ -898,15 +899,15 @@ gpointer
g_hash_table_lookup (GHashTable *hash_table, g_hash_table_lookup (GHashTable *hash_table,
gconstpointer key) gconstpointer key)
{ {
GHashNode *node;
guint node_index; guint node_index;
g_return_val_if_fail (hash_table != NULL, NULL); g_return_val_if_fail (hash_table != NULL, NULL);
node_index = g_hash_table_lookup_node (hash_table, key); node_index = g_hash_table_lookup_node (hash_table, key);
node = &hash_table->nodes [node_index];
return HASH_IS_REAL (node->key_hash) ? node->value : NULL; return HASH_IS_REAL (hash_table->hashes[node_index])
? hash_table->values[node_index]
: NULL;
} }
/** /**
@ -933,22 +934,20 @@ g_hash_table_lookup_extended (GHashTable *hash_table,
gpointer *orig_key, gpointer *orig_key,
gpointer *value) gpointer *value)
{ {
GHashNode *node;
guint node_index; guint node_index;
g_return_val_if_fail (hash_table != NULL, FALSE); g_return_val_if_fail (hash_table != NULL, FALSE);
node_index = g_hash_table_lookup_node (hash_table, lookup_key); node_index = g_hash_table_lookup_node (hash_table, lookup_key);
node = &hash_table->nodes [node_index];
if (!HASH_IS_REAL (node->key_hash)) if (!HASH_IS_REAL (hash_table->hashes[node_index]))
return FALSE; return FALSE;
if (orig_key) if (orig_key)
*orig_key = node->key; *orig_key = hash_table->keys[node_index];
if (value) if (value)
*value = node->value; *value = hash_table->values[node_index];
return TRUE; return TRUE;
} }
@ -975,7 +974,6 @@ g_hash_table_insert_internal (GHashTable *hash_table,
gpointer value, gpointer value,
gboolean keep_new_key) gboolean keep_new_key)
{ {
GHashNode *node;
guint node_index; guint node_index;
guint key_hash; guint key_hash;
guint old_hash; guint old_hash;
@ -984,17 +982,16 @@ g_hash_table_insert_internal (GHashTable *hash_table,
g_return_if_fail (hash_table->ref_count > 0); g_return_if_fail (hash_table->ref_count > 0);
node_index = g_hash_table_lookup_node_for_insertion (hash_table, key, &key_hash); node_index = g_hash_table_lookup_node_for_insertion (hash_table, key, &key_hash);
node = &hash_table->nodes [node_index];
old_hash = node->key_hash; old_hash = hash_table->hashes[node_index];
if (HASH_IS_REAL (old_hash)) if (HASH_IS_REAL (old_hash))
{ {
if (keep_new_key) if (keep_new_key)
{ {
if (hash_table->key_destroy_func) if (hash_table->key_destroy_func)
hash_table->key_destroy_func (node->key); hash_table->key_destroy_func (hash_table->keys[node_index]);
node->key = key; hash_table->keys[node_index] = key;
} }
else else
{ {
@ -1003,15 +1000,15 @@ g_hash_table_insert_internal (GHashTable *hash_table,
} }
if (hash_table->value_destroy_func) if (hash_table->value_destroy_func)
hash_table->value_destroy_func (node->value); hash_table->value_destroy_func (hash_table->values[node_index]);
node->value = value; hash_table->values[node_index] = value;
} }
else else
{ {
node->key = key; hash_table->keys[node_index] = key;
node->value = value; hash_table->values[node_index] = value;
node->key_hash = key_hash; hash_table->hashes[node_index] = key_hash;
hash_table->nnodes++; hash_table->nnodes++;
@ -1089,18 +1086,16 @@ g_hash_table_remove_internal (GHashTable *hash_table,
gconstpointer key, gconstpointer key,
gboolean notify) gboolean notify)
{ {
GHashNode *node;
guint node_index; guint node_index;
g_return_val_if_fail (hash_table != NULL, FALSE); g_return_val_if_fail (hash_table != NULL, FALSE);
node_index = g_hash_table_lookup_node (hash_table, key); node_index = g_hash_table_lookup_node (hash_table, key);
node = &hash_table->nodes [node_index];
if (!HASH_IS_REAL (node->key_hash)) if (!HASH_IS_REAL (hash_table->hashes[node_index]))
return FALSE; return FALSE;
g_hash_table_remove_node (hash_table, node, notify); g_hash_table_remove_node (hash_table, node_index, notify);
g_hash_table_maybe_resize (hash_table); g_hash_table_maybe_resize (hash_table);
#ifndef G_DISABLE_ASSERT #ifndef G_DISABLE_ASSERT
@ -1226,12 +1221,14 @@ g_hash_table_foreach_remove_or_steal (GHashTable *hash_table,
for (i = 0; i < hash_table->size; i++) for (i = 0; i < hash_table->size; i++)
{ {
GHashNode *node = &hash_table->nodes [i]; guint node_hash = hash_table->hashes[i];
gpointer node_key = hash_table->keys[i];
gpointer node_value = hash_table->values[i];
if (HASH_IS_REAL (node->key_hash) && if (HASH_IS_REAL (node_hash) &&
(* func) (node->key, node->value, user_data)) (* func) (node_key, node_value, user_data))
{ {
g_hash_table_remove_node (hash_table, node, notify); g_hash_table_remove_node (hash_table, i, notify);
deleted++; deleted++;
} }
} }
@ -1328,10 +1325,12 @@ g_hash_table_foreach (GHashTable *hash_table,
for (i = 0; i < hash_table->size; i++) for (i = 0; i < hash_table->size; i++)
{ {
GHashNode *node = &hash_table->nodes [i]; guint node_hash = hash_table->hashes[i];
gpointer node_key = hash_table->keys[i];
gpointer node_value = hash_table->values[i];
if (HASH_IS_REAL (node->key_hash)) if (HASH_IS_REAL (node_hash))
(* func) (node->key, node->value, user_data); (* func) (node_key, node_value, user_data);
} }
} }
@ -1373,11 +1372,13 @@ g_hash_table_find (GHashTable *hash_table,
for (i = 0; i < hash_table->size; i++) for (i = 0; i < hash_table->size; i++)
{ {
GHashNode *node = &hash_table->nodes [i]; guint node_hash = hash_table->hashes[i];
gpointer node_key = hash_table->keys[i];
gpointer node_value = hash_table->values[i];
if (HASH_IS_REAL (node->key_hash) && if (HASH_IS_REAL (node_hash) &&
predicate (node->key, node->value, user_data)) predicate (node_key, node_value, user_data))
return node->value; return node_value;
} }
return NULL; return NULL;
@ -1424,10 +1425,8 @@ g_hash_table_get_keys (GHashTable *hash_table)
retval = NULL; retval = NULL;
for (i = 0; i < hash_table->size; i++) for (i = 0; i < hash_table->size; i++)
{ {
GHashNode *node = &hash_table->nodes [i]; if (HASH_IS_REAL (hash_table->hashes[i]))
retval = g_list_prepend (retval, hash_table->keys[i]);
if (HASH_IS_REAL (node->key_hash))
retval = g_list_prepend (retval, node->key);
} }
return retval; return retval;
@ -1458,10 +1457,8 @@ g_hash_table_get_values (GHashTable *hash_table)
retval = NULL; retval = NULL;
for (i = 0; i < hash_table->size; i++) for (i = 0; i < hash_table->size; i++)
{ {
GHashNode *node = &hash_table->nodes [i]; if (HASH_IS_REAL (hash_table->hashes[i]))
retval = g_list_prepend (retval, hash_table->values[i]);
if (HASH_IS_REAL (node->key_hash))
retval = g_list_prepend (retval, node->value);
} }
return retval; return retval;