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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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