Skip to content

perf(minifier): index SymbolValues by SymbolId#22441

Merged
graphite-app[bot] merged 1 commit into
mainfrom
perf/minifier-symbol-values
May 15, 2026
Merged

perf(minifier): index SymbolValues by SymbolId#22441
graphite-app[bot] merged 1 commit into
mainfrom
perf/minifier-symbol-values

Conversation

@Dunqing

@Dunqing Dunqing commented May 15, 2026

Copy link
Copy Markdown
Member

Stacked on top of #22425.

Summary

Swap SymbolValues's backing store from FxHashMap<SymbolId, SymbolValue> to IndexVec<SymbolId, Option<SymbolValue>>. Symbol IDs are dense u32s, so indexed access drops the hash + probe on every hot path — inline_identifier_reference, is_symbol_mutated, and the dead-code checks in remove_unused_expression / minimize_statements.

The buffer is sized once from Scoping::symbols_len() at MinifierState::new and reset in place between peephole iterations, so the whole fixed-point loop stays on the indexed-write fast path. No minifier pass currently mints SymbolIds mid-run (verified across peephole/, normalize/, the mangler, and keep_var.rs — only traverse_context/ defines generate_uid_name / create_symbol, with zero callers), so init_value is a single indexed write. If a future pass starts minting UIDs, that write panics on out-of-range — the signal to add a grow path.

Local harness running Compressor::build_with_scoping against TestFiles::minimal() (RadixUIAdoptionSection.jsx, react.development.js, cal.com.tsx, binder.ts), 27 paired cycles in randomized order after warm-up:

binary min median p75 std
bitset base (#22425) 6 905 125 6 958 167 6 975 501 37 944
this PR on top 6 693 875 6 750 875 6 774 626 40 482

Paired delta: this PR wins 27 / 27 cycles, median −207 µs (−2.97 %), sign-test p < 0.000001. cargo test -p oxc_minifier --tests passes 494/494; allocation snapshot shows reduced sys allocs on every file.

@github-actions github-actions Bot added the A-minifier Area - Minifier label May 15, 2026
@codspeed-hq

codspeed-hq Bot commented May 15, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 3.48%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 2 improved benchmarks
✅ 42 untouched benchmarks
⏩ 7 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation minifier[react.development.js] 2.5 ms 2.4 ms +3.7%
Simulation minifier[binder.ts] 3.7 ms 3.6 ms +3.26%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing perf/minifier-symbol-values (a170a82) with perf/minifier-live-usage-bitset (a1e2e4b)2

Open in CodSpeed

Footnotes

  1. 7 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.

  2. No successful run was found on perf/minifier-live-usage-bitset (5eb1e9d) during the generation of this report, so 0dd49a2 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@Dunqing Dunqing force-pushed the perf/minifier-symbol-values branch from b966025 to 0de54d7 Compare May 15, 2026 02:39
@Dunqing Dunqing changed the base branch from main to graphite-base/22441 May 15, 2026 02:45
@Dunqing Dunqing force-pushed the perf/minifier-symbol-values branch from 0de54d7 to e3c8e36 Compare May 15, 2026 02:45
@Dunqing Dunqing force-pushed the graphite-base/22441 branch from e49ee26 to b50d3dd Compare May 15, 2026 02:45
@Dunqing Dunqing changed the base branch from graphite-base/22441 to perf/minifier-live-usage-bitset May 15, 2026 02:45

Dunqing commented May 15, 2026

Copy link
Copy Markdown
Member Author

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@Dunqing Dunqing force-pushed the perf/minifier-live-usage-bitset branch from b50d3dd to a1e2e4b Compare May 15, 2026 02:57
@Dunqing Dunqing force-pushed the perf/minifier-symbol-values branch 2 times, most recently from f9ff40e to 0e6f5ad Compare May 15, 2026 03:08
@github-actions github-actions Bot added A-parser Area - Parser A-allocator Area - Allocator labels May 15, 2026
@Dunqing Dunqing force-pushed the perf/minifier-symbol-values branch 3 times, most recently from b173038 to bd340ff Compare May 15, 2026 06:38
@Dunqing Dunqing force-pushed the perf/minifier-live-usage-bitset branch from a1e2e4b to 5eb1e9d Compare May 15, 2026 14:25
@Dunqing Dunqing force-pushed the perf/minifier-symbol-values branch from 55ae955 to a170a82 Compare May 15, 2026 14:25
@Dunqing Dunqing changed the title perf(minifier): index per-symbol state by SymbolId in Compressor perf(minifier): index SymbolValues by SymbolId May 15, 2026
@Dunqing Dunqing marked this pull request as ready for review May 15, 2026 14:53
@Dunqing Dunqing added the 0-merge Merge with Graphite Merge Queue label May 15, 2026

Dunqing commented May 15, 2026

Copy link
Copy Markdown
Member Author

Merge activity

Stacked on top of #22425.

## Summary

Swap `SymbolValues`'s backing store from `FxHashMap<SymbolId, SymbolValue>` to `IndexVec<SymbolId, Option<SymbolValue>>`. Symbol IDs are dense `u32`s, so indexed access drops the hash + probe on every hot path — `inline_identifier_reference`, `is_symbol_mutated`, and the dead-code checks in `remove_unused_expression` / `minimize_statements`.

The buffer is sized once from `Scoping::symbols_len()` at `MinifierState::new` and reset in place between peephole iterations, so the whole fixed-point loop stays on the indexed-write fast path. No minifier pass currently mints `SymbolId`s mid-run (verified across `peephole/`, `normalize/`, the mangler, and `keep_var.rs` — only `traverse_context/` defines `generate_uid_name` / `create_symbol`, with zero callers), so `init_value` is a single indexed write. If a future pass starts minting UIDs, that write panics on out-of-range — the signal to add a grow path.

Local harness running `Compressor::build_with_scoping` against `TestFiles::minimal()` (`RadixUIAdoptionSection.jsx`, `react.development.js`, `cal.com.tsx`, `binder.ts`), 27 paired cycles in randomized order after warm-up:

| binary                | min       | median    | p75       | std      |
|-----------------------|----------:|----------:|----------:|---------:|
| bitset base (#22425)  | 6 905 125 | 6 958 167 | 6 975 501 |   37 944 |
| this PR on top        | 6 693 875 | 6 750 875 | 6 774 626 |   40 482 |

Paired delta: this PR wins **27 / 27** cycles, median **−207 µs (−2.97 %)**, sign-test p < 0.000001. `cargo test -p oxc_minifier --tests` passes 494/494; allocation snapshot shows reduced sys allocs on every file.
@graphite-app graphite-app Bot force-pushed the perf/minifier-live-usage-bitset branch from 5eb1e9d to d782b78 Compare May 15, 2026 14:54
@graphite-app graphite-app Bot force-pushed the perf/minifier-symbol-values branch from a170a82 to 217d7d8 Compare May 15, 2026 14:55
Base automatically changed from perf/minifier-live-usage-bitset to main May 15, 2026 14:59
@graphite-app graphite-app Bot removed the 0-merge Merge with Graphite Merge Queue label May 15, 2026
@graphite-app graphite-app Bot merged commit 217d7d8 into main May 15, 2026
30 checks passed
@graphite-app graphite-app Bot deleted the perf/minifier-symbol-values branch May 15, 2026 15:00
camc314 pushed a commit that referenced this pull request May 18, 2026
### 🐛 Bug Fixes

- 0f26de6 ecmascript: Resolve identifier value type via tracked
constants (#22234) (Alexander Lichter)
- c27a8cf minifier: Normalize `{ x: x }` shorthand so adjacent-if merge
is idempotent (#22401) (Dunqing)
- e431a0e parser: Break extends clause loop on fatal error (#22517)
(Boshen)
- e9ec7c6 minifier: Fold optional chains by base nullishness (#22236)
(Alexander Lichter)
- e6090e7 transformer: Keep enum IIFE when a non-inlinable value
reference remains (#22501) (Dunqing)
- 931b7d6 transformer: Inline const enum members through type-cast
wrappers (#22500) (Dunqing)
- b9615b2 codegen: Preserve string quotes in require() calls during
minification (#22475) (zennnnnnn11)
- c73c159 transformer/async-to-generator: Reparent parameter initializer
scopes (#22507) (camc314)
- ecfd3ca transformer/async-to-generator: Move only parameter bindings
(#22503) (camc314)
- 3ce3431 transformer/explicit-resource-managment: Preserve shadowed
for-head block (#22451) (camc314)

### ⚡ Performance

- ce92c6c semantic: `#[inline]` `Scoping::get_binding` (#22414)
(Dunqing)
- 98be95c regular_expression: Track regex flags via bitflags (#22427)
(Boshen)
- dbbc059 jsdoc: Skip should_attach_jsdoc when no remaining comments
(#22409) (Boshen)
- 217d7d8 minifier: Index `SymbolValues` by `SymbolId` (#22441)
(Dunqing)
- d782b78 minifier: Use BitSet for LiveUsageCollector live references
(#22425) (Boshen)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-allocator Area - Allocator A-minifier Area - Minifier A-parser Area - Parser

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant