Benchmark the boundary and close the cold read's tutorial gaps

The interop test now ends with a measured comparison: 24,000 formation
updates through the script boundary against a line-for-line C
translation of the same state machine. 881 us against 0.01 us per call
on this machine, quoted verbatim in the new chapter 21 Step 11 with the
architectural decisions it prices.

A Haiku-class cold read of the chapters produced a build whose failures
were all mechanical -- invented include paths, never-shown sink statics,
guessed status codes and character names. The chapters now carry the
include lists, the script.c statics, the status-code roster, the
sprite/character table, the full CMake recipe and the explosion spawn's
HANDLE example, so none of those have to be guessed again.

Co-authored-by: andrew <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
This commit is contained in:
2026-08-04 09:01:59 -04:00
parent d5a0edd692
commit 54ab85a276
4 changed files with 322 additions and 5 deletions

View File

@@ -35,6 +35,8 @@ whole development loop; the engine never rebuilds.
the game, and make it do that
- **[Step 10](#step-10-prove-it)** — prove the boundary with a test that links
the real files
- **[Step 11](#step-11-the-cost-measured)** — measure what thinking in BASIC
costs, against the same logic in C
---
@@ -507,6 +509,56 @@ the readout tells you how each brain did:
galaga: 3000 frames, screen 2, score 2350, alive 0, kills bee 20 bfly 15 boss 1, shots bee 1 bfly 1 boss 1, script errors 0
```
## Step 11: The cost, measured
**Goal: the real price of the boundary, in numbers, next to the same logic in C.**
The interop test binary ends with a benchmark: 24,000 formation-hold updates —
forty enemies at sixty frames a second for ten seconds — once through
`galaga_script_update_enemy()` and once through a line-for-line C translation
of `UPDATEBEE` with its helpers inlined. Same guard, same branches, same
arithmetic; the difference is the interpreter. On this repository's build
machine (a two-core VM, the interpreter built `-O2`):
```text
benchmark: 24000 formation-hold updates, dt 0.016
BASIC through the boundary: 21.147 s 881.11 us/call 35.245 ms per 40-enemy frame
the same logic in C: 0.000 s 0.01 us/call 0.001 ms per 40-enemy frame
ratio: 61022x
```
The facts, without decoration:
- **A BASIC-driven update costs about four orders of magnitude more than the
same logic compiled.** The C translation of the whole state machine costs
tens of *nano*seconds; the scripted call costs high hundreds of
*micro*seconds.
- **The cost is per line executed, not per call.** The interpreter scans and
parses each body line from source text on every call; a 3-line body measured
~148 us on this class of machine, and this ~15-line body measures ~881 us.
Body length is the knob.
- **At this cost, forty thinking enemies spend ~35 ms per frame on this
hardware** — more than two 60 Hz frames. The shipped example visibly runs
below 60 fps on this machine while the whole wave is alive, and exactly at
its frame pace once the wave thins. A faster machine moves the numbers, not
the shape.
This is the measured version of decisions the chapters already made on
architectural grounds. Bullets, collision and the starfield are C
([Chapter 20](20-tutorial-galaga.md), Steps 2 and 4) — at two shots and forty
tests a frame, scripting them would multiply the call count for things that
decide nothing. The fire decision is one flag rather than a per-bullet
callback (Step 6): the script's call budget is bounded by the enemy count and
nothing else. C owns the formation and the spawn timing (Step 8), so zero
calls happen for enemies that do not exist yet. And the 36 KiB function slots
and 2.40 MiB runtime (Step 5) are the memory half of the same bill.
What the cost buys is the previous ten steps: behavior as data, edited and
swapped without a compiler. Whether ~900 us per thinking entity per frame is
acceptable is a per-project decision — fewer thinkers, shorter bodies, or a
lower think rate (every Nth frame) are the standard levers, and all three are
host-side choices this architecture leaves open.
---
Where to go from here: more waves are rows in the table; a new enemy kind is