#8417 fixed #8394's quadratic concat but regressed iso_miss by 24.8%
Bisected on the quiet bench mini sweep. Clean adjacent-commit attribution:
| commit |
iso_miss instructions |
ddc1cd845 (parent) |
11.54 G |
5f9aa1f40 — #8417 "retain string accumulators in concat chains" |
14.68 G |
That is +27.2% instructions, and on the mini it shows as wall time 0.6810 s -> 0.8501 s
(+24.8%), pushing iso_miss from 1.96x to 2.53x Node. The sample ranges are fully
disjoint (previous max 0.7080 < current min 0.8501), so this is not noise.
-Os (#8457) is not the cause — forcing PERRY_LL_SIZE_OPT=0 gives +0.8% with
overlapping samples, and both arms already sit at ~14.7 G.
This is a real trade, not a mistake
#8417 did exactly what it claimed. Re-measured on the same fixture from #8394:
| n |
before #8417 |
after #8417 |
Node |
| 2000 |
17 ms |
3 ms |
1 ms |
| 16000 |
1450 ms |
16 ms |
1 ms |
90x at n=16000, and the curve went from roughly quadratic to flat. That is a large,
real win on accumulator-shaped concatenation.
The problem is that iso_miss builds a string it usually throws away — its hot line is
seen = seen + "[" + names[i] + "]"; // iso_miss.ts:205, per name per env frame
and the result is only read on a lookup miss. Retaining the accumulator helps when the
string is kept and hurts when it is discarded. iso_miss is the discard case, and it is the
row where string concat was already ~18% of self time.
What would resolve it
Ideally the retention becomes conditional rather than unconditional — retain when the
accumulator is subsequently read, fall back when it is dead. Whether that is cheaply
decidable in literals_vars.rs is the open question; if it is not, this is a genuine
policy choice about which shape to favour and should be recorded as one.
Note #8417 is small (crates/perry-codegen/src/expr/literals_vars.rs, +42/-1 plus tests),
so the surface to reason about is narrow.
Process note
I merged #8417 after validating it against the #8394 scaling fixture and confirming 19/19
corpus byte-exactness — but I did not run a timing A/B across the corpus, only a
correctness one. A perf PR that changes codegen for a common operation needs the corpus
timed, not just diffed. That gap is why this took a full sweep to surface.
#8417 fixed #8394's quadratic concat but regressed
iso_missby 24.8%Bisected on the quiet bench mini sweep. Clean adjacent-commit attribution:
iso_missinstructionsddc1cd845(parent)5f9aa1f40— #8417 "retain string accumulators in concat chains"That is +27.2% instructions, and on the mini it shows as wall time 0.6810 s -> 0.8501 s
(+24.8%), pushing
iso_missfrom 1.96x to 2.53x Node. The sample ranges are fullydisjoint (previous max 0.7080 < current min 0.8501), so this is not noise.
-Os(#8457) is not the cause — forcingPERRY_LL_SIZE_OPT=0gives +0.8% withoverlapping samples, and both arms already sit at ~14.7 G.
This is a real trade, not a mistake
#8417 did exactly what it claimed. Re-measured on the same fixture from #8394:
90x at n=16000, and the curve went from roughly quadratic to flat. That is a large,
real win on accumulator-shaped concatenation.
The problem is that
iso_missbuilds a string it usually throws away — its hot line isand the result is only read on a lookup miss. Retaining the accumulator helps when the
string is kept and hurts when it is discarded.
iso_missis the discard case, and it is therow where string concat was already ~18% of self time.
What would resolve it
Ideally the retention becomes conditional rather than unconditional — retain when the
accumulator is subsequently read, fall back when it is dead. Whether that is cheaply
decidable in
literals_vars.rsis the open question; if it is not, this is a genuinepolicy choice about which shape to favour and should be recorded as one.
Note #8417 is small (
crates/perry-codegen/src/expr/literals_vars.rs, +42/-1 plus tests),so the surface to reason about is narrow.
Process note
I merged #8417 after validating it against the #8394 scaling fixture and confirming 19/19
corpus byte-exactness — but I did not run a timing A/B across the corpus, only a
correctness one. A perf PR that changes codegen for a common operation needs the corpus
timed, not just diffed. That gap is why this took a full sweep to surface.