Skip to content

Commit 33f20d8

Browse files
newrengitster
authored andcommitted
hashmap: introduce a new hashmap_partial_clear()
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b7879b0 commit 33f20d8

File tree

2 files changed

+39
-13
lines changed

2 files changed

+39
-13
lines changed

hashmap.c

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,22 +174,37 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
174174
map->do_count_items = 1;
175175
}
176176

177+
static void free_individual_entries(struct hashmap *map, ssize_t entry_offset)
178+
{
179+
struct hashmap_iter iter;
180+
struct hashmap_entry *e;
181+
182+
hashmap_iter_init(map, &iter);
183+
while ((e = hashmap_iter_next(&iter)))
184+
/*
185+
* like container_of, but using caller-calculated
186+
* offset (caller being hashmap_free_entries)
187+
*/
188+
free((char *)e - entry_offset);
189+
}
190+
191+
void hashmap_partial_clear_(struct hashmap *map, ssize_t entry_offset)
192+
{
193+
if (!map || !map->table)
194+
return;
195+
if (entry_offset >= 0) /* called by hashmap_clear_entries */
196+
free_individual_entries(map, entry_offset);
197+
memset(map->table, 0, map->tablesize * sizeof(struct hashmap_entry *));
198+
map->shrink_at = 0;
199+
map->private_size = 0;
200+
}
201+
177202
void hashmap_free_(struct hashmap *map, ssize_t entry_offset)
178203
{
179204
if (!map || !map->table)
180205
return;
181-
if (entry_offset >= 0) { /* called by hashmap_free_entries */
182-
struct hashmap_iter iter;
183-
struct hashmap_entry *e;
184-
185-
hashmap_iter_init(map, &iter);
186-
while ((e = hashmap_iter_next(&iter)))
187-
/*
188-
* like container_of, but using caller-calculated
189-
* offset (caller being hashmap_free_entries)
190-
*/
191-
free((char *)e - entry_offset);
192-
}
206+
if (entry_offset >= 0) /* called by hashmap_free_entries */
207+
free_individual_entries(map, entry_offset);
193208
free(map->table);
194209
memset(map, 0, sizeof(*map));
195210
}

hashmap.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,8 @@ void hashmap_init(struct hashmap *map,
235235
const void *equals_function_data,
236236
size_t initial_size);
237237

238-
/* internal function for freeing hashmap */
238+
/* internal functions for clearing or freeing hashmap */
239+
void hashmap_partial_clear_(struct hashmap *map, ssize_t offset);
239240
void hashmap_free_(struct hashmap *map, ssize_t offset);
240241

241242
/*
@@ -268,6 +269,16 @@ void hashmap_free_(struct hashmap *map, ssize_t offset);
268269
*/
269270
#define hashmap_free(map) hashmap_free_(map, -1)
270271

272+
/*
273+
* Basically the same as calling hashmap_free() followed by hashmap_init(),
274+
* but doesn't incur the overhead of deallocating and reallocating
275+
* map->table; it leaves map->table allocated and the same size but zeroes
276+
* it out so it's ready for use again as an empty map. As with
277+
* hashmap_free(), you may need to free the entries yourself before calling
278+
* this function.
279+
*/
280+
#define hashmap_partial_clear(map) hashmap_partial_clear_(map, -1)
281+
271282
/*
272283
* Frees @map and all entries. @type is the struct type of the entry
273284
* where @member is the hashmap_entry struct used to associate with @map.

0 commit comments

Comments
 (0)