From: Miklos Szeredi Date: Mon, 13 Dec 2010 14:46:37 +0000 (+0100) Subject: Highlevel lib: use dynamically resized hash table X-Git-Tag: fuse_2_9_0~80 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=8ff63b9e9bf2d7b90356fa90f60dc07413fd4937;p=qemu-gpiodev%2Flibfuse.git Highlevel lib: use dynamically resized hash table 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. --- diff --git a/ChangeLog b/ChangeLog index 2cf7052..1a9e602 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-12-13 Miklos Szeredi + + * Highlevel lib: use dynamically resized hash table for looking up + by name and node ID. + 2010-11-10 Miklos Szeredi * Add new write_buf() method to the highlevel API. Similarly to diff --git a/lib/fuse.c b/lib/fuse.c index 4d24aea..5b0a9ac 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -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);