BytorioGet started

DOCUMENTATION / experiments/luau-executor/BATCHED-RESULTS.md

Batched drawing and cached executor results

Recorded 2026-10-19 on the same Windows x64 / Core i7-11700K / Chrome 155 desktop as the rectangle baseline, using Wasmtime 49.0.3. Five processes/workers per variant, two warmup passes and five measured passes per instance: 3,350 timed frames per variant. Measurements ran serially. Numeric snapshot, drawing protocol, cache behavior.

Before and after

Mean CPU frame work includes game update, command generation/validation/copy, and shared software rendering. It excludes presentation, audio delivery, hashing, Worker messaging, and the separately measured browser framebuffer copy. Cached and cold code execute the same commands; caching primarily changes startup.

Workload Original rectangle path Batched path, cached executor Change
Native Luau, metered 4.762 ms/frame 0.276 ms/frame About 17× less CPU time
Browser Luau 3.501 ms/frame 0.182 ms/frame About 19× less CPU time
Native Odin, metered 0.209 ms/frame 0.201 ms/frame Roughly unchanged
Browser Odin 0.137 ms/frame 0.120 ms/frame Small improvement
Native Luau startup 166.80 ms, no cache 10.42 ms, warm cache Compilation avoided on reuse
Browser Luau startup 32.50 ms, no application cache 8.40 ms, warm page cache Compiled code and bytes reused

The initial scene now sends 255 commands instead of 8,582. Its sprites and text are expanded in the shared Odin renderer, using a cartridge-owned atlas/font uploaded once. All 134 quest frames and 76 defeat/recovery frames retain the pre-optimization hashes and sound-request counts in tests/rpg/parity.json.

These are observations from two runs on one desktop, not isolated language speed measurements. The browser loader also changed to asynchronous module preparation and sharing; engine caching/tiering contributes to startup behavior. No low-end/mobile performance claim is made.

Current variants

Host / execution Cartridge Mean frame work Frame p95 Median startup
Native, metered Odin 0.201 ms 0.255 ms 8.49 ms
Native, metered Luau, cold cache 0.274 ms 0.336 ms 194.89 ms
Native, metered Luau, warm cache 0.276 ms 0.351 ms 10.42 ms
Native, unmetered benchmark Odin 0.201 ms 0.254 ms 6.67 ms
Native, unmetered benchmark Luau, cold cache 0.258 ms 0.313 ms 148.02 ms
Native, unmetered benchmark Luau, warm cache 0.259 ms 0.324 ms 8.95 ms
Chrome Worker Odin 0.120 ms 0.200 ms 5.40 ms
Chrome Worker Luau, bypass page cache 0.181 ms 0.300 ms 21.00 ms
Chrome Worker Luau, warm page cache 0.182 ms 0.300 ms 8.40 ms

Cold native Luau still incurs compilation and cache population: 191.05 ms median module compilation versus 6.62 ms cached module loading. Luau source/VM initialization remains about 1.7 ms in either native case. Cold starts are not eliminated; the cache helps subsequent loads of the same executor/configuration. All five warm native cases reported a cache usage count of two.

All five cached browser loads reported hit: true, compileMs: 0 for the executor, and downloadedExecutor: false. Median Worker creation-to-ready time including page-side preparation was 16.3 ms. The table's browser startup metric includes page-side executor preparation and Worker-side loading through first framebuffer, while excluding Worker creation/message delivery. Native startup remains measured inside the player, before reading module/source files through first framebuffer; it excludes executable/DLL loading. Compare startup within a host. Browser cold controls cannot flush internal engine caches.

Frame work and memory

Native metered Luau submission dropped from 4.690 ms to 0.076 ms, including validation and buffer copies. Game update averages 0.002 ms. The shared renderer now costs about 0.198 ms for either language. It does more work than the old rectangle renderer because sprite/font expansion moved into it, which largely offsets Odin's submission savings. The remaining native gap is about 0.075 ms/frame, rather than a broad 23× language difference.

Browser cached Luau averages 0.062 ms for submission and 0.117 ms for rendering; Odin averages 0.0035 ms and 0.116 ms. Final framebuffer copying adds about 0.093 ms and 0.092 ms respectively, outside the frame-work totals.

Memory measure Odin Luau, cold cache Luau, warm cache
Allocated guest linear memory 1.5625 MiB 2 MiB 2 MiB
Native working-set median, metered, after replay 19.1 MiB 29.4 MiB 17.1 MiB
Native maximum observed peak working set, metered 21.0 MiB 136.4 MiB 19.6 MiB

The browser renderer's separate linear memory is now 2.5625 MiB for either guest, including atlas and both command formats. The tracked Luau heap peaks at about 687 KiB; it is already part of the 2 MiB guest allocation. Browser cached bytes and compiled code are additional player-side memory, not included in guest allocation. Native working sets cover the full headless process, including Wasmtime and JIT/cache activity; peak compilation residency differs sharply from steady replay residency. Total browser process RAM is unmeasured.

The current executables/source bundles are about 14.1 KiB for the Odin cartridge and 15.1 KiB for the Luau cartridge. The shared player-supplied executor is about 694 KiB; it is not duplicated in cartridges.

Reproduce and interpret

npm run build:unified
node experiments/luau-executor/verify-drawing.mjs
node experiments/luau-executor/verify-cache.mjs
npm run bench:unified

Use PLAYER_BROWSER_CHANNEL=chrome for an installed Chrome. Raw data goes to build/unified-player/benchmark-batched/. The harness creates unique native cache directories without deleting existing caches. It verifies both native SDL render/audio paths, browser input/audio, Luau diagnostics, buffer validation, cache reuse/invalidation, and fresh game state. The original hello reproduction's recovery tests also pass.

The implementation now makes Luau practical for this RPG with little absolute CPU cost. It does not settle performance for vehicle simulation, world streaming, traffic, or allocation-heavy gameplay. Those remain separate workloads to measure before choosing a default language for more demanding cartridges.