Skip to content

perf(mangler): compile slot sort once instead of per CAPACITY#23577

Merged
graphite-app[bot] merged 1 commit into
mainfrom
perf/mangler-sort-monomorphization
Jun 18, 2026
Merged

perf(mangler): compile slot sort once instead of per CAPACITY#23577
graphite-app[bot] merged 1 commit into
mainfrom
perf/mangler-sort-monomorphization

Conversation

@Boshen

@Boshen Boshen commented Jun 18, 2026

Copy link
Copy Markdown
Member

What

NameTable::apply is generic over const CAPACITY: usize and is instantiated twicebuild_with_semantic picks the name generator at runtime via the debug option (debug_nameInlineString<15> vs base54), so both CAPACITY instantiations are always compiled and ship in release. The sort_unstable_by_key inside apply was therefore monomorphized once per CAPACITY, even though the sort (over &SlotFrequency by .slot) is CAPACITY-independent.

This moves the sort into a non-generic sort_batch_by_slot helper, so the sort implementation is compiled once.

Why it's safe

Pure refactor — identical key and comparator, behavior unchanged. Mangler tests pass.

Impact

cargo llvm-lines --lib -p oxc_mangler (dev profile):

Lines Copies
before 30,908 1,626
after 29,297 1,594
delta −1,611 (−5.2%) −32

quicksort copies drop from 4 to 3.

Found via a cargo llvm-lines sweep of all published crates. Note: the same runtime debug flag also forces the whole CAPACITY-generic mangle pipeline to compile twice — a larger lever left for a follow-up (it touches a warmer path and wants a benchmark).

🤖 Generated with Claude Code

@codspeed-hq

codspeed-hq Bot commented Jun 18, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 52 untouched benchmarks
⏩ 19 skipped benchmarks1


Comparing perf/mangler-sort-monomorphization (2807912) with main (1b7b17b)

Open in CodSpeed

Footnotes

  1. 19 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 added the 0-merge Merge with Graphite Merge Queue label Jun 18, 2026

Boshen commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

Merge activity

## What

`NameTable::apply` is generic over `const CAPACITY: usize` and is instantiated **twice** — `build_with_semantic` picks the name generator at runtime via the `debug` option (`debug_name` → `InlineString<15>` vs `base54`), so both `CAPACITY` instantiations are always compiled and ship in release. The `sort_unstable_by_key` inside `apply` was therefore monomorphized once per `CAPACITY`, even though the sort (over `&SlotFrequency` by `.slot`) is `CAPACITY`-independent.

This moves the sort into a non-generic `sort_batch_by_slot` helper, so the sort implementation is compiled once.

## Why it's safe

Pure refactor — identical key and comparator, behavior unchanged. Mangler tests pass.

## Impact

`cargo llvm-lines --lib -p oxc_mangler` (dev profile):

| | Lines | Copies |
|---|--:|--:|
| before | 30,908 | 1,626 |
| after | 29,297 | 1,594 |
| **delta** | **−1,611 (−5.2%)** | **−32** |

`quicksort` copies drop from 4 to 3.

Found via a `cargo llvm-lines` sweep of all published crates. Note: the same runtime `debug` flag also forces the *whole* `CAPACITY`-generic mangle pipeline to compile twice — a larger lever left for a follow-up (it touches a warmer path and wants a benchmark).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@graphite-app graphite-app Bot force-pushed the perf/mangler-sort-monomorphization branch from 2807912 to cf24329 Compare June 18, 2026 01:47
@graphite-app graphite-app Bot merged commit cf24329 into main Jun 18, 2026
29 checks passed
@graphite-app graphite-app Bot removed the 0-merge Merge with Graphite Merge Queue label Jun 18, 2026
@graphite-app graphite-app Bot deleted the perf/mangler-sort-monomorphization branch June 18, 2026 01:51
Boshen added a commit that referenced this pull request Jun 18, 2026
### 💥 BREAKING CHANGES

- 7a76cd3 estree: [**BREAKING**] Make whether to include TS fields a
runtime option (#23574) (overlookmotel)
- e7b6b68 estree: [**BREAKING**] `ESTree` config use methods not consts
(#23573) (overlookmotel)

### 🚀 Features

- 556cc6d data_structures: Add `CodeBuffer::as_str` method (#23571)
(overlookmotel)
- 38c4b06 parser: Add friendly error for adjacent JSX elements (#23378)
(sapphi-red)
- 53509a8 minifier: Treeshake pure typed arrays and Set/Map array
literals (#23469) (Dunqing)
- 09762d9 minifier: Inline const value for read-only vars (#22593)
(Dunqing)

### 🐛 Bug Fixes

- 20375f9 react_compiler: Keep imports referenced only by a computed key
(#23586) (Boshen)
- 31bfd9b minifier: Keep Object introspection calls on a possible Proxy
(#23483) (Dunqing)
- 837a395 parser: Treat a line comment after ':' as leading, not
trailing (#23515) (Dunqing)
- e409fe0 minifier: Keep `new Map`/`WeakSet`/`WeakMap` with a string
argument (#23470) (Dunqing)
- ae02b4e ci/parser: Use `minimal` for vitest reporter (#23457)
(camc314)

### ⚡ Performance

- cf24329 mangler: Compile slot sort once instead of per CAPACITY
(#23577) (Boshen)
- 4058a6a parser: Reduce code bloat from verify_modifiers
monomorphization (#23576) (Boshen)
- 053b0c1 estree: Remove pointless `mem::take` (#23572) (overlookmotel)
- dfb52b6 transformer: Pre-size statement vecs in TS enum & namespace
lowering (#23516) (Yunfei He)
- 970e09a minifier: Compute template-literal inline checks in a single
pass (#23467) (Yunfei He)
- 3170c0e semantic,mangler,minifier: Fix `Semantic::stats` node count
and reuse stats in mangler builds (#23352) (Boshen)
- d1fa6e0 minifier: Evaluate ternary branches once in
minimize_conditional_expression (#23479) (Yunfei He)
- 3fa8051 transformer: Pre-size JSX props vec to attribute count
(#23466) (Yunfei He)
- 488b382 react_compiler: Borrow binding names in prefilter instead of
allocating (#23471) (Yunfei He)
- bcb3894 minifier: Incremental scoping refresh, delete
LiveUsageCollector (#23197) (Dunqing)

### 📚 Documentation

- f68641e data_structures: Improve docs on safety contract (#23575)
(overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
camc314 pushed a commit that referenced this pull request Jul 3, 2026
## What

`NameTable::apply` is generic over `const CAPACITY: usize` and is instantiated **twice** — `build_with_semantic` picks the name generator at runtime via the `debug` option (`debug_name` → `InlineString<15>` vs `base54`), so both `CAPACITY` instantiations are always compiled and ship in release. The `sort_unstable_by_key` inside `apply` was therefore monomorphized once per `CAPACITY`, even though the sort (over `&SlotFrequency` by `.slot`) is `CAPACITY`-independent.

This moves the sort into a non-generic `sort_batch_by_slot` helper, so the sort implementation is compiled once.

## Why it's safe

Pure refactor — identical key and comparator, behavior unchanged. Mangler tests pass.

## Impact

`cargo llvm-lines --lib -p oxc_mangler` (dev profile):

| | Lines | Copies |
|---|--:|--:|
| before | 30,908 | 1,626 |
| after | 29,297 | 1,594 |
| **delta** | **−1,611 (−5.2%)** | **−32** |

`quicksort` copies drop from 4 to 3.

Found via a `cargo llvm-lines` sweep of all published crates. Note: the same runtime `debug` flag also forces the *whole* `CAPACITY`-generic mangle pipeline to compile twice — a larger lever left for a follow-up (it touches a warmer path and wants a benchmark).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
camc314 pushed a commit that referenced this pull request Jul 3, 2026
### 💥 BREAKING CHANGES

- 7a76cd3 estree: [**BREAKING**] Make whether to include TS fields a
runtime option (#23574) (overlookmotel)
- e7b6b68 estree: [**BREAKING**] `ESTree` config use methods not consts
(#23573) (overlookmotel)

### 🚀 Features

- 556cc6d data_structures: Add `CodeBuffer::as_str` method (#23571)
(overlookmotel)
- 38c4b06 parser: Add friendly error for adjacent JSX elements (#23378)
(sapphi-red)
- 53509a8 minifier: Treeshake pure typed arrays and Set/Map array
literals (#23469) (Dunqing)
- 09762d9 minifier: Inline const value for read-only vars (#22593)
(Dunqing)

### 🐛 Bug Fixes

- 20375f9 react_compiler: Keep imports referenced only by a computed key
(#23586) (Boshen)
- 31bfd9b minifier: Keep Object introspection calls on a possible Proxy
(#23483) (Dunqing)
- 837a395 parser: Treat a line comment after ':' as leading, not
trailing (#23515) (Dunqing)
- e409fe0 minifier: Keep `new Map`/`WeakSet`/`WeakMap` with a string
argument (#23470) (Dunqing)
- ae02b4e ci/parser: Use `minimal` for vitest reporter (#23457)
(camc314)

### ⚡ Performance

- cf24329 mangler: Compile slot sort once instead of per CAPACITY
(#23577) (Boshen)
- 4058a6a parser: Reduce code bloat from verify_modifiers
monomorphization (#23576) (Boshen)
- 053b0c1 estree: Remove pointless `mem::take` (#23572) (overlookmotel)
- dfb52b6 transformer: Pre-size statement vecs in TS enum & namespace
lowering (#23516) (Yunfei He)
- 970e09a minifier: Compute template-literal inline checks in a single
pass (#23467) (Yunfei He)
- 3170c0e semantic,mangler,minifier: Fix `Semantic::stats` node count
and reuse stats in mangler builds (#23352) (Boshen)
- d1fa6e0 minifier: Evaluate ternary branches once in
minimize_conditional_expression (#23479) (Yunfei He)
- 3fa8051 transformer: Pre-size JSX props vec to attribute count
(#23466) (Yunfei He)
- 488b382 react_compiler: Borrow binding names in prefilter instead of
allocating (#23471) (Yunfei He)
- bcb3894 minifier: Incremental scoping refresh, delete
LiveUsageCollector (#23197) (Dunqing)

### 📚 Documentation

- f68641e data_structures: Improve docs on safety contract (#23575)
(overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
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