Highlevel lib: use dynamically resized hash table
authorMiklos Szeredi <mszeredi@suse.cz>
Mon, 13 Dec 2010 14:46:37 +0000 (15:46 +0100)
committerMiklos Szeredi <mszeredi@suse.cz>
Mon, 13 Dec 2010 14:46:37 +0000 (15:46 +0100)
Use dynamically resized hash table for looking up by name and node ID.

The chosen implementation is linear hashing, which features gradual
rehashing when while the size of the table increases.

ChangeLog
lib/fuse.c

index 2cf7052c39cf17ee48444f38c6aa218a0e7469f6..1a9e60262065818db1ec1b2c86be2dc2ac2b24f4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2010-12-13  Miklos Szeredi <miklos@szeredi.hu>
+
+       * Highlevel lib: use dynamically resized hash table for looking up
+       by name and node ID.
+
 2010-11-10  Miklos Szeredi <miklos@szeredi.hu>
 
        * Add new write_buf() method to the highlevel API.  Similarly to
index 4d24aea15b1eb3b6126352527acf8b0fbe7a723e..5b0a9ac4739115c3b47670b1136015e781c24067 100644 (file)
@@ -84,12 +84,17 @@ struct lock_queue_element {
        pthread_cond_t cond;
 };
 
+struct node_table {
+       struct node **array;
+       size_t use;
+       size_t size;
+       size_t split;
+};
+
 struct fuse {
        struct fuse_session *se;
-       struct node **name_table;
-       size_t name_table_size;
-       struct node **id_table;
-       size_t id_table_size;
+       struct node_table name_table;
+       struct node_table id_table;
        fuse_ino_t ctr;
        unsigned int generation;
        unsigned int hidectr;
@@ -259,12 +264,23 @@ static void fuse_put_module(struct fuse_module *m)
        pthread_mutex_unlock(&fuse_context_lock);
 }
 
+static size_t id_hash(struct fuse *f, fuse_ino_t ino)
+{
+       uint64_t hash = ((uint32_t) ino * 2654435761) % f->id_table.size;
+       uint64_t oldhash = hash % (f->id_table.size / 2);
+
+       if (oldhash >= f->id_table.split)
+               return oldhash;
+       else
+               return hash;
+}
+
 static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
 {
-       size_t hash = nodeid % f->id_table_size;
+       size_t hash = id_hash(f, nodeid);
        struct node *node;
 
-       for (node = f->id_table[hash]; node != NULL; node = node->id_next)
+       for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
                if (node->nodeid == nodeid)
                        return node;
 
@@ -290,33 +306,88 @@ static void free_node(struct node *node)
 
 static void unhash_id(struct fuse *f, struct node *node)
 {
-       size_t hash = node->nodeid % f->id_table_size;
-       struct node **nodep = &f->id_table[hash];
+       struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
 
        for (; *nodep != NULL; nodep = &(*nodep)->id_next)
                if (*nodep == node) {
                        *nodep = node->id_next;
+                       f->id_table.use--;
                        return;
                }
 }
 
+static int node_table_resize(struct node_table *t)
+{
+       size_t newsize = t->size * 2;
+       void *newarray;
+
+       newarray = realloc(t->array, sizeof(struct node *) * newsize);
+       if (newarray == NULL)
+               return -1;
+
+       t->array = newarray;
+       memset(t->array + t->size, 0, t->size * sizeof(struct node *));
+       t->size = newsize;
+       t->split = 0;
+
+       return 0;
+}
+
+static void rehash_id(struct fuse *f)
+{
+       struct node_table *t = &f->id_table;
+       struct node **nodep;
+       struct node **next;
+       size_t hash;
+
+       if (t->split == t->size / 2)
+               return;
+
+       hash = t->split;
+       t->split++;
+       for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
+               struct node *node = *nodep;
+               size_t newhash = id_hash(f, node->nodeid);
+
+               if (newhash != hash) {
+                       next = nodep;
+                       *nodep = node->id_next;
+                       node->id_next = t->array[newhash];
+                       t->array[newhash] = node;
+               } else {
+                       next = &node->id_next;
+               }
+       }
+       if (t->split == t->size / 2)
+               node_table_resize(t);
+}
+
 static void hash_id(struct fuse *f, struct node *node)
 {
-       size_t hash = node->nodeid % f->id_table_size;
-       node->id_next = f->id_table[hash];
-       f->id_table[hash] = node;
+       size_t hash = id_hash(f, node->nodeid);
+       node->id_next = f->id_table.array[hash];
+       f->id_table.array[hash] = node;
+       f->id_table.use++;
+
+       if (f->id_table.use >= f->id_table.size / 2)
+               rehash_id(f);
 }
 
-static unsigned int name_hash(struct fuse *f, fuse_ino_t parent,
-                             const char *name)
+static size_t name_hash(struct fuse *f, fuse_ino_t parent,
+                       const char *name)
 {
-       unsigned int hash = *name;
+       uint64_t hash = parent;
+       uint64_t oldhash;
 
-       if (hash)
-               for (name += 1; *name != '\0'; name++)
-                       hash = (hash << 5) - hash + *name;
+       for (; *name; name++)
+               hash = hash * 31 + (unsigned char) *name;
 
-       return (hash + parent) % f->name_table_size;
+       hash %= f->name_table.size;
+       oldhash = hash % (f->name_table.size / 2);
+       if (oldhash >= f->name_table.split)
+               return oldhash;
+       else
+               return hash;
 }
 
 static void unref_node(struct fuse *f, struct node *node);
@@ -325,7 +396,7 @@ static void unhash_name(struct fuse *f, struct node *node)
 {
        if (node->name) {
                size_t hash = name_hash(f, node->parent->nodeid, node->name);
-               struct node **nodep = &f->name_table[hash];
+               struct node **nodep = &f->name_table.array[hash];
 
                for (; *nodep != NULL; nodep = &(*nodep)->name_next)
                        if (*nodep == node) {
@@ -335,6 +406,7 @@ static void unhash_name(struct fuse *f, struct node *node)
                                free(node->name);
                                node->name = NULL;
                                node->parent = NULL;
+                               f->name_table.use--;
                                return;
                        }
                fprintf(stderr,
@@ -344,6 +416,35 @@ static void unhash_name(struct fuse *f, struct node *node)
        }
 }
 
+static void rehash_name(struct fuse *f)
+{
+       struct node_table *t = &f->name_table;
+       struct node **nodep;
+       struct node **next;
+       size_t hash;
+
+       if (t->split == t->size / 2)
+               return;
+
+       hash = t->split;
+       t->split++;
+       for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
+               struct node *node = *nodep;
+               size_t newhash = name_hash(f, node->parent->nodeid, node->name);
+
+               if (newhash != hash) {
+                       next = nodep;
+                       *nodep = node->name_next;
+                       node->name_next = t->array[newhash];
+                       t->array[newhash] = node;
+               } else {
+                       next = &node->name_next;
+               }
+       }
+       if (t->split == t->size / 2)
+               node_table_resize(t);
+}
+
 static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
                     const char *name)
 {
@@ -355,8 +456,13 @@ static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
 
        parent->refctr ++;
        node->parent = parent;
-       node->name_next = f->name_table[hash];
-       f->name_table[hash] = node;
+       node->name_next = f->name_table.array[hash];
+       f->name_table.array[hash] = node;
+       f->name_table.use++;
+
+       if (f->name_table.use >= f->name_table.size / 2)
+               rehash_name(f);
+
        return 0;
 }
 
@@ -397,7 +503,7 @@ static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
        size_t hash = name_hash(f, parent, name);
        struct node *node;
 
-       for (node = f->name_table[hash]; node != NULL; node = node->name_next)
+       for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
                if (node->parent->nodeid == parent &&
                    strcmp(node->name, name) == 0)
                        return node;
@@ -3802,6 +3908,20 @@ struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
        return fs;
 }
 
+static int node_table_init(struct node_table *t)
+{
+       t->size = 8192;
+       t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
+       if (t->array == NULL) {
+               fprintf(stderr, "fuse: memory allocation failed\n");
+               return -1;
+       }
+       t->use = 0;
+       t->split = 0;
+
+       return 0;
+}
+
 struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
                             const struct fuse_operations *op,
                             size_t op_size, void *user_data, int compat)
@@ -3893,22 +4013,11 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
        f->fs->debug = f->conf.debug;
        f->ctr = 0;
        f->generation = 0;
-       /* FIXME: Dynamic hash table */
-       f->name_table_size = 14057;
-       f->name_table = (struct node **)
-               calloc(1, sizeof(struct node *) * f->name_table_size);
-       if (f->name_table == NULL) {
-               fprintf(stderr, "fuse: memory allocation failed\n");
+       if (node_table_init(&f->name_table) == -1)
                goto out_free_session;
-       }
 
-       f->id_table_size = 14057;
-       f->id_table = (struct node **)
-               calloc(1, sizeof(struct node *) * f->id_table_size);
-       if (f->id_table == NULL) {
-               fprintf(stderr, "fuse: memory allocation failed\n");
+       if (node_table_init(&f->id_table) == -1)
                goto out_free_name_table;
-       }
 
        fuse_mutex_init(&f->lock);
 
@@ -3943,9 +4052,9 @@ out_free_root_name:
 out_free_root:
        free(root);
 out_free_id_table:
-       free(f->id_table);
+       free(f->id_table.array);
 out_free_name_table:
-       free(f->name_table);
+       free(f->name_table.array);
 out_free_session:
        fuse_session_destroy(f->se);
 out_free_fs:
@@ -3982,10 +4091,10 @@ void fuse_destroy(struct fuse *f)
                memset(c, 0, sizeof(*c));
                c->ctx.fuse = f;
 
-               for (i = 0; i < f->id_table_size; i++) {
+               for (i = 0; i < f->id_table.size; i++) {
                        struct node *node;
 
-                       for (node = f->id_table[i]; node != NULL;
+                       for (node = f->id_table.array[i]; node != NULL;
                             node = node->id_next) {
                                if (node->is_hidden) {
                                        char *path;
@@ -3997,17 +4106,18 @@ void fuse_destroy(struct fuse *f)
                        }
                }
        }
-       for (i = 0; i < f->id_table_size; i++) {
+       for (i = 0; i < f->id_table.size; i++) {
                struct node *node;
                struct node *next;
 
-               for (node = f->id_table[i]; node != NULL; node = next) {
+               for (node = f->id_table.array[i]; node != NULL; node = next) {
                        next = node->id_next;
                        free_node(node);
                }
        }
-       free(f->id_table);
-       free(f->name_table);
+
+       free(f->id_table.array);
+       free(f->name_table.array);
        pthread_mutex_destroy(&f->lock);
        fuse_session_destroy(f->se);
        free(f->conf.modules);