perf(minifier): index SymbolValues by SymbolId#22441
Conversation
Merging this PR will improve performance by 3.48%
|
| 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
Footnotes
-
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. ↩
-
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. ↩
b966025 to
0de54d7
Compare
0de54d7 to
e3c8e36
Compare
e49ee26 to
b50d3dd
Compare
How to use the Graphite Merge QueueAdd either label to this PR to merge it via 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. |
b50d3dd to
a1e2e4b
Compare
f9ff40e to
0e6f5ad
Compare
b173038 to
bd340ff
Compare
a1e2e4b to
5eb1e9d
Compare
55ae955 to
a170a82
Compare
SymbolId in CompressorSymbolValues by SymbolId
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.
5eb1e9d to
d782b78
Compare
a170a82 to
217d7d8
Compare
### 🐛 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)

Stacked on top of #22425.
Summary
Swap
SymbolValues's backing store fromFxHashMap<SymbolId, SymbolValue>toIndexVec<SymbolId, Option<SymbolValue>>. Symbol IDs are denseu32s, so indexed access drops the hash + probe on every hot path —inline_identifier_reference,is_symbol_mutated, and the dead-code checks inremove_unused_expression/minimize_statements.The buffer is sized once from
Scoping::symbols_len()atMinifierState::newand reset in place between peephole iterations, so the whole fixed-point loop stays on the indexed-write fast path. No minifier pass currently mintsSymbolIds mid-run (verified acrosspeephole/,normalize/, the mangler, andkeep_var.rs— onlytraverse_context/definesgenerate_uid_name/create_symbol, with zero callers), soinit_valueis 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_scopingagainstTestFiles::minimal()(RadixUIAdoptionSection.jsx,react.development.js,cal.com.tsx,binder.ts), 27 paired cycles in randomized order after warm-up:Paired delta: this PR wins 27 / 27 cycles, median −207 µs (−2.97 %), sign-test p < 0.000001.
cargo test -p oxc_minifier --testspasses 494/494; allocation snapshot shows reduced sys allocs on every file.