Repository navigation
Opt target 1 — typed_array: 6.29x Node, 8.52x instructions #8590
Description
Opt target 1 — typed_array: 6.29x Node, 8.52x instructions
This is the single largest remaining gap in the corpus, roughly three times worse than
anything else Perry runs. Take this one first.
Measurement — quiet M1 mini, 5 shuffled interleaved repeats, verdict CLEAN, perry fefdc367b
| node | perry | ratio | |
|---|---|---|---|
| wall | 0.646 s | 4.064 s | 6.29x |
| instructions | 6.539 G | 55.717 G | 8.52x |
| cycles | 2.066 G | 13.012 G | 6.30x |
| IPC | 3.16 | 4.28 | — |
| peak RSS | 82.2 MB | 5.3 MB | 0.06x |
Read the IPC before you start. Perry retires instructions more efficiently than Node here
(4.28 vs 3.16) and still loses 6.29x. Nothing is stalling; the machine is executing the work
well. There are simply 8.5x too many instructions. Do not go looking for a cache or memory
problem — this is a "delete work" task, not a "speed up work" task.
Workload: gc-handoff/apps/typed_array.ts — bcryptjs's Blowfish _encipher shape, S/P
Int32Arrays reached through untyped parameters, ~4.27 M calls, indexed with >>>/| and
accumulated with +/^. Prints only checksum:-821955270, which must not change.
What is already known — do not re-derive any of this
- The S-box element loads are already native. Optimized AArch64 shows
ldr ..., [base, index, uxtw #2]— no NaN-box round trip, no per-element registry lookup.
An earlier agent assumed otherwise, built a "focused exact-index optimization", got −24.6%
instructions almost entirely on the typed path, and correctly reverted it because it made
the untyped/typed ratio worse. - This is no longer a Perf: untyped typed-array element access ~6x slower (per-access thread-local kind lookup) — bcryptjs cost-12 ~28s vs ~250ms #5525 untyped-vs-typed gap. perf(codegen): preserve numeric envelope proofs #8491 closed that: the ratio is 1.0017.
Both paths are slow together. Chasing the ratio chases a solved problem. - perf(codegen): preserve numeric envelope proofs #8491 already took −44.1% wall / −34.7% instructions / −52.6% RSS out of this workload
by making a numeric type proof survive reassignment (number_by_construction). What remains
is a different part of the envelope, or something else entirely.
Method
Profile before hypothesising. Perry strips its output binaries, so compile with
PERRY_KEEP_SYMBOLS=1 PERRY_DEBUG_SYMBOLS=1 or sample attributes everything to ???.
Aggregate over many runs. #8491 removed what used to be the dominant leaves
(js_number_coerce, js_dynamic_bitxor, js_dynamic_string_or_number_add,
lookup_registered_typed_array_kind), so the current profile is genuinely unknown.
An 8.5x instruction ratio against a mature JIT on a tight integer loop usually means something
structural is still on the per-iteration path — a guard, a bounds check, a rooting store, a
call that should have been inlined. Find which before changing anything.
Acceptance
- Meaningful instruction reduction. Instructions retired is the primary signal.
- The win must not be lopsided. Report the untyped/typed ratio before and after; it is
1.0017 now and must not regress materially. An optimization that only helps the typed path
reopens Perf: untyped typed-array element access ~6x slower (per-access thread-local kind lookup) — bcryptjs cost-12 ~28s vs ~250ms #5525 and will be rejected. checksum:-821955270unchanged; all 20 corpus programs byte-exact.- Time the whole 20-row corpus before and after; report every row moving more than 1%.
- Peak RSS is 0.06x Node here — enormous headroom, but the standing constraint is to minimize
RSS while keeping the best compute. Quantify and argue any RSS cost rather than spending it
quietly.
Activity