Summary
Triaging #8103 sized its prize and found the element-shape fact is only a ~23% ceiling. The other ~77% is per-callback invocation machinery — roughly 368 instructions per call. forEach is about 16x the equivalent for loop, and landing #8103 as written would leave it there.
This is filed separately because #8103 is scoped to a repsel fact and this is not; working #8103 first would deliver under a quarter of the available win.
The measurement
Reproduced #8103's own program (same t: 80000000000, same Ptr<Shape> verdicts: 0 selected / 1 denied), then isolated the components by replacing the callback body with s = s + 1:
| component |
instructions |
share |
| field reads inside the callback |
435.9 M |
~23% |
| per-callback invocation |
~1.47 G |
~77% |
| total |
1.91 G |
|
Against a for loop over the same data: 92.1 M vs 1.32 G, a 14x gap — not the 10.1% #8103 reports. That issue's percentage was diluted by a ~15.7 G common term; its increment (1.59 G) does match the measured 1.23 G, so the absolute finding was sound and only the ratio was wrong.
~368 instructions per callback invocation is the number to attack.
Why this is the better target
Caveat on the split, stated honestly
The 23/77 partition was measured under an opt-level-1 runtime. Both sides of the split are runtime calls, so it is less biased than a generated-vs-runtime comparison would be, but the bias was not bounded. Worth re-establishing at release opt-level before committing significant work.
What to look at
The invocation path is js_closure_call* plus whatever the fused array helpers do per element — argument marshalling, NaN-boxing, and the handle/rooting traffic around each call. Two adjacent findings that may share the cost:
Refs #8103, #8179, #8175.
Summary
Triaging #8103 sized its prize and found the element-shape fact is only a ~23% ceiling. The other ~77% is per-callback invocation machinery — roughly 368 instructions per call.
forEachis about 16x the equivalentforloop, and landing #8103 as written would leave it there.This is filed separately because #8103 is scoped to a repsel fact and this is not; working #8103 first would deliver under a quarter of the available win.
The measurement
Reproduced #8103's own program (same
t: 80000000000, samePtr<Shape>verdicts:0 selected / 1 denied), then isolated the components by replacing the callback body withs = s + 1:Against a
forloop over the same data: 92.1 M vs 1.32 G, a 14x gap — not the 10.1% #8103 reports. That issue's percentage was diluted by a ~15.7 G common term; its increment (1.59 G) does match the measured 1.23 G, so the absolute finding was sound and only the ratio was wrong.~368 instructions per callback invocation is the number to attack.
Why this is the better target
forEach,map,filter,reduce,some,every— not just the ones that would gain an element-shape proof.Caveat on the split, stated honestly
The 23/77 partition was measured under an opt-level-1 runtime. Both sides of the split are runtime calls, so it is less biased than a generated-vs-runtime comparison would be, but the bias was not bounded. Worth re-establishing at release opt-level before committing significant work.
What to look at
The invocation path is
js_closure_call*plus whatever the fused array helpers do per element — argument marshalling, NaN-boxing, and the handle/rooting traffic around each call. Two adjacent findings that may share the cost:dispatch_uint8_buffer_methodholds its reduce accumulator and map output in unrooted locals acrossjs_closure_call*. Fixing that correctly will add rooting traffic to the same loop, so the two interact and should be measured together.fib40's IPC collapsed to 1.92 against a corpus norm of 4.5–6.5, i.e. call-heavy numeric code is stalling rather than merely executing many instructions. If callback invocation shares that stall, instruction counting will understate the win here too.Refs #8103, #8179, #8175.