Metabolism: region pool — SharedArrayBuffer arena, band-parallel phases (block sums / VM decisions / advection+reaction), bit-identical to serial; 5.9x at 41k organisms; provider protocol sketched

This commit is contained in:
Dejvino
2026-10-28 15:25:52 +01:00
parent 3a72525ccb
commit d6e97a2b52
5 changed files with 685 additions and 268 deletions
+46
View File
@@ -249,6 +249,52 @@ and wander); rock has a budget that survives a whole film (calm songs keep
sparse stone, percussive ones accumulate walls); agriculture consumes stone
instead of minting it. 44 tests green; realtime holds at 640×360.
### The region pool + the browser as workhorse (landed)
**Node stays CPU** — it is the dev, test and export path, simple on purpose.
**The browser is the workhorse**: it renders, it previews, and it has the
GPU. The compute architecture, in three layers:
1. **Think slower than you move** (landed): the strains decide every
`vmEvery`-th substep (default 2) and empty ground never thinks. Measured
at field 128 with 41 k organisms: 25 → 20 ms/frame, ecology unchanged.
2. **The region pool** (landed): the field is carved into horizontal
coarse-row bands; every shared array lives in ONE SharedArrayBuffer
(`arenaLayout`), and `World`'s substep splits into three band-local
phases — block sums, the strain programs, advection+reaction — separated
by atomics barriers (`beginPhase`). The main thread keeps only the
cross-band work: the war front (hits land at aim targets), the swap, the
rock economy, politics, decay. The mail carries the frame, the decide
flag, warmth, the living bodies and the current swap orders (the
workers' named arrays follow the main's swaps through the mail).
**Measured: 52.3 → 14.7 ms/frame (2 workers) → 8.9 ms/frame (4 workers)
at 41 k organisms — near-linear.** And the invariant that makes it safe:
the phases are EXACT partitions, so a parallel world is the serial world
BIT FOR BIT (pinned by a test across 0/2/4 workers). Node runs it on
`worker_threads` (lazily resolved, so the browser still loads the
module); the browser will run the same pool inside its simulation worker.
3. **The GPU** (first slice landed): the strain programs are uniform over
thousands of blocks — one dispatch per strain covers the whole decision
layer. `src/sim/gpu.js` + `tools/gpu-check.js` (browser-side validation
against the CPU interpreter) await device time.
**The browser as the server's worker (protocol sketch, next).** Once
rendering exists, the browser becomes a compute provider to the node farm:
- the farm (node) holds prepared songs (`out/prepared/*.json`) and a job
queue: `(songId, seed, frameFrom, frameTo)`;
- a browser provider connects (WebSocket), registers its capability
(GPU? cores?), receives a prepared artifact + a frame range, runs
`Run` inside its worker pool with the GPU decision layer, and streams
back encoded frames (PNG or raw) keyed by frame number;
- the farm stitches the frames into the film. Sim runs are sequential in
time, so the parallel unit is the TAKE (a song × seed × range), not a
frame — many takes in flight across many providers, with the farm
checkpointing world state at range boundaries so a provider can resume.
Determinism policy unchanged: node's CPU path is the canonical exporter;
the GPU/preview paths are the same world to within float noise.
### The decision layer on the GPU (analysis, first slice landed)
The profile of a heavy frame (field 128, ~40 000 organisms planted) says: