Skip to content

perf(codegen): #6812 w8/w13 — clone inlined-helper bodies, integer static keys, first-iteration peel#6841

Merged
proggeramlug merged 4 commits into
mainfrom
perf/6812-w8-temps-w13-intkeys
Jul 26, 2026
Merged

perf(codegen): #6812 w8/w13 — clone inlined-helper bodies, integer static keys, first-iteration peel#6841
proggeramlug merged 4 commits into
mainfrom
perf/6812-w8-temps-w13-intkeys

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Third slice of the #6812 beat-node campaign, widening the whole-loop write clone to the shapes real code produces. The scoreboard moves to 11 of 18 matrix rows beating node.

1. Inliner temp lets in the clone matcher — w8: 47 ms → 6 ms, 0.75× (beats node)
The call inliner already inlines write helpers (setCD(objs[i], r + i, r - i)), but leaves let x = r + i; let y = r - i; between the element alias and the writes — which broke the clone matcher's body shape, stranding every inlined-helper loop on the per-write path. The matcher now admits a leading run of immutable, unboxed numeric temps parsed in the same counter-expression grammar; write values resolve LocalGet(temp) by substitution, so the emitter and the finite-range proof see the trees the user could have written inline (recomputing a pure numeric expression is unobservable). An ineligible temp rejects the loop — statements are never skipped.

2. Constant integer keys are static write keyso[7] ≡ property "7"
static_write_key (write PIC) and the clone matcher's property extraction accept Expr::Integer via i64 formatting (canonical for every integer, including negatives). Array/typed-array receivers stay safe: the PIC miss handler and the clone preflight both require REGULAR heap objects, so real arrays fall to the generic write, which performs the element store (covered in sanity).

3. Peel outer iteration #1 before versioning — first-write appends stop vetoing the nest
A loop whose write introduces a new key (o[7] = v on {a,b,c} objects) appends that key to every receiver — a shape transition — so a preflight taken before any iteration rejected with "target key is absent from the shared shape" and the entire nest ran generically. The first outer round is now peeled through the ordinary lowering (exact source semantics; calls lower_for_after_init directly so it cannot re-enter versioning), then the guard proves the remaining [start+1, bound) rounds. Within-capacity append variant: 5 ms vs node 12 ms (0.42×, beats node).

Measured (release, default pipeline, node v26.3.0 arm64, 3 alternated runs, identical checksums)

cell before after node ratio
w8_helper_mono 47 ms 6 ms 8 0.75 — beats node
int-key within-capacity append (variant) ~160 ms class 5 ms 12 0.42 — beats node
w16_overflow_slot 2–3 ms 6 ms 23 0.26 — still beats node

Known trade: the unconditional peel costs w16 one ordinary outer round (its fallback writes are the slow ones). Reclaimable with a guard-first second-chance layout; planned together with the object-owned spill slice, which is also what the canonical w13 cell (append past inline capacity → overflow side-table) needs.

Full 18-cell matrix: no other row moved; all previous beat-node rows hold.

Semantics

New intkey_temps_sanity.ts (10 cases): numeric-key enumeration order (7 before a), negative/huge/fractional keys, helper-fn temps, any-typed array receiver (element write, length, Array.isArray), temp-reads-alias rejection fallback, typed-array receiver (Uint8Array clamping), literal-declared integer key overwrite, closure-captured temp (boxed → rejected → fallback), mutable temp rejection — byte-identical vs node, plus the three existing sanity batteries (empty-site, builder-fold, mul-index) all byte-identical on this build.

perry-hir/codegen/runtime suites green (serial protocol); gap gate results in follow-up comment.

Also refreshes the stale w3/w4/w5/w7 rows in docs/object-write-matrix.md (they beat node since #6830 but the doc still said GAP).

Refs #6812.

https://claude.ai/code/session_01QJ5mwMDPc63tNLAFPdthAG

Summary by CodeRabbit

  • Performance Improvements

    • Enhanced optimization for object-array write loops, including support for immutable numeric temporaries between aliasing and writes.
    • Improved handling of constant numeric property keys to better match fast paths.
    • Expanded and refined whole-loop optimization coverage, including peeled first-write handling and additional loop/body patterns.
  • Documentation

    • Updated the object-write performance/eligibility matrix with revised verdicts, improved ratios, and broader descriptions of what the whole-loop optimization includes.

Ralph Küpper added 3 commits July 25, 2026 21:11
… integer static write keys

w8: the call inliner leaves 'let x = r + i; let y = r - i;' between the
element alias and the writes, which broke the whole-loop clone body match.
Admit a leading run of immutable, unboxed numeric temps parsed in the same
counter-expression grammar; write values resolve LocalGet(temp) by
substitution, so the emitter and finite-range proof see inline-equivalent
trees. Ineligible temps reject the loop (never skipped).

w13: a constant integer key is the canonical numeric-string property key.
static_write_key (write PIC) and the clone matcher's property extraction
accept Expr::Integer via i64 formatting; array/typed-array receivers stay
safe (PIC miss handler and clone preflight both require REGULAR heap
objects, so they fall to the generic write).

Claude-Session: https://claude.ai/code/session_01QJ5mwMDPc63tNLAFPdthAG
…the write nest

A first-write loop appends its key to every receiver (shape transition),
so the preflight taken before any iteration rejects the whole nest and
every round runs generically. Peel round #1 through the ordinary lowering
(exact source semantics, cannot re-enter versioning), then version the
remaining [start+1, bound) rounds — the guard now sees the primed shapes
and the clone covers 99.9% of the writes. Cost when the guard would have
passed anyway: one ordinary round.

Claude-Session: https://claude.ai/code/session_01QJ5mwMDPc63tNLAFPdthAG
@proggeramlug

Copy link
Copy Markdown
Contributor Author

Follow-up evidence on the final tip:

  • Unit suites (serial protocol): perry-hir 210, perry-codegen 253, perry-runtime 1462 — 0 failures.
  • Gap gate: only the same 7 known/triaged failures as main (defineproperty_class_prototype, settracesigint, iterator_helpers, perfhooks, stream_tee_tick, v8_2, zlib_params). Gate OK — report test-parity/reports/parity_report_20260725_213337.json.
  • Sanity batteries (all byte-identical vs node v26.3.0): new intkey_temps_sanity.ts (10 cases) + empty_site_sanity.ts + builder_fold_sanity.ts + mul_index_sanity.ts.
  • 18-cell matrix: w8 47→6 ms (0.75, beats node); within-capacity int-key append variant 5 vs 12 ms (0.42, beats node); w16 6 ms (0.26, still beats — peel trade documented); no other row moved.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The object-array write codegen matcher resolves immutable numeric temps, recognizes integer property keys canonically, and peels the first outer iteration before versioning. Changelog and performance matrix documentation reflect the expanded whole-loop clone coverage and updated measurements.

Changes

Object-write codegen

Layer / File(s) Summary
Numeric write matching
crates/perry-codegen/src/expr/proxy_reflect.rs, crates/perry-codegen/src/stmt/loops.rs
Static key handling recognizes integer expressions as canonical numeric-string keys. Loop matching peels immutable numeric temps, substitutes them during expression parsing, bounds expression complexity, and applies the write-field limit after peeling.
First-iteration peeling
crates/perry-codegen/src/stmt/loops.rs
Versioned loop lowering emits the first outer iteration through the ordinary lowering path, then starts versioning at the updated outer bound.
Reported behavior
changelog.d/6812-w8-temps-w13-intkeys.md, docs/object-write-matrix.md
Changelog and triage documentation report the new shapes, eligibility routing, and revised performance measurements.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ObjectWriteLoopMatcher
  participant NumericExpressionMatcher
  participant StoreMatcher
  participant VersionedLoopLowerer
  ObjectWriteLoopMatcher->>NumericExpressionMatcher: Parse and substitute immutable numeric temps
  ObjectWriteLoopMatcher->>StoreMatcher: Match remaining writes and integer keys
  StoreMatcher->>NumericExpressionMatcher: Parse substituted write values
  VersionedLoopLowerer->>ObjectWriteLoopMatcher: Lower the peeled first outer iteration
Loading

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the work, but it omits several required template sections like Changes, Test plan, and Checklist. Reformat the PR description to include all template headings, especially Changes, Related issue, Test plan, and Checklist items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main changes: inlined-helper clone support, integer static keys, and first-iteration peel.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/6812-w8-temps-w13-intkeys

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/perry-codegen/src/stmt/loops.rs`:
- Around line 2245-2278: Cap the number of leading immutable numeric temps
peeled by the loop in the temp-collection logic before substitution can build
deeper expressions. Add a dedicated bound alongside
MAX_OBJECT_ARRAY_WRITE_FIELDS, reject the loop when that bound is exceeded, and
preserve the existing rejection behavior for boxed or unparsable temps.

In `@docs/object-write-matrix.md`:
- Line 40: Update the post-ratio value in the w3_mul_rhs row to match the
displayed post-change timings: 6 ms divided by 7 ms, approximately 0.86. Leave
the baseline ratio and surrounding benchmark data unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d35b146-536a-4e56-9884-55ed57942662

📥 Commits

Reviewing files that changed from the base of the PR and between 21a3d00 and c52397d.

📒 Files selected for processing (4)
  • changelog.d/6812-w8-temps-w13-intkeys.md
  • crates/perry-codegen/src/expr/proxy_reflect.rs
  • crates/perry-codegen/src/stmt/loops.rs
  • docs/object-write-matrix.md

Comment thread crates/perry-codegen/src/stmt/loops.rs
Comment thread docs/object-write-matrix.md Outdated
… ratio typo

Cap the temp run at 8 and every parsed tree (temps and store values) at 64
nodes, counted iteratively with early exit — chained substitution
(let b = a + a; let c = b + b) compounds tree size per level, and the
range/emit walkers recurse over these trees.

Claude-Session: https://claude.ai/code/session_01QJ5mwMDPc63tNLAFPdthAG

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/object-write-matrix.md (1)

52-52: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reconcile the w16 timing and ratio with the post-peel result.

This row reports 4163 → 3 ms and 0.26, but its own note says the peel changes 3 → 6 ms. The PR objective also reports the final w16 result as 6 ms. If the displayed 29 ms node baseline is used, the post-peel ratio is approximately 6/29 = 0.21, not 0.26; otherwise, expose the separate baseline used for the ratio.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/object-write-matrix.md` at line 52, Update the w16_overflow_slot row in
the object-write performance matrix to consistently report the post-peel 6 ms
result and its corresponding ratio, approximately 0.21 against the displayed 29
ms node baseline. If retaining 0.26, explicitly identify the separate baseline
used for that calculation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@docs/object-write-matrix.md`:
- Line 52: Update the w16_overflow_slot row in the object-write performance
matrix to consistently report the post-peel 6 ms result and its corresponding
ratio, approximately 0.21 against the displayed 29 ms node baseline. If
retaining 0.26, explicitly identify the separate baseline used for that
calculation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3f648a05-a032-4e53-b9a3-22fd6f0b2a0d

📥 Commits

Reviewing files that changed from the base of the PR and between c52397d and 0a3e431.

📒 Files selected for processing (2)
  • crates/perry-codegen/src/stmt/loops.rs
  • docs/object-write-matrix.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/perry-codegen/src/stmt/loops.rs

@proggeramlug
proggeramlug merged commit 6ff0100 into main Jul 26, 2026
30 of 31 checks passed
@proggeramlug
proggeramlug deleted the perf/6812-w8-temps-w13-intkeys branch July 26, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant