Skip to content

perf: #8417 regressed iso_miss +24.8% (+27.2% instructions) while fixing #8394's quadratic concat #8486

Description

@proggeramlug

#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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions