Bytecode cache: write each code block's records contiguously, bodies breadth-first, expression info last
Stacked on #494 (only because both touch CachedTypes.cpp; this one stands alone logically — encoder-only, no format or decoder change).
What
Decoding a code block reads, besides its own arrays, one 104-byte CachedFunctionExecutable record per directly nested function (the bodies stay lazy). The encoder wrote those records depth-first: it recursed into child k's body — and that child's whole subtree — before writing child k+1's record. So the records one decode needs were spread across the entire subtree region of the file, and for a mapped payload (a bun build --compile --bytecode executable) decoding one block faulted in pages from most of the file.
Now CachedFunctionExecutable::encode queues the body instead of recursing (Encoder::deferBody), CachedCodeBlock::encode queues its ExpressionInfo onto a second queue (deferCold), and encodeDeferred() drains bodies FIFO (breadth-first — each body is again contiguous with its own children's records) and then the cold queue, so line/column tables that are only read to build a stack trace form a tail that ordinary execution never touches (and with #494 never even copies). Every pointer in the format is a relative offset, so the decoder doesn't care where things landed.
Measured
12 MB minified bundle (typescript, babel, prettier, zod, …) → 75 MB cache, 48k functions. Cache file mapped cold (posix_fadvise(DONTNEED) + mincore after decode), jsc shell, x64 Linux:
| decoded | depth-first (before) | this PR |
|---|---|---|
| top-level block only (module load) | 32.7 MB resident, 143 ms | 1.9 MB, 10 ms |
| + bodies of its 5,823 direct children | 50.4 MB, 280 ms | 17.8 MB, 117 ms |
| + grandchildren (15k blocks) | 64.5 MB, 524 ms | 34.7 MB, 242 ms |
| everything (48k blocks) | 71.7 MB, 785 ms | 71.7 MB, 465 ms |
File size is unchanged (75,227,216 → 75,159,840, the difference is alignment padding). JSTests/stress through the disk cache (write run + JSC_forceDiskCache read run, 5,259 tests, with and without #494's borrowing): identical pass/fail to main.