Skip to content

perf(gc): main regressed the retain cluster 2.2-4.8x — retain now runs 2 full collections where it ran none (suspect #7901/#7902) #7965

Description

@proggeramlug

Summary

The retain cluster regressed 2.2–4.8× on main between 8260a9e50 and d78efca41. Independently confirmed by a second party on a separate build.

The load-independent evidence is the collection census — main now runs full mark-sweeps on programs that previously ran none:

program collection_kind:"full" minor
retain 2 6
retain1 1 3
deeplist 1 3

(measured at cf9999855, PERRY_GC_TRACE=1 PERRY_GC_DIAG=1)

The last clean quiet-mini sweep at 8039fa798 recorded retain 0.153 s (1.13× node), retain1 0.068 s, deeplist 0.057 s — and that commit predates both suspects, so the regression is invisible to it. Corroborating timing on a loaded dev box (magnitude only, not a benchmark): retain 0.83 s, retain1 0.24 s, deeplist 0.24 s.

The original reporter measured retain 2,840 M → 12,421 M instructions with fulls costing 190 ms + 605 ms.

Suspected cause — not bisected

Two PRs merged in that window, both in the promotion/survival accounting:

Both are plausible on mechanism, and they interact: the survival ratio feeds the in-place-promotion decision, and promotion step size feeds the old-gen trigger.

The mechanism this most likely trips

A third party independently instrumented old_reclaim_pressure_due's absolute arm (old_in_use >= T && baseline < T) and found it races two quantities that both move with promotion, so whether it fires depends on promotion step size, not on the heap: same program, same live set, same total promotion, and changing only the schedule from (18.7, 34.6) to (17.7, 17.8) makes it fire twice and buy 588 ms of futile fulls.

#7592 exempted the proportional arm via GC_MAJOR_PACING_RETAINING and left this one. #7902 changing promotion step size is exactly the input that would newly trip it.

That also makes this an instance of the rule CLAUDE.md already carries, for the fifth time: never pace a collection on a quantity that collection does not move — and its corollary that a constant band cannot pace an O(live) process.

Asks

  1. Bisect 8260a9e50..d78efca41 on the full count (a counter, so a single run per commit suffices — no quiet host needed).
  2. Owners of memory(gc): copied minor subtracts from-space high-water from an exact live census #7901 and memory(gc): untraced promotion can retain an old-heap-sized phase-change cohort as live #7902 should look at the interaction rather than each PR alone; each may be individually correct.
  3. Whatever the fix, gate it on the full count, not on wall time — that is what makes this cheap to detect and is how it was found.

Note on downstream measurements

Any A/B taken against a base after e886b56dd is measuring against a regressed baseline. #7960 (first-cycle in-place promotion) reports −41% to −77% against current main but −14% to −31% against the pre-regression base; the latter is the honest figure and its author flagged this unprompted.

Metadata

Metadata

Assignees

Labels

performanceRuntime, compile-time, build-size, or memory performance

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions