Files
Malte Ubl 73a99b2393 Let the cycle collector's growth fraction widen to 16
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%).
2026-10-10 14:44:14 -07:00
..
2026-10-06 19:01:30 -05:00
2026-07-22 18:08:04 -05:00
2026-07-22 18:08:04 -05:00
2026-07-22 18:08:04 -05:00
2026-10-06 19:01:30 -05:00
2026-07-22 18:08:04 -05:00
2026-07-22 18:08:04 -05:00
2026-07-22 18:08:04 -05:00
2026-07-22 18:08:04 -05:00