Skip to content

perf(semantic): improve SoA with multi index vec#19138

Merged
graphite-app[bot] merged 1 commit intomainfrom
boshen/multi-index-vec-scope-table
Feb 12, 2026
Merged

perf(semantic): improve SoA with multi index vec#19138
graphite-app[bot] merged 1 commit intomainfrom
boshen/multi-index-vec-scope-table

Conversation

@Boshen
Copy link
Member

@Boshen Boshen commented Feb 8, 2026

Summary

  • Introduce multi_index_vec! macro (modeled on Zig's MultiArrayList) that generates a struct-of-arrays container backed by a single allocation
  • Replace 3 separate IndexVec fields (scope_parent_ids, scope_node_ids, scope_flags) with a single ScopeTable
  • Single len/cap (u32) instead of 3× ptr+len+cap (72 bytes → 24 bytes of metadata)
  • Single capacity check on push, single reserve call for scopes
  • Follow-up: extend to symbol fields, reference SOA split, arena allocation

Ref: oxc-project/backlog#11

🤖 Generated with Claude Code

@Boshen Boshen requested a review from Dunqing as a code owner February 8, 2026 11:42
Copilot AI review requested due to automatic review settings February 8, 2026 11:42
@github-actions github-actions bot added A-semantic Area - Semantic C-cleanup Category - technical debt or refactoring. Solution not expected to change behavior labels Feb 8, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors oxc_semantic’s scope tree storage to reduce memory overhead by replacing multiple scope-indexed IndexVecs with a single struct-of-arrays container backed by one allocation.

Changes:

  • Add a multi_index_vec! macro to generate a single-allocation SoA container with typed per-field accessors.
  • Replace scope_parent_ids, scope_node_ids, and scope_flags with a unified ScopeTable in Scoping.
  • Wire the new module into the crate (mod multi_index_vec;) and update scope operations to use ScopeTable.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
crates/oxc_semantic/src/scoping.rs Replaces three IndexVec scope fields with a single ScopeTable and updates callers.
crates/oxc_semantic/src/multi_index_vec.rs Introduces the multi_index_vec! macro + generated container implementation.
crates/oxc_semantic/src/lib.rs Registers the new multi_index_vec module.

@codspeed-hq
Copy link

codspeed-hq bot commented Feb 8, 2026

Merging this PR will improve performance by 3.31%

⚡ 1 improved benchmark
✅ 46 untouched benchmarks
⏩ 3 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation transformer[binder.ts] 1.7 ms 1.6 ms +3.31%

Comparing boshen/multi-index-vec-scope-table (9f18fa3) with main (41c50a5)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Boshen Boshen force-pushed the boshen/multi-index-vec-scope-table branch from 8fad1f4 to 12bcb67 Compare February 8, 2026 14:36
Copy link
Member

@overlookmotel overlookmotel left a comment

Choose a reason for hiding this comment

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

I haven't reviewed this in full. The entire implementation being a huge macro doesn't make doing that very easy.

From a quick look, there's parts which are totally unsound because bounds checks have been removed when they shouldn't be. But, on other side, there's lots of calls to Idx::from_usize which introduce bounds checks where there don't need to be any!

The latter probably explains why the perf impact isn't as much as we could hope for from this change.

So, in my view, I think the aim of this PR is good, but this isn't the best way of going about it.

Personally, I would favour an approach which defines most of the logic in static code (a trait), and making the macro much more minimal - just sugar over that trait to provide nicely-named methods. That would make it a lot easier to review the code properly and write tests etc.

Also, Aapo from Nova has also written a crate for SoA. I've not looked at it yet, but I'd imagine it's more robust.

@Boshen
Copy link
Member Author

Boshen commented Feb 12, 2026

Pushed an update with safety/perf fixes for multi_index_vec on top of current PR head (commit 6c9337c476).

Changes in this update:

  • restore sound checked indexing in safe accessors (assert!(idx < len)),
  • remove extra Idx::from_usize checks in push/iter_ids via from_usize_unchecked under explicit capacity invariants,
  • add index-range capacity guards,
  • tighten Send/Sync bounds for field types,
  • fix generic Clone for non-Copy field types,
  • add focused tests for invalid-index panic, index-range panic, id iteration, and clone behavior.

Validation run: cargo check -p oxc_semantic, cargo test -p oxc_semantic --lib --tests, and semantic benchmark run (cargo benchmark --bench semantic --no-default-features --features compiler).

AI-assisted update: I used Codex to prepare this patch and validated the results before pushing.

@Boshen Boshen force-pushed the boshen/multi-index-vec-scope-table branch from 6c9337c to d92ee77 Compare February 12, 2026 06:40
@Boshen Boshen changed the title refactor(semantic): replace scope IndexVecs with single-allocation ScopeTable perf(semantic): improve SoA with multi index vec Feb 12, 2026
@Boshen Boshen added the 0-merge Merge with Graphite Merge Queue label Feb 12, 2026
@github-actions github-actions bot added the C-performance Category - Solution not expected to change functional behavior, only performance label Feb 12, 2026
Copy link
Member Author

Boshen commented Feb 12, 2026

Merge activity

## Summary

- Introduce `multi_index_vec!` macro (modeled on Zig's `MultiArrayList`) that generates a struct-of-arrays container backed by a single allocation
- Replace 3 separate `IndexVec` fields (`scope_parent_ids`, `scope_node_ids`, `scope_flags`) with a single `ScopeTable`
- Single `len`/`cap` (u32) instead of 3× `ptr+len+cap` (72 bytes → 24 bytes of metadata)
- Single capacity check on `push`, single `reserve` call for scopes
- Follow-up: extend to symbol fields, reference SOA split, arena allocation

Ref: oxc-project/backlog#11

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@graphite-app graphite-app bot force-pushed the boshen/multi-index-vec-scope-table branch from 9f18fa3 to 5b90d46 Compare February 12, 2026 07:24
@graphite-app graphite-app bot merged commit 5b90d46 into main Feb 12, 2026
21 checks passed
@graphite-app graphite-app bot deleted the boshen/multi-index-vec-scope-table branch February 12, 2026 07:30
@graphite-app graphite-app bot removed the 0-merge Merge with Graphite Merge Queue label Feb 12, 2026
graphite-app bot pushed a commit that referenced this pull request Feb 12, 2026
## Summary
Follow-up to #19138 after merge.

This PR fixes panic-safety in `multi_index_vec!` clone and keeps strict lint clean for `oxc_semantic` tests.

### Changes
- Make macro-generated `Clone` panic-safe by cloning row-by-row through `push`.
  - Avoids setting `len` before all elements are initialized.
  - Prevents dropping uninitialized elements if a field `clone()` panics.
- Add `#[expect(clippy::unused_self)]` to cfg-disabled integration helper methods to keep `cargo clippy -p oxc_semantic --lib --tests -D warnings` green.

## Validation
- `cargo clippy -p oxc_semantic --lib --tests -- -D warnings`
- `just lint`

## AI disclosure
AI-assisted: I used Codex to draft and apply this patch, then ran lint checks and reviewed the result before opening this PR.
Limerio pushed a commit to Limerio/oxc that referenced this pull request Feb 12, 2026
## Summary

- Introduce `multi_index_vec!` macro (modeled on Zig's `MultiArrayList`) that generates a struct-of-arrays container backed by a single allocation
- Replace 3 separate `IndexVec` fields (`scope_parent_ids`, `scope_node_ids`, `scope_flags`) with a single `ScopeTable`
- Single `len`/`cap` (u32) instead of 3× `ptr+len+cap` (72 bytes → 24 bytes of metadata)
- Single capacity check on `push`, single `reserve` call for scopes
- Follow-up: extend to symbol fields, reference SOA split, arena allocation

Ref: oxc-project/backlog#11

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Limerio pushed a commit to Limerio/oxc that referenced this pull request Feb 12, 2026
## Summary
Follow-up to oxc-project#19138 after merge.

This PR fixes panic-safety in `multi_index_vec!` clone and keeps strict lint clean for `oxc_semantic` tests.

### Changes
- Make macro-generated `Clone` panic-safe by cloning row-by-row through `push`.
  - Avoids setting `len` before all elements are initialized.
  - Prevents dropping uninitialized elements if a field `clone()` panics.
- Add `#[expect(clippy::unused_self)]` to cfg-disabled integration helper methods to keep `cargo clippy -p oxc_semantic --lib --tests -D warnings` green.

## Validation
- `cargo clippy -p oxc_semantic --lib --tests -- -D warnings`
- `just lint`

## AI disclosure
AI-assisted: I used Codex to draft and apply this patch, then ran lint checks and reviewed the result before opening this PR.
camc314 pushed a commit that referenced this pull request Feb 16, 2026
### 🚀 Features

- 429d876 semantic: Assign ast node ids during semantic build (#19263)
(Boshen)
- ebb80b3 ast: Add `node_id` field to all AST struct nodes (#18138)
(Boshen)

### 🐛 Bug Fixes

- bfb15a3 semantic: Make multi_index_vec clone panic-safe (#19299)
(Boshen)
- 41c50a5 transformer: Ignore invalid JSX pragma identifiers (#19296)
(Boshen)
- deed3d8 transformer: Remove unnecessary trailing expression in object
rest spread assignment (#19259) (Boshen)
- 5bdaacc transformer: Propagate source spans for sourcemap correctness
(#19258) (Boshen)
- 3e0e5ba isolated-declarations: Align readonly class array initializer
diagnostics with tsc (#19218) (camc314)

### ⚡ Performance

- c169c77 syntax: Optimize `is_identifier_name_patched` (#19386)
(sapphi-red)
- aa1e1a8 allocator: Inline BitSet accessors (#19331) (Boshen)
- 5b90d46 semantic: Improve SoA with multi index vec (#19138) (Boshen)
- 99ce2a6 isolated_declarations: Mark all diagnostic functions as
`#[cold]` (#19279) (camc314)
- dd0220f transformer: Remove TS-only nodes earlier in
`enter_statements` (#19166) (Dunqing)
- e5baf60 isolated-declarations: Replace hash collections with
index-based Vec (#19221) (Dunqing)

### 📚 Documentation

- 569aa61 rust: Add missing rustdocs and remove missing_docs lint attrs
(#19306) (Boshen)
OskarLebuda pushed a commit to OskarLebuda/oxc that referenced this pull request Feb 17, 2026
## Summary

- Introduce `multi_index_vec!` macro (modeled on Zig's `MultiArrayList`) that generates a struct-of-arrays container backed by a single allocation
- Replace 3 separate `IndexVec` fields (`scope_parent_ids`, `scope_node_ids`, `scope_flags`) with a single `ScopeTable`
- Single `len`/`cap` (u32) instead of 3× `ptr+len+cap` (72 bytes → 24 bytes of metadata)
- Single capacity check on `push`, single `reserve` call for scopes
- Follow-up: extend to symbol fields, reference SOA split, arena allocation

Ref: oxc-project/backlog#11

🤖 Generated with [Claude Code](https://claude.com/claude-code)
OskarLebuda pushed a commit to OskarLebuda/oxc that referenced this pull request Feb 17, 2026
## Summary
Follow-up to oxc-project#19138 after merge.

This PR fixes panic-safety in `multi_index_vec!` clone and keeps strict lint clean for `oxc_semantic` tests.

### Changes
- Make macro-generated `Clone` panic-safe by cloning row-by-row through `push`.
  - Avoids setting `len` before all elements are initialized.
  - Prevents dropping uninitialized elements if a field `clone()` panics.
- Add `#[expect(clippy::unused_self)]` to cfg-disabled integration helper methods to keep `cargo clippy -p oxc_semantic --lib --tests -D warnings` green.

## Validation
- `cargo clippy -p oxc_semantic --lib --tests -- -D warnings`
- `just lint`

## AI disclosure
AI-assisted: I used Codex to draft and apply this patch, then ran lint checks and reviewed the result before opening this PR.
OskarLebuda pushed a commit to OskarLebuda/oxc that referenced this pull request Feb 17, 2026
### 🚀 Features

- 429d876 semantic: Assign ast node ids during semantic build (oxc-project#19263)
(Boshen)
- ebb80b3 ast: Add `node_id` field to all AST struct nodes (oxc-project#18138)
(Boshen)

### 🐛 Bug Fixes

- bfb15a3 semantic: Make multi_index_vec clone panic-safe (oxc-project#19299)
(Boshen)
- 41c50a5 transformer: Ignore invalid JSX pragma identifiers (oxc-project#19296)
(Boshen)
- deed3d8 transformer: Remove unnecessary trailing expression in object
rest spread assignment (oxc-project#19259) (Boshen)
- 5bdaacc transformer: Propagate source spans for sourcemap correctness
(oxc-project#19258) (Boshen)
- 3e0e5ba isolated-declarations: Align readonly class array initializer
diagnostics with tsc (oxc-project#19218) (camc314)

### ⚡ Performance

- c169c77 syntax: Optimize `is_identifier_name_patched` (oxc-project#19386)
(sapphi-red)
- aa1e1a8 allocator: Inline BitSet accessors (oxc-project#19331) (Boshen)
- 5b90d46 semantic: Improve SoA with multi index vec (oxc-project#19138) (Boshen)
- 99ce2a6 isolated_declarations: Mark all diagnostic functions as
`#[cold]` (oxc-project#19279) (camc314)
- dd0220f transformer: Remove TS-only nodes earlier in
`enter_statements` (oxc-project#19166) (Dunqing)
- e5baf60 isolated-declarations: Replace hash collections with
index-based Vec (oxc-project#19221) (Dunqing)

### 📚 Documentation

- 569aa61 rust: Add missing rustdocs and remove missing_docs lint attrs
(oxc-project#19306) (Boshen)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-semantic Area - Semantic C-cleanup Category - technical debt or refactoring. Solution not expected to change behavior C-performance Category - Solution not expected to change functional behavior, only performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments