Grow small map entry arrays through 1, 2, 3, 4, 6, 8, 12 and 16 slots (#1688)

This commit is contained in:
Chris Tate
2026-10-21 12:09:50 -05:00
committed by GitHub
2 changed files with 19 additions and 12 deletions
+11 -4
View File
@@ -702,11 +702,18 @@ static void scr_map_compact(ScrMap *m, bool reindex) {
* range. */
#define SCR_MAP_MAX_ENTRIES ((size_t)1 << 30)
/* The first entry gets an array of one, so a one-entry map keeps no unused
* slots; the second grows it to four, and capacity doubles after that. */
/* The entry capacity after `cap`: 1, 2, 3, 4, 6, 8, 12, 16, then doubling.
* Most maps hold one to four entries, so a small map keeps at most a third
* of its slots unused, and appends stay amortized constant. */
static size_t scr_map_next_ecap(size_t cap) {
if (cap < 4) return cap + 1;
if (cap < 16) return cap < 8 ? cap + 2 : cap + 4;
return cap * 2;
}
static void scr_map_grow_entries(ScrMap *m) {
size_t cap = m->ecap == 0 ? 1 : m->ecap < 4 ? 4 : m->ecap;
while (cap < m->nentries + 1) cap *= 2;
size_t cap = scr_map_next_ecap(m->ecap);
while (cap < m->nentries + 1) cap = scr_map_next_ecap(cap);
if (cap > SCR_MAP_MAX_ENTRIES) scr_map_oom();
ScrMapEntry *entries = scr_mem_realloc(m->entries, cap * scr_map_entry_size(m));
if (!entries) scr_map_oom();
+8 -8
View File
@@ -242,17 +242,17 @@ static void modes(void) {
scr_map_release(set);
}
/* Small maps keep compact storage: the entries array starts with one slot
* and grows to four, and maps of up to 8 entries keep no bucket table. */
/* Small maps keep compact storage: the entries array grows through 1, 2,
* 3, 4, 6 and 8 slots, and maps of up to 8 entries keep no bucket table. */
static void small_storage(void) {
ScrMap *map = scr_map_new(SCR_MAP_KEY_F64, SCR_MAP_VAL_F64, NULL);
assert(!map->entries && map->ecap == 0);
scr_map_set_f64_f64(map, 100000, 1);
assert(map->ecap == 1);
scr_map_set_f64_f64(map, 200000, 2);
assert(map->ecap == 4);
for (int i = 3; i <= 8; i++) scr_map_set_f64_f64(map, i * 100000.0, i);
assert(map->ecap == 8 && !map->nbuckets && !map->ndense);
static const uint32_t caps[] = {1, 2, 3, 4, 6, 6, 8, 8};
for (int i = 1; i <= 8; i++) {
scr_map_set_f64_f64(map, i * 100000.0, i);
assert(map->ecap == caps[i - 1]);
}
assert(!map->nbuckets && !map->ndense);
double out;
for (int i = 1; i <= 8; i++) assert(scr_map_get_f64_f64(map, i * 100000.0, &out) && out == i);
assert(!scr_map_has_f64(map, 50000) && !scr_map_has_f64(map, 0));