You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The adaptive tenuring loop selects survival age S = 1 for a large minority of
minors on claude-code, and each turn pays 2-5 full collections that reclaim
0.2-0.3 % for it. Pinning the age removes them, monotonically. This is on main; it was surfaced by the regex-header-nursery work (#9845) but is not
caused by it — the unmodified allocator arm shows it identically.
Measurement
One 400-character claude-code reply, offline mock-API rig, sandbox rx. PERRY_GC_DIAG=1 + PERRY_GC_TRACE=1. A = unmodified allocator, R = #9845's nursery header. =N pins PERRY_GC_TENURING_SURVIVALS.
arm
tenuring ages chosen
fulls
first-full reclaim
old-gen live at last full
A adaptive
{1:16, 2:6, 3:5, 4:15}
7
0.2 %
49.9 MB
A adaptive
{1:12, 2:6, 3:5, 4:15}
5
0.3 %
48.3 MB
A pinned =2
{2:38}
3
0.3 %
45.3 MB
R adaptive
{1:13, 2:5, 3:5, 4:11}
5
0.2 %
48.7 MB
R adaptive
{1:15, 2:5, 3:4, 4:12}
7
0.2 %
46.6 MB
R pinned =2
{2:35}
3
0.3 %
45.5 MB
R pinned =4
{4:36}
2
0.2 %
34.7 MB
Two readings:
The effect is on main, not on perf(regex): allocate the RegExp header in the nursery, not the malloc arm #9845. A and R adaptive overlap completely
({7,5} against {5,7}) and their age distributions are near-identical, so the
allocator change does not alter tenuring behaviour. A pre-registered
prediction that R would show more fulls was falsified by this matrix.
Fewer fulls, monotone in the pinned age, in both arms: 5-7 adaptive -> 3
at =2 -> 2 at =4, with old-gen live at the last full falling 48-50 MB ->
45 MB -> 35 MB.
Why this is a defect and not a tuning preference
The fulls being removed reclaim 0.2-0.3 %. By #9589's rule — price a
collection by what it freed, never by occupancy — they are unproductive, and the
policy is scheduling 2-5 of them per turn because it tenured objects that were
about to die. Promoting on the first survival converts a short-lived object into
old-gen occupancy, and occupancy is what schedules the full.
This is the cost half of the argument; the arena lane owns the producer and the
structural fix (the occupancy rule's S = 1 + desired/influx reaching exactly 1
with no rung at 2 or 3, and the invariant that S = 1 destroys the measurement
that could refute it, since prev_copied = 0 holds the guard false forever).
Its rule — occupancy may lower only to 2, and reaching 1 belongs to the
mortality-measuring exits — is consistent with this matrix.
Three constraints on how these numbers may be used
Count claim only. This box ran at load 100-300 throughout; its CPU
figures are unusable and none are quoted here. Every number above is a count
or a byte total from the trace.
Not merged with the other host. These fulls reclaim 0.2-0.3 %; the same
arms on a quiet host reclaim 14-25 %. The two hosts sit in different regions
of the same policy and their numbers are not combined into one table.
PERRY_GC_TENURING_SURVIVALS=2 is the diagnostic, not the fix. It is
documented diagnostic-only, and moving a threshold is explicitly not the
remedy being proposed.
Reproduce
stream_scale.py LABEL HOME PORT 100 400 150 \
--env PERRY_GC_DIAG=1 --env PERRY_GC_TRACE=1 --stderr OUT.txt -- <binary>
stream_scale.py LABEL HOME PORT 100 400 150 \
--env PERRY_GC_DIAG=1 --env PERRY_GC_TRACE=1 \
--env PERRY_GC_TENURING_SURVIVALS=2 --stderr OUT_t2.txt -- <binary>
Count collection_kind == "full" records, and read tenuring_survivals= off
the [gc-copy-minor] ran lines.
The adaptive tenuring loop selects survival age
S = 1for a large minority ofminors on claude-code, and each turn pays 2-5 full collections that reclaim
0.2-0.3 % for it. Pinning the age removes them, monotonically. This is on
main; it was surfaced by the regex-header-nursery work (#9845) but is notcaused by it — the unmodified allocator arm shows it identically.
Measurement
One 400-character claude-code reply, offline mock-API rig, sandbox
rx.PERRY_GC_DIAG=1+PERRY_GC_TRACE=1.A= unmodified allocator,R= #9845's nursery header.=NpinsPERRY_GC_TENURING_SURVIVALS.{1:16, 2:6, 3:5, 4:15}{1:12, 2:6, 3:5, 4:15}=2{2:38}{1:13, 2:5, 3:5, 4:11}{1:15, 2:5, 3:4, 4:12}=2{2:35}=4{4:36}Two readings:
main, not on perf(regex): allocate the RegExp header in the nursery, not the malloc arm #9845. A and R adaptive overlap completely({7,5} against {5,7}) and their age distributions are near-identical, so the
allocator change does not alter tenuring behaviour. A pre-registered
prediction that R would show more fulls was falsified by this matrix.
at
=2-> 2 at=4, with old-gen live at the last full falling 48-50 MB ->45 MB -> 35 MB.
Why this is a defect and not a tuning preference
The fulls being removed reclaim 0.2-0.3 %. By #9589's rule — price a
collection by what it freed, never by occupancy — they are unproductive, and the
policy is scheduling 2-5 of them per turn because it tenured objects that were
about to die. Promoting on the first survival converts a short-lived object into
old-gen occupancy, and occupancy is what schedules the full.
This is the cost half of the argument; the arena lane owns the producer and the
structural fix (the occupancy rule's
S = 1 + desired/influxreaching exactly 1with no rung at 2 or 3, and the invariant that
S = 1destroys the measurementthat could refute it, since
prev_copied = 0holds the guard false forever).Its rule — occupancy may lower only to 2, and reaching 1 belongs to the
mortality-measuring exits — is consistent with this matrix.
Three constraints on how these numbers may be used
figures are unusable and none are quoted here. Every number above is a count
or a byte total from the trace.
arms on a quiet host reclaim 14-25 %. The two hosts sit in different regions
of the same policy and their numbers are not combined into one table.
PERRY_GC_TENURING_SURVIVALS=2is the diagnostic, not the fix. It isdocumented diagnostic-only, and moving a threshold is explicitly not the
remedy being proposed.
Reproduce
Count
collection_kind == "full"records, and readtenuring_survivals=offthe
[gc-copy-minor] ranlines.