]> git.ipfire.org Git - thirdparty/git.git/commitdiff
hashmap: introduce a new hashmap_partial_clear()
authorElijah Newren <newren@gmail.com>
Mon, 2 Nov 2020 18:55:04 +0000 (18:55 +0000)
committerJunio C Hamano <gitster@pobox.com>
Mon, 2 Nov 2020 20:15:50 +0000 (12:15 -0800)
merge-ort is a heavy user of strmaps, which are built on hashmap.[ch].
clear_or_reinit_internal_opts() in merge-ort was taking about 12% of
overall runtime in my testcase involving rebasing 35 patches of
linux.git across a big rename.  clear_or_reinit_internal_opts() was
calling hashmap_free() followed by hashmap_init(), meaning that not only
was it freeing all the memory associated with each of the strmaps just
to immediately allocate a new array again, it was allocating a new array
that was likely smaller than needed (thus resulting in later need to
rehash things).  The ending size of the map table on the previous commit
was likely almost perfectly sized for the next commit we wanted to pick,
and not dropping and reallocating the table immediately is a win.

Add some new API to hashmap to clear a hashmap of entries without
freeing map->table (and instead only zeroing it out like alloc_table()
would do, along with zeroing the count of items in the table and the
shrink_at field).

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
hashmap.c
hashmap.h

index bb7c9979b8d7e459439516237c747a08f000ace3..922ed07954b748e852eed1150e51b208a861d039 100644 (file)
--- a/hashmap.c
+++ b/hashmap.c
@@ -174,22 +174,37 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
        map->do_count_items = 1;
 }
 
+static void free_individual_entries(struct hashmap *map, ssize_t entry_offset)
+{
+       struct hashmap_iter iter;
+       struct hashmap_entry *e;
+
+       hashmap_iter_init(map, &iter);
+       while ((e = hashmap_iter_next(&iter)))
+               /*
+                * like container_of, but using caller-calculated
+                * offset (caller being hashmap_free_entries)
+                */
+               free((char *)e - entry_offset);
+}
+
+void hashmap_partial_clear_(struct hashmap *map, ssize_t entry_offset)
+{
+       if (!map || !map->table)
+               return;
+       if (entry_offset >= 0)  /* called by hashmap_clear_entries */
+               free_individual_entries(map, entry_offset);
+       memset(map->table, 0, map->tablesize * sizeof(struct hashmap_entry *));
+       map->shrink_at = 0;
+       map->private_size = 0;
+}
+
 void hashmap_free_(struct hashmap *map, ssize_t entry_offset)
 {
        if (!map || !map->table)
                return;
-       if (entry_offset >= 0) { /* called by hashmap_free_entries */
-               struct hashmap_iter iter;
-               struct hashmap_entry *e;
-
-               hashmap_iter_init(map, &iter);
-               while ((e = hashmap_iter_next(&iter)))
-                       /*
-                        * like container_of, but using caller-calculated
-                        * offset (caller being hashmap_free_entries)
-                        */
-                       free((char *)e - entry_offset);
-       }
+       if (entry_offset >= 0)  /* called by hashmap_free_entries */
+               free_individual_entries(map, entry_offset);
        free(map->table);
        memset(map, 0, sizeof(*map));
 }
index 3b0f2bcadea118512afb64cba18d77f0a19fdeee..e9430d582a94fa9a640a7287b0eb9e392c0787e6 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
@@ -235,7 +235,8 @@ void hashmap_init(struct hashmap *map,
                  const void *equals_function_data,
                  size_t initial_size);
 
-/* internal function for freeing hashmap */
+/* internal functions for clearing or freeing hashmap */
+void hashmap_partial_clear_(struct hashmap *map, ssize_t offset);
 void hashmap_free_(struct hashmap *map, ssize_t offset);
 
 /*
@@ -268,6 +269,16 @@ void hashmap_free_(struct hashmap *map, ssize_t offset);
  */
 #define hashmap_free(map) hashmap_free_(map, -1)
 
+/*
+ * Basically the same as calling hashmap_free() followed by hashmap_init(),
+ * but doesn't incur the overhead of deallocating and reallocating
+ * map->table; it leaves map->table allocated and the same size but zeroes
+ * it out so it's ready for use again as an empty map.  As with
+ * hashmap_free(), you may need to free the entries yourself before calling
+ * this function.
+ */
+#define hashmap_partial_clear(map) hashmap_partial_clear_(map, -1)
+
 /*
  * Frees @map and all entries.  @type is the struct type of the entry
  * where @member is the hashmap_entry struct used to associate with @map.