Complete.Org: Mailing Lists: Archives: freeciv-dev: September 2005:
[Freeciv-Dev] (PR#14097) improvements to hash bucket deletion
Home

[Freeciv-Dev] (PR#14097) improvements to hash bucket deletion

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#14097) improvements to hash bucket deletion
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 23 Sep 2005 13:37:30 -0700
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14097 >

Because of the ugliness of many hash callers I looked at the hash 
interface/internals.

One of the first things I noticed is that hash_delete_all_entries is 
really inefficient.  It's hard to tell offhand exactly (the effect of 
hash_maybe_shrink is hard to judge) but I suspect it is at least O(n^2). 
  Of course it should only be O(n).

In a somewhat related issue, hash_free doesn't call 
hash_delete_all_entries.  Instead it just has its own, faster free.

This patch solves both problems.  A new function hash_delete_bucket is 
created, and called from hash_delete_entry and hash_delete_all_entries. 
  The latter just iterates over all buckets now so it is now O(n) (and 
calls hash_maybe_shrink - but just once, after all entries are gone). 
hash_free now also calls hash_delete_all_entries (which is a little 
slower than the old loop since it does have to zero the entries...but 
still just O(n)).

There are further inefficiencies here.  Hash resizing (a common 
operation) is slow since it zeroes all the buckets of the old hash 
bucket array...right before it frees the array.  I also didn't address 
any of the interface problems yet.  Generally the hash code could use 
some work I think.

-jason

Index: utility/hash.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/utility/hash.c,v
retrieving revision 1.28
diff -p -u -r1.28 hash.c
--- utility/hash.c      23 Sep 2005 19:23:32 -0000      1.28
+++ utility/hash.c      23 Sep 2005 20:33:05 -0000
@@ -376,18 +376,7 @@ static void hash_free_contents(struct ha
 **************************************************************************/
 void hash_free(struct hash_table *h)
 {
-  unsigned i;
-
-  for(i = 0; i < h->num_buckets; i++) {
-    struct hash_bucket *bucket = &h->buckets[i];
-
-    if (h->free_key_func) {
-      h->free_key_func((void*)bucket->key);
-    }
-    if (h->free_data_func) {
-      h->free_data_func((void*)bucket->data);
-    }
-  }
+  hash_delete_all_entries(h);
   hash_free_contents(h);
   free(h);
 }
@@ -591,6 +580,47 @@ void *hash_replace(struct hash_table *h,
 }
 
 /**************************************************************************
+  Deletes a single bucket from the hash.  You may call this on an unused
+  bucket.  old_key and old_value, if non-NULL, will be set to contain the
+  pointers to the key and value that are being deleted.  free_key_func
+  and free_data_func will (if set) be called on the key and data so these
+  return values may already have been freed.
+**************************************************************************/
+static void hash_delete_bucket(struct hash_table *h,
+                              struct hash_bucket *bucket,
+                              void **old_key,
+                              void **old_value)
+{
+  if (bucket->used == BUCKET_USED) {
+    if (old_key) {
+      *old_key = (void*)bucket->key;
+    }
+    if (old_value) {
+      *old_value = (void*)bucket->data;
+    }
+    if (h->free_key_func) {
+      h->free_key_func((void*)bucket->key);
+    }
+    if (h->free_data_func) {
+      h->free_data_func((void*)bucket->data);
+    }
+    zero_hbucket(bucket);
+    bucket->used = BUCKET_DELETED;
+    h->num_deleted++;
+    assert(h->num_entries > 0);
+    h->num_entries--;
+  } else {
+    if (old_key) {
+      *old_key = NULL;
+    }
+    if (old_value) {
+      *old_value = NULL;
+    }
+  }
+
+}
+
+/**************************************************************************
   Delete an entry with specified key.  Returns user-data of deleted
   entry, or NULL if not found.
 **************************************************************************/
@@ -609,29 +639,12 @@ void *hash_delete_entry_full(struct hash
                             void **old_key)
 {
   struct hash_bucket *bucket;
+  void *old_value;
 
   hash_maybe_shrink(h);  
   bucket = internal_lookup(h, key, HASH_VAL(h,key));
-  if (bucket->used == BUCKET_USED) {
-    const void *ret = bucket->data;
-    if (h->free_key_func) {
-      h->free_key_func((void*)bucket->key);
-    }
-    if (h->free_data_func) {
-      h->free_data_func((void*)bucket->data);
-    }
-    zero_hbucket(bucket);
-    bucket->used = BUCKET_DELETED;
-    h->num_deleted++;
-    assert(h->num_entries > 0);
-    h->num_entries--;
-    return (void*) ret;
-  } else {
-    if (old_key) {
-      *old_key = NULL;
-    }
-    return NULL;
-  }
+  hash_delete_bucket(h, bucket, old_key, &old_value);
+  return old_value;
 }
 
 /**************************************************************************
@@ -639,8 +652,13 @@ void *hash_delete_entry_full(struct hash
 **************************************************************************/
 void hash_delete_all_entries(struct hash_table *h)
 {
-  while (hash_num_entries(h) > 0)
-    (void) hash_delete_entry(h, hash_key_by_number(h, 0));
+  unsigned int bucket_nr;
+
+  /* Modeled after hash_key_by_number and hash_delete_entry. */
+  for (bucket_nr = 0; bucket_nr < h->num_buckets; bucket_nr++) {
+    hash_delete_bucket(h, &h->buckets[bucket_nr], NULL, NULL);
+  }
+  hash_maybe_shrink(h);
 }
 
 /**************************************************************************

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#14097) improvements to hash bucket deletion, Jason Short <=