mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-22 05:59:31 +08:00
Mature and old passes are scheduled by heap growth. While growth-triggered passes at a level keep finding nothing, the fraction they wait for widens by doublings from 1/4, and it used to stop at 1, so a large live heap that keeps growing was walked in full once per doubling. On tsc-ts those walks are most of the collector's time: in single-threaded Excalidraw, 12 growth-triggered full passes cost 2.0 s of 14.7 s, the last two walking 2.6M and 5.2M objects to free 1.4%. The cap is now 16. That is a memory trade-off: once passes stop finding garbage, the cycle-headered heap may grow to 17 times its size after the last pass at a level before the next (it was 2 times), so a program that accumulates dead cyclic garbage while passes find nothing can hold up to 16 times more of it. Yield-driven narrowing is unchanged: a pass that finds old garbage moves the fraction straight back to what its yield asks for. SCR_CYCLE_GROWTH_CAP lowers the cap for memory-sensitive programs, read like SCR_CYCLE_THRESHOLD: a power of two from 1 to 16 (other positive values round down, larger ones read as 16); 1 restores the previous schedule. test_cycle pins the cap on a heap at the growth floor: a dead cycle through the old generation must go exactly when the heap has grown the configured cap past the floor (not one doubling earlier, not later), and a pass that finds old garbage must narrow the fraction at once. cycle.test.ts runs it at the default cap and at SCR_CYCLE_GROWTH_CAP=1, 3 and 64. The large-chain widening check keeps its walk bound but no longer waits out the cap on a 266k-node structure, and now runs after the tests that assume the base schedule. Measured with tsc-ts origin/parallel-check e469e33, built with this runtime vs the parent commit, interleaved in one Linux Vercel Sandbox (8 vCPU; 5 rounds, medians; --checkers 8 repeated with 10 rounds). Diagnostics identical. "cap 1" is this build with SCR_CYCLE_GROWTH_CAP=1: single-threaded wall RSS cap 1 Excalidraw -10.1% 1283 -> 1263 +0.5% Playwright -11.0% 1446 -> 1457 +0.3% --checkers 8 Excalidraw -8.7% / -4.5% 2110 -> 2132 / 2115 -> 2125 +2.6% (10 rounds) Playwright -7.8% / -7.7% 2616 -> 2634 / 2589 -> 2678 -1.0% (10 rounds) RSS in MB (max of two runs). The runtime benchmark suite's allocation-heavy workloads (alloc-trees, word-graph, ast-interp, json-records, records-sort, functional-pipeline; --layouts=4 --runs=16) are neutral in time and peak RSS (-0.3% to +1.7%).