Summary
asyncpipe at its shipped size (120 batches) sits exactly on a nursery collection trigger boundary, which makes it unusable as an A/B target and makes any change touching allocation volume look arbitrarily good or bad.
Measured while landing #7924 (Promise.all fast arm):
| arm |
asyncpipe @120 |
copying minors |
| before |
— |
0 |
| after |
+4.7% |
1 |
The fast arm allocates strictly less, and the entire regression is one extra copying minor, decided by a 444 KB window on 17.5 MB of arena in-use. With the nursery cap inactive the same change on the same program is −9.0%.
At larger sizes the change is unambiguous: @240 −33.9%, @360 −34.3%, @480 −30.7%, with identical GC-cycle counts in both arms.
Why this is a benchmark defect and not a #7924 defect
A workload poised on a trigger boundary measures the boundary, not the program. Any future change that moves allocation volume by a fraction of a percent will flip the cycle count and swamp its real effect — in either direction. That is the same hazard as the constant-band pacing bugs (#7592, #7690): a threshold that a workload sits exactly on.
It also interacts with two known findings:
So the trigger this benchmark straddles is one we already believe is mis-paced here.
Requested
- Establish where the cliff is (bisect batch count against
copying_minors), and record it.
- Either move the corpus
asyncpipe off the boundary, or add a second size that is provably clear of it, so allocation-affecting changes have an honest target. Do not simply re-scale silently — whichever is chosen, the standings table must note that the program changed, or the round-over-round deltas become meaningless.
- Until then:
asyncpipe@120 must not be used to size Promise/async allocation work. Use @240 or larger, and report the cycle counts of both arms alongside any number.
Summary
asyncpipeat its shipped size (120 batches) sits exactly on a nursery collection trigger boundary, which makes it unusable as an A/B target and makes any change touching allocation volume look arbitrarily good or bad.Measured while landing #7924 (
Promise.allfast arm):The fast arm allocates strictly less, and the entire regression is one extra copying minor, decided by a 444 KB window on 17.5 MB of arena in-use. With the nursery cap inactive the same change on the same program is −9.0%.
At larger sizes the change is unambiguous: @240 −33.9%, @360 −34.3%, @480 −30.7%, with identical GC-cycle counts in both arms.
Why this is a benchmark defect and not a #7924 defect
A workload poised on a trigger boundary measures the boundary, not the program. Any future change that moves allocation volume by a fraction of a percent will flip the cycle count and swamp its real effect — in either direction. That is the same hazard as the constant-band pacing bugs (#7592, #7690): a threshold that a workload sits exactly on.
It also interacts with two known findings:
asyncpipe's collection cost is per registered root (82 scanners over 218,455 roots), not per copied object, so a single extra minor is disproportionately expensive on this program specifically.So the trigger this benchmark straddles is one we already believe is mis-paced here.
Requested
copying_minors), and record it.asyncpipeoff the boundary, or add a second size that is provably clear of it, so allocation-affecting changes have an honest target. Do not simply re-scale silently — whichever is chosen, the standings table must note that the program changed, or the round-over-round deltas become meaningless.asyncpipe@120 must not be used to size Promise/async allocation work. Use @240 or larger, and report the cycle counts of both arms alongside any number.