Skip to content

perf(interp): fuse arithmetic-to-local stores and array loads - #167

Merged
siyul-park merged 2 commits into
mainfrom
codex/perf-generic-threaded-dispatch
Jul 30, 2026
Merged

perf(interp): fuse arithmetic-to-local stores and array loads#167
siyul-park merged 2 commits into
mainfrom
codex/perf-generic-threaded-dispatch

Conversation

@siyul-park

@siyul-park siyul-park commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a feature-free fast path in Interpreter.dispatch that skips tick/hook/gas/profiler bookkeeping and runs threaded closures back to back when none of those features are configured.
  • Extend the geninterp fusion catalog: non-trapping scalar binary results stored directly into typed locals (no stack materialization), and typed-array constants indexed by scalar producers feeding array.get (still guarded by a runtime type/bounds check).
  • Add reset-time reference-array header reuse: Reset pools released *types.Array headers (with Typ/Elems cleared) capped by the run's dynamic heap size; newArray draws from that pool before allocating fresh. Arrays detached via Pop or reclaimed pre-reset are excluded.
  • Update docs/fusion.md, docs/instruction-set.md, docs/memory-model.md, and docs/benchmarks.md with the new fusion patterns, the array-header-reuse invariant, and re-measured benchmarks (issue perf(interp): beat every scripting VM in threaded mode on the three lagging kernels #164).

Testing

  • make check-generated — generated interp/threaded.go matches the generator output.
  • make lint / make vet — clean.
  • go test -race -count=1 ./interp/... ./internal/cmd/geninterp/... — pass, including new tests for local-arithmetic fusion, typed-array-load fusion, and array-header pooling/detachment semantics.
  • make check-arm64 (GOOS=linux GOARCH=arm64 build + test) — pass.
  • go test -race ./... — pass across the full module.

Summary by CodeRabbit

  • Performance

    • Expanded threaded fast paths for numeric operations, comparisons/branches, and array access.
    • Reduced runtime allocations by reusing array header storage across interpreter resets.
    • Refreshed benchmark methodology and reported results.
  • Documentation

    • Updated fusion and instruction-set guidance, including typed-array validation and trapping arithmetic behavior.
    • Added clarification on reset-time array header reuse.
  • Bug Fixes

    • Improved correctness for optimized typed-array reads (type + bounds validation) and related fusion paths.
  • Tests

    • Extended interpreter and reset coverage, plus added new fusion/oracle parity cases.

Add a feature-free fast path in Interpreter.dispatch that skips the
tick/hook/gas/profiler bookkeeping loop entirely when none of those
features are configured, running threaded closures back to back.

Extend the geninterp fusion catalog with two new families:
- non-trapping scalar binary results stored directly into typed
  locals, skipping stack materialization
- typed-array constants indexed by scalar producers feeding
  array.get, validated against the current heap value's concrete
  type and bounds on every execution

Add reset-time reference-array header reuse: Reset keeps released
*types.Array headers (Typ/Elems cleared) in an interpreter-local pool
capped by the run's dynamic heap size, and newArray draws from that
pool before allocating. Arrays detached through Pop or reclaimed
before reset are not pooled.

Update docs/fusion.md, docs/instruction-set.md, docs/memory-model.md,
and docs/benchmarks.md to describe the new fusion patterns, the array
header reuse invariant, and re-measured benchmark numbers (issue #164).
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c80ae60a-9321-4f7b-bdc9-4b90675ad542

📥 Commits

Reviewing files that changed from the base of the PR and between 0b90b1f and baa6b4e.

📒 Files selected for processing (2)
  • interp/interp.go
  • interp/interp_test.go

📝 Walkthrough

Walkthrough

The interpreter now generates direct local arithmetic stores and typed-array access paths, expands threaded numeric execution handlers, reuses array headers across resets, and adds corresponding tests and documentation updates.

Changes

Runtime execution changes

Layer / File(s) Summary
Fusion patterns and lowering
internal/cmd/geninterp/*, docs/fusion.md, docs/instruction-set.md
Fusion generation and lowering now support direct non-trapping numeric stores to locals and typed-array constant indexing with type and bounds checks.
Threaded numeric execution
interp/interp.go, interp/threaded.go
The dispatcher adds a coordination-free fast path, while threaded execution expands inline arithmetic, comparison, branching, integer, and floating-point handlers.
Array header reuse
interp/interp.go, interp/threaded.go, interp/interp_test.go, docs/memory-model.md
Reset pools eligible array headers, newArray restores pooled headers, and array construction paths use the helper; tests cover reset and array ownership behavior.
Runtime validation and benchmark documentation
interp/interp_test.go, internal/cmd/geninterp/generate_test.go, docs/benchmarks.md
Tests add local-store parity, generated-code checks, reset coverage, and exact hook-count cases; benchmark methodology and minivm results are updated.

Estimated code review effort: 5 (Critical) | ~100 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant PatternGenerator
  participant CodeGenerator
  participant Interpreter
  PatternGenerator->>CodeGenerator: produce local-store and typed-array patterns
  CodeGenerator->>Interpreter: emit fused handlers
  Interpreter->>Interpreter: validate operands and update locals or array results
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fusing arithmetic-to-local stores and array loads in the interpreter.
Description check ✅ Passed Core changes and testing are covered, but the template's Related Issues and Additional Information sections are missing.
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 codex/perf-generic-threaded-dispatch

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.

@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.28571% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 31.20%. Comparing base (10d6adf) to head (baa6b4e).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
internal/cmd/geninterp/lower.go 85.82% 11 Missing and 7 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #167      +/-   ##
==========================================
- Coverage   32.25%   31.20%   -1.06%     
==========================================
  Files          85       85              
  Lines       56013    59612    +3599     
==========================================
+ Hits        18069    18601     +532     
- Misses      36807    39846    +3039     
- Partials     1137     1165      +28     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@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 `@docs/benchmarks.md`:
- Around line 24-27: Correct the measurement-date statement in the benchmark
documentation: replace the future-dated July 31, 2026 minivm re-measurement
claim with the actual completed date, or remove/defer that claim until the
measurements have been performed. Preserve the surrounding sample and commit
details.

In `@docs/memory-model.md`:
- Around line 129-137: Update the Reset description to say it invalidates every
live dynamic object still owned by the interpreter, rather than every live
dynamic object. Keep the existing Pop exception and pooling behavior 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: 5279de4a-3cc9-447a-b915-70409f727653

📥 Commits

Reviewing files that changed from the base of the PR and between 10d6adf and 0b90b1f.

📒 Files selected for processing (10)
  • docs/benchmarks.md
  • docs/fusion.md
  • docs/instruction-set.md
  • docs/memory-model.md
  • internal/cmd/geninterp/generate_test.go
  • internal/cmd/geninterp/lower.go
  • internal/cmd/geninterp/pattern.go
  • interp/interp.go
  • interp/interp_test.go
  • interp/threaded.go
👮 Files not reviewed due to content moderation or server errors (1)
  • interp/threaded.go

Comment thread docs/benchmarks.md
Comment on lines +24 to +27
The external-runtime rows were measured on July 30, 2026 with three sequential
samples. Every minivm row was re-measured on July 31, 2026 from five
interleaved baseline/current pairs against commit `10d6adf`. The public API
cost tables below were measured on July 16, 2026:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the future-dated measurement claim.

As of July 30, 2026, the document cannot truthfully state that minivm was re-measured on July 31, 2026. Replace it with the actual completed measurement date, or defer this documentation update until those measurements exist.

🧰 Tools
🪛 LanguageTool

[grammar] ~25-~25: Ensure spelling is correct
Context: ...26 with three sequential samples. Every minivm row was re-measured on July 31, 2026 fr...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 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/benchmarks.md` around lines 24 - 27, Correct the measurement-date
statement in the benchmark documentation: replace the future-dated July 31, 2026
minivm re-measurement claim with the actual completed date, or remove/defer that
claim until the measurements have been performed. Preserve the surrounding
sample and commit details.

Comment thread docs/memory-model.md
Comment on lines +129 to +137
`Reset` invalidates every live dynamic object. Before clearing those slots, the
interpreter keeps released `*types.Array` headers in an interpreter-local pool
capped by that run's dynamic heap size. VM array construction, slicing, and map
key extraction may reuse those headers on the next run.

Only the Go object header is reused. `Typ` and `Elems` are cleared at reset, and
every new array receives a fresh element backing store, so zeroing and retained
memory behavior stay unchanged. Arrays detached through `Pop` or reclaimed
before reset are not pooled; their returned Go values remain intact. `Close`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Clarify Reset’s ownership scope.

“Every live dynamic object” contradicts the following Pop exception. Limit this to dynamic objects still owned by the interpreter.

Proposed fix
-`Reset` invalidates every live dynamic object. Before clearing those slots, the
+`Reset` invalidates every dynamic object still owned by the interpreter. Before clearing those slots, the
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`Reset` invalidates every live dynamic object. Before clearing those slots, the
interpreter keeps released `*types.Array` headers in an interpreter-local pool
capped by that run's dynamic heap size. VM array construction, slicing, and map
key extraction may reuse those headers on the next run.
Only the Go object header is reused. `Typ` and `Elems` are cleared at reset, and
every new array receives a fresh element backing store, so zeroing and retained
memory behavior stay unchanged. Arrays detached through `Pop` or reclaimed
before reset are not pooled; their returned Go values remain intact. `Close`
`Reset` invalidates every dynamic object still owned by the interpreter. Before clearing those slots, the
interpreter keeps released `*types.Array` headers in an interpreter-local pool
capped by that run's dynamic heap size. VM array construction, slicing, and map
key extraction may reuse those headers on the next run.
Only the Go object header is reused. `Typ` and `Elems` are cleared at reset, and
every new array receives a fresh element backing store, so zeroing and retained
memory behavior stay unchanged. Arrays detached through `Pop` or reclaimed
before reset are not pooled; their returned Go values remain intact. `Close`
🤖 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/memory-model.md` around lines 129 - 137, Update the Reset description to
say it invalidates every live dynamic object still owned by the interpreter,
rather than every live dynamic object. Keep the existing Pop exception and
pooling behavior unchanged.

@siyul-park
siyul-park merged commit 0dc7e72 into main Jul 30, 2026
5 of 6 checks passed
@siyul-park
siyul-park deleted the codex/perf-generic-threaded-dispatch branch July 30, 2026 22:52
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