Skip to content

feat(bench): add kitchen-sink.tsx to TestFiles#22609

Merged
graphite-app[bot] merged 1 commit into
mainfrom
claude/bench-kitchen-sink-fixture
May 20, 2026
Merged

feat(bench): add kitchen-sink.tsx to TestFiles#22609
graphite-app[bot] merged 1 commit into
mainfrom
claude/bench-kitchen-sink-fixture

Conversation

@Dunqing

@Dunqing Dunqing commented May 20, 2026

Copy link
Copy Markdown
Member

Summary

Adds kitchen-sink.tsx — a comprehensive synthetic TypeScript+JSX fixture maintained at oxc-project/benchmark-files — to both TestFiles::minimal() (bench input set) and TestFiles::complicated() (alloc-tracking input set). The existing files in each set are untouched; this is a strict append.

Why

The existing bench input set didn't reliably surface general-purpose perf wins above the ~1-2% measurement noise floor:

The kitchen-sink fixes that by exercising every AST node, every transformer plugin, every minifier optimization opportunity, and every semantic step in one large file. Verified by re-benching #22596 against this fixture: minifier mean −1.5%, min −3.7% — above noise, signal confirmed.

Fixture stats (cross-checked locally)

Metric Value
Source size 21,117 lines / 732.90 kB
AST nodes ~133,000
Scopes ~4,750
Symbols ~7,000
Resolved references ~16,000
Semantic diagnostics 0 errors / 0 warnings

Snap baselines

tasks/track_memory_allocations/allocs_*.snap updated with the kitchen-sink row across all 5 pipelines (parser / semantic / transformer / minifier / formatter). Future PRs that change allocation behavior on this fixture will produce a snap diff in CI.

Bench-cleaner fix

tasks/benchmark/benches/lexer.rs's SourceCleaner was missing visit_ts_template_literal_type — TypeScript type-level template literals (e.g. `${T}-${U}` in conditional / mapped types) are syntactically identical to value-level template literals, so the bench-mode lexer (without parser context) cannot distinguish them. Without the cleaner converting them to plain strings, kitchen-sink's type-level templates caused the lexer bench to swallow ~1 KB spans as a single TemplateHead and produce spurious "Unterminated string" / "Invalid Unicode escape" errors. One-line fix to mirror the existing visit_template_literal handling.

AI disclosure: drafted with Claude Code, reviewed manually.

Dunqing commented May 20, 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 changed the title feat(bench): add kitchen-sink.tsx to TestFiles::minimal() feat(bench): add kitchen-sink.tsx to TestFiles May 20, 2026
@codspeed-hq

codspeed-hq Bot commented May 20, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 48 untouched benchmarks
🆕 9 new benchmarks
⏩ 3 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
🆕 Simulation linter[kitchen-sink.tsx] N/A 486.4 ms N/A
🆕 Simulation pipeline[kitchen-sink.tsx] N/A 128.4 ms N/A
🆕 Simulation codegen[kitchen-sink.tsx] N/A 23.9 ms N/A
🆕 Simulation minifier[kitchen-sink.tsx] N/A 63.3 ms N/A
🆕 Simulation mangler[kitchen-sink.tsx] N/A 3.6 ms N/A
🆕 Simulation transformer[kitchen-sink.tsx] N/A 16.2 ms N/A
🆕 Simulation parser[kitchen-sink.tsx] N/A 23.6 ms N/A
🆕 Simulation lexer[kitchen-sink.tsx] N/A 5.2 ms N/A
🆕 Simulation semantic[kitchen-sink.tsx] N/A 18.2 ms N/A

Comparing claude/bench-kitchen-sink-fixture (87dcb4f) with main (2a60012)2

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.

  2. No successful run was found on main (13579cb) during the generation of this report, so 2a60012 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 claude/bench-kitchen-sink-fixture branch 3 times, most recently from 3cce4d9 to f75b4e4 Compare May 20, 2026 10:13
@Dunqing Dunqing marked this pull request as ready for review May 20, 2026 10:27
@Dunqing Dunqing requested a review from camc314 May 20, 2026 13:23
Comment thread tasks/common/src/test_file.rs Outdated
@camc314 camc314 assigned Dunqing and unassigned camc314 May 20, 2026
@camc314

camc314 commented May 20, 2026

Copy link
Copy Markdown
Contributor

@Dunqing assigning back to you - see my note about pinning to a revision.

Rest looks good to me

@Dunqing Dunqing force-pushed the claude/bench-kitchen-sink-fixture branch from f75b4e4 to 87dcb4f Compare May 20, 2026 15:20
@camc314 camc314 added the 0-merge Merge with Graphite Merge Queue label May 20, 2026
@camc314 camc314 assigned camc314 and unassigned Dunqing May 20, 2026

camc314 commented May 20, 2026

Copy link
Copy Markdown
Contributor

Merge activity

## Summary

Adds `kitchen-sink.tsx` — a comprehensive synthetic TypeScript+JSX fixture maintained at [oxc-project/benchmark-files](https://github.com/oxc-project/benchmark-files) — to both `TestFiles::minimal()` (bench input set) and `TestFiles::complicated()` (alloc-tracking input set). The existing files in each set are untouched; this is a strict append.

## Why

The existing bench input set didn't reliably surface general-purpose perf wins above the ~1-2% measurement noise floor:
- #22580 (semantic pre-reserve) — visible because `binder.ts` exercises it
- #22594 (formatter buffer) — visible
- #22596 (minifier `try_fold_concat`) — **not visible** on the old set
- #22599 (semantic resolve-refs no-temp-Vec) — **not visible**
- #22603 (semantic var-hoist SmallVec) — **not visible**

The kitchen-sink fixes that by exercising every AST node, every transformer plugin, every minifier optimization opportunity, and every semantic step in one large file. Verified by re-benching #22596 against this fixture: **minifier mean −1.5%, min −3.7%** — above noise, signal confirmed.

## Fixture stats (cross-checked locally)

| Metric | Value |
|---|---|
| Source size | 21,117 lines / 732.90 kB |
| AST nodes | ~133,000 |
| Scopes | ~4,750 |
| Symbols | ~7,000 |
| Resolved references | ~16,000 |
| Semantic diagnostics | 0 errors / 0 warnings |

## Snap baselines

`tasks/track_memory_allocations/allocs_*.snap` updated with the kitchen-sink row across all 5 pipelines (parser / semantic / transformer / minifier / formatter). Future PRs that change allocation behavior on this fixture will produce a snap diff in CI.

## Bench-cleaner fix

`tasks/benchmark/benches/lexer.rs`'s `SourceCleaner` was missing `visit_ts_template_literal_type` — TypeScript type-level template literals (e.g. `` `${T}-${U}` `` in conditional / mapped types) are syntactically identical to value-level template literals, so the bench-mode lexer (without parser context) cannot distinguish them. Without the cleaner converting them to plain strings, kitchen-sink's type-level templates caused the lexer bench to swallow ~1 KB spans as a single `TemplateHead` and produce spurious "Unterminated string" / "Invalid Unicode escape" errors. One-line fix to mirror the existing `visit_template_literal` handling.

AI disclosure: drafted with Claude Code, reviewed manually.
@graphite-app graphite-app Bot force-pushed the claude/bench-kitchen-sink-fixture branch from 87dcb4f to a15be79 Compare May 20, 2026 15:56
@graphite-app graphite-app Bot merged commit a15be79 into main May 20, 2026
29 checks passed
@graphite-app graphite-app Bot removed the 0-merge Merge with Graphite Merge Queue label May 20, 2026
@graphite-app graphite-app Bot deleted the claude/bench-kitchen-sink-fixture branch May 20, 2026 16:02
graphite-app Bot pushed a commit that referenced this pull request May 21, 2026
## Summary

When the minifier folds `.concat()` calls into template literals via `try_fold_concat` in `peephole/replace_known_methods.rs`, the original implementation built two short-lived intermediate buffers per call:

- `quasi_strs: Vec<Cow<'a, str>>` — fresh `std::Vec` allocation per call.
- A `String` inside each `Cow::Owned` — created when `Cow::to_mut()` cloned a `Borrowed` into an `Owned`, then grown via doubling as more arg strings were pushed in.

For files with many `.concat()` calls — common in bundled ES5 libraries where template literals weren't available — both structures were freshly allocated thousands of times per minify, with the `String` growing via doubling each time.

This PR replaces those per-call buffers with a single reusable scratch `String` held on `MinifierState`, and constructs `TemplateElement` AST nodes directly into the arena `Vec` as args are drained. The intermediate `Vec<Cow<'a, str>>` is gone entirely. After the first few folds the scratch's capacity stabilizes and subsequent folds are amortized zero std-heap allocs.

The state machine simplifies in the process: the `pushed_quasi: bool` flag is gone. Its invariant ("scratch holds an in-progress quasi") is naturally maintained because every expression flush clears the scratch, so a back-to-back expression's flush produces the required empty separator quasi without a branch.

## Allocation impact

`cargo allocs`, `allocs_minifier.snap` (baseline = parent commit, after resolve-refs and var-hoist landed):

| File | Size | Sys allocs before | Sys allocs after | Sys reallocs before | Sys reallocs after |
|---|---|---|---|---|---|
| `antd.js` | 6.69 MB ES5 | **4,652** | **3,084** (`−1,568`, `−33.7%`) | **1,622** | **53** (`−1,569`) |
| Other tracked files | various | unchanged | unchanged | unchanged | unchanged |

Only `antd.js` shows a change because it's the only tracked file that exercises `.concat()` folding heavily. ES5 bundled code uses `.concat()` instead of template literals; antd.js has thousands of these.

The headline `Sys reallocs` improvement (`1,622` → `53`) comes from eliminating the per-call `String` doubling. The headline `Sys allocs` improvement (`4,652` → `3,084`) comes from eliminating the per-call `Vec<Cow<'a, str>>` allocation.

## Commits

This PR contains two commits, kept separate for review history:

1. **`perf(minifier): pre-size buffers in try_fold_concat to eliminate growth reallocs`** — the original tactical fix that pre-sized the doomed-to-be-removed intermediate buffers. Dropped sys reallocs first but didn't touch sys allocs.
2. **`perf(minifier): drop per-call buffers in try_fold_concat`** — the root-cause refactor described above. Removes the intermediate buffers entirely. Cuts both columns.

Once the second commit lands, the pre-sizing in the first becomes dead-code (the buffers it pre-sized no longer exist). They're kept as separate commits so the diff against `main` is reviewable — feel free to squash on merge.

## How I found this

After #22580, profiling on `antd.js` minifier showed it had unusually high sys reallocs vs other tracked files. I wrote a backtrace-capturing `System` allocator wrapper and ran it on the minify path. The top realloc sites all converged on `try_fold_concat` — the overwhelming majority of captured reallocs came from this one function. After landing the pre-size fix, code review pushed for a root-cause fix that also addressed the `Sys allocs` column, which this refactor delivers.

## Timing

The minifier bench suite (originally) didn't include antd.js or other `.concat()`-heavy fixtures, so bench numbers locally were flat (no regression, no obvious improvement). With kitchen-sink in the bench input set (#22609), CodSpeed shows: **minifier mean −1.5%, min −3.7%** on kitchen-sink.

## Verification

- `cargo test -p oxc_minifier` — pass.
- `cargo minsize` — no size regressions (`minsize.snap` byte-identical).
- `cargo allocs` — only `antd.js` row changed in `allocs_minifier.snap`.
- `cargo fmt -p oxc_minifier` clean.

AI disclosure: drafted with Claude Code, reviewed manually.
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.

2 participants