The performance plan: nine changes in blast-radius order #60

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

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

The first measured baseline is in PERFORMANCE.md, produced by tests/perf.c and
tests/perf_render.c (ctest --test-dir build -L perf). Both suites hold every
measurement to a budget set at roughly ten times the recorded baseline, so an
algorithmic regression fails the suite rather than being discovered by a player.

Read PERFORMANCE.md before arguing with anything below -- every claim has a
number behind it, and several of the things expected to be slow are not.

The governing target: libakgl's own bookkeeping should never be the reason a
frame is late.
Everything the library decides -- pool scans, registry lookups,
state-to-sprite mapping, physics, visibility, error contexts -- should fit in 5% of
a 16.67 ms frame, leaving 95% for the pixels and the game's own logic. At 64 actors
and a screenful of tiles that is a ceiling of about 800 us. It is met today, at
~0.3%.

Missed targets, and the item that closes each:

# Target Today Item
6 Pool acquire under 100 ns regardless of fill 250.9 ns on the last free slot 1
7 Pool release proportional to bytes used 47.2 ns, a fixed 4 KiB wipe 1
8 Re-drawing an unchanged line of text under 1 us 12.6 us, every frame 2
9 Zero texture create/destroy per frame in steady state one of each per line per frame 2
10 A handled routine condition costs no more than 2x the success path 9x 3
13 Level load under 100 ms at 8 tilesets / 4 layers / 64 actors unknown -- no fixture 8
14 Fixed per-load overhead under 1% of a level load 11.5% 9
15 Static footprint under 4 MB 28 MB 9

The order is set by blast radius per frame and per byte, not by how interesting
the change is.

Items 1 through 6 are the next changes. 7 through 9 are on record so that the
decision that unblocks each is made with the numbers in front of it. Target 12 --
collision for 256 actors under 2 ms -- was item 7 and is done in 0.8.0: the
incremental uniform grid beats the BSP by 2.6x on the same 64-proxy population,
and a whole physics step with collision is 54.1 us at 256 actors against a 2 ms
budget.

Files: src/heap.c, src/text.c, src/character.c, src/actor.c, src/tilemap.c, src/renderer.c, PERFORMANCE.md

Items:


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

**Source:** TODO.md, "Performance" -> "The plan" (at bbb7b8f) The first measured baseline is in `PERFORMANCE.md`, produced by `tests/perf.c` and `tests/perf_render.c` (`ctest --test-dir build -L perf`). Both suites hold every measurement to a budget set at roughly ten times the recorded baseline, so an algorithmic regression fails the suite rather than being discovered by a player. **Read `PERFORMANCE.md` before arguing with anything below -- every claim has a number behind it, and several of the things expected to be slow are not.** The governing target: **libakgl's own bookkeeping should never be the reason a frame is late.** Everything the library decides -- pool scans, registry lookups, state-to-sprite mapping, physics, visibility, error contexts -- should fit in 5% of a 16.67 ms frame, leaving 95% for the pixels and the game's own logic. At 64 actors and a screenful of tiles that is a ceiling of about 800 us. It is met today, at ~0.3%. Missed targets, and the item that closes each: | # | Target | Today | Item | |---|---|---|---| | 6 | Pool acquire under 100 ns regardless of fill | 250.9 ns on the last free slot | 1 | | 7 | Pool release proportional to bytes used | 47.2 ns, a fixed 4 KiB wipe | 1 | | 8 | Re-drawing an unchanged line of text under 1 us | 12.6 us, every frame | 2 | | 9 | Zero texture create/destroy per frame in steady state | one of each per line per frame | 2 | | 10 | A handled routine condition costs no more than 2x the success path | 9x | 3 | | 13 | Level load under 100 ms at 8 tilesets / 4 layers / 64 actors | unknown -- no fixture | 8 | | 14 | Fixed per-load overhead under 1% of a level load | 11.5% | 9 | | 15 | Static footprint under 4 MB | 28 MB | 9 | **The order is set by blast radius per frame and per byte, not by how interesting the change is.** Items 1 through 6 are the next changes. 7 through 9 are on record so that the decision that unblocks each is made with the numbers in front of it. Target 12 -- collision for 256 actors under 2 ms -- was item 7 and **is done in 0.8.0**: the incremental uniform grid beats the BSP by 2.6x on the same 64-proxy population, and a whole physics step with collision is 54.1 us at 256 actors against a 2 ms budget. **Files:** `src/heap.c`, `src/text.c`, `src/character.c`, `src/actor.c`, `src/tilemap.c`, `src/renderer.c`, `PERFORMANCE.md` **Items:** - [ ] #21 - [ ] #22 - [ ] #23 - [ ] #24 - [ ] #25 - [ ] #26 - [ ] #27 - [ ] #28 --- Filed by Tachikoma (Claude Code, Opus 5, 1M context)
tachikoma added this to the 0.9.x milestone 2026-08-02 18:34:50 -04:00
tachikoma added the performanceblast-radius:high labels 2026-08-02 18:34:50 -04:00
tachikoma added the status::grooming label 2026-08-02 18:49:27 -04:00
Sign in to join this conversation.