P1: string-pool free-list index -- targets 6 and 7, one change #21

Open
opened 2026-08-02 18:33:01 -04:00 by tachikoma · 0 comments
Collaborator

Source: TODO.md, "Performance" -> "The plan", item 1 (at bbb7b8f)

Two missed targets, one fix.

# Target Today
6 Pool acquire under 100 ns regardless of how full the pool is 3.9 ns empty, 250.9 ns on the last free string slot
7 Pool release proportional to the bytes actually used, not the slot's capacity 47.2 ns, a fixed 4 KiB wipe

The acquire scan is 64x slower on a full string pool than an empty one purely
because the pool is a megabyte and the scan touches one refcount per 4 KiB. A
next-free hint per pool -- one int per layer, remembering where the last free
slot was -- takes both to constant time without changing the "no malloc" rule
at all
. The five scans are src/heap.c:52-116.

This lands before anyone raises AKGL_MAX_HEAP_*, because today's cost grows
with the ceiling rather than with the usage.

Make the release wipe (src/heap.c:211) proportional to bytes used: either
memset through strnlen(data, AKGL_MAX_STRING_LENGTH) + 1, or a tracked length
field. The length-field option must respect the layout history in
akgl_string_initialize, which put two real bugs on the bytes after data.

Fold in the heap-acquire asymmetry filed separately -- akgl_heap_next_string is
the only acquire that takes a reference, and a free-list change touches every
acquire anyway.

Budgets to move: tests/perf.c:317, :333, :347. Re-record the three rows
in PERFORMANCE.md in the same commit.

Files: src/heap.c:52-116, src/heap.c:211, tests/perf.c:317,333,347, PERFORMANCE.md


Filed by Tachikoma (Claude Code, Opus 5, 1M context)

**Source:** TODO.md, "Performance" -> "The plan", item 1 (at bbb7b8f) Two missed targets, one fix. | # | Target | Today | |---|---|---| | 6 | Pool acquire under 100 ns regardless of how full the pool is | 3.9 ns empty, **250.9 ns** on the last free string slot | | 7 | Pool release proportional to the bytes actually used, not the slot's capacity | **47.2 ns**, a fixed 4 KiB wipe | The acquire scan is 64x slower on a full string pool than an empty one purely because the pool is a megabyte and the scan touches one refcount per 4 KiB. A next-free hint per pool -- one `int` per layer, remembering where the last free slot was -- takes both to constant time **without changing the "no `malloc`" rule at all**. The five scans are `src/heap.c:52-116`. **This lands before anyone raises `AKGL_MAX_HEAP_*`**, because today's cost grows with the ceiling rather than with the usage. Make the release wipe (`src/heap.c:211`) proportional to bytes used: either `memset` through `strnlen(data, AKGL_MAX_STRING_LENGTH) + 1`, or a tracked length field. The length-field option must respect the layout history in `akgl_string_initialize`, which put two real bugs on the bytes after `data`. Fold in the heap-acquire asymmetry filed separately -- `akgl_heap_next_string` is the only acquire that takes a reference, and a free-list change touches every acquire anyway. **Budgets to move:** `tests/perf.c:317`, `:333`, `:347`. Re-record the three rows in `PERFORMANCE.md` in the same commit. **Files:** `src/heap.c:52-116`, `src/heap.c:211`, `tests/perf.c:317,333,347`, `PERFORMANCE.md` --- Filed by Tachikoma (Claude Code, Opus 5, 1M context)
tachikoma added this to the 0.9.x milestone 2026-08-02 18:33:01 -04:00
tachikoma added the performanceblast-radius:high labels 2026-08-02 18:33:01 -04:00
tachikoma added the status::grooming label 2026-08-02 18:49:15 -04:00
Sign in to join this conversation.