perf(transformer): pre-size JSX props vec to attribute count#23466
Merged
camc314 merged 2 commits intoJun 16, 2026
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
Dunqing
approved these changes
Jun 16, 2026
camc314
reviewed
Jun 16, 2026
camc314
reviewed
Jun 16, 2026
camc314
pushed a commit
that referenced
this pull request
Jun 16, 2026
…ering (#23516) ## What Two zero-capacity arena `Vec`s in the TypeScript transformer were filled in known-length loops and grew via reallocation. Pre-size them — mirroring the JSX-props pre-size in #23466: - **`transform_ts_enum_members`**: emits exactly `members.len() + 1` statements (one per member + the final `return`) → `vec_with_capacity(members.len() + 1)` (an **exact** capacity). - **TS namespace `enter_program`**: rebuilds the program body; pre-size to `program.body.len()` (an estimate — most statements pass through 1:1). ## Δ — fewer reallocations on TS fixtures `allocs_transformer.snap`, reallocation count: | fixture | before | after | | --- | --- | --- | | checker.ts | 64 | **5** | | kitchen-sink.tsx | 304 | **280** | | App.tsx | 25 | **17** | | binder.ts | 6 | **0** | (allocation **count** and **bytes** are unchanged — this removes the intermediate growth reallocations.) ## Why it is behavior-preserving Capacity-only: `vec_with_capacity` differs from `vec` solely in initial capacity; element type, push order, and contents are identical, so the produced AST is byte-identical. `oxc_transformer` tests pass. Prepared with AI assistance.
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
added a commit
that referenced
this pull request
Jul 3, 2026
## Memory reduction: fewer arena reallocations on JSX `transform_jsx` built the `React.createElement` / jsx props object in a **zero-capacity** arena vec (`ctx.ast.vec()`), regrowing it (4 → 8 → 16 …) as it pushed one property per attribute. The attribute count is already known, so pre-sizing the vec to it removes those regrowths — mirroring `arguments`, pre-sized a few lines above, and the same pattern as #9915. Measured with `cargo allocs` (`TestFiles::complicated`). **Arena reallocations drop on every JSX file; nothing else changes** (system allocs and the arena-alloc count are essentially flat): | file | arena reallocs: before → after | **reduction** | | --- | --- | --- | | App.tsx | 77 → 25 | **−52 (−68%)** | | kitchen-sink.tsx | 517 → 304 | **−213 (−41%)** | | RadixUIAdoptionSection.jsx | 24 → 13 | **−11 (−46%)** | Each eliminated realloc is a buffer grow + memcpy of the existing elements, so this is real work removed on the JSX hot path, not just bytes. <details><summary>Raw <code>allocs_transformer.snap</code> diff (updated in this PR)</summary> ```diff -App.tsx | 415.34 kB || 25 | 2 || 719 | 77 +App.tsx | 415.34 kB || 25 | 2 || 719 | 25 -RadixUIAdoptionSection.jsx | 2.52 kB || 21 | 2 || 259 | 24 +RadixUIAdoptionSection.jsx | 2.52 kB || 21 | 2 || 259 | 13 -kitchen-sink.tsx | 732.90 kB || 184 | 3 || 6689 | 517 +kitchen-sink.tsx | 732.90 kB || 184 | 3 || 6690 | 304 ``` (Non-JSX files unchanged. The lone `+1` arena alloc on kitchen-sink is an eager 1-slot allocation for a rare element whose attributes are all filtered out — negligible against −213 reallocs.) </details> ## Change ```rust let mut properties = ctx.ast.vec_with_capacity(opening_element.as_ref().map_or(0, |e| e.attributes.len())); ``` Capacity-only: the vec's contents, push order, and emitted output are unchanged — only the initial allocation differs. CodSpeed (instructions) may pick up the saved memcpys on JSX-heavy input, but this is framed as a memory/allocation win. ## Behavior-preserving `cargo test -p oxc_transformer` passes. The change is a capacity hint and cannot alter the produced AST. --- Prepared with AI assistance. Co-authored-by: Cameron <cameron.clark@hey.com>
camc314
pushed a commit
that referenced
this pull request
Jul 3, 2026
…ering (#23516) ## What Two zero-capacity arena `Vec`s in the TypeScript transformer were filled in known-length loops and grew via reallocation. Pre-size them — mirroring the JSX-props pre-size in #23466: - **`transform_ts_enum_members`**: emits exactly `members.len() + 1` statements (one per member + the final `return`) → `vec_with_capacity(members.len() + 1)` (an **exact** capacity). - **TS namespace `enter_program`**: rebuilds the program body; pre-size to `program.body.len()` (an estimate — most statements pass through 1:1). ## Δ — fewer reallocations on TS fixtures `allocs_transformer.snap`, reallocation count: | fixture | before | after | | --- | --- | --- | | checker.ts | 64 | **5** | | kitchen-sink.tsx | 304 | **280** | | App.tsx | 25 | **17** | | binder.ts | 6 | **0** | (allocation **count** and **bytes** are unchanged — this removes the intermediate growth reallocations.) ## Why it is behavior-preserving Capacity-only: `vec_with_capacity` differs from `vec` solely in initial capacity; element type, push order, and contents are identical, so the produced AST is byte-identical. `oxc_transformer` tests pass. Prepared with AI assistance.
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Memory reduction: fewer arena reallocations on JSX
transform_jsxbuilt theReact.createElement/ jsx props object in a zero-capacity arena vec(
ctx.ast.vec()), regrowing it (4 → 8 → 16 …) as it pushed one property per attribute. The attributecount is already known, so pre-sizing the vec to it removes those regrowths — mirroring
arguments,pre-sized a few lines above, and the same pattern as #9915.
Measured with
cargo allocs(TestFiles::complicated). Arena reallocations drop on every JSX file;nothing else changes (system allocs and the arena-alloc count are essentially flat):
Each eliminated realloc is a buffer grow + memcpy of the existing elements, so this is real
work removed on the JSX hot path, not just bytes.
Raw
allocs_transformer.snapdiff (updated in this PR)(Non-JSX files unchanged. The lone
+1arena alloc on kitchen-sink is an eager 1-slot allocation fora rare element whose attributes are all filtered out — negligible against −213 reallocs.)
Change
Capacity-only: the vec's contents, push order, and emitted output are unchanged — only the initial
allocation differs. CodSpeed (instructions) may pick up the saved memcpys on JSX-heavy input, but this
is framed as a memory/allocation win.
Behavior-preserving
cargo test -p oxc_transformerpasses. The change is a capacity hint and cannot alter the produced AST.Prepared with AI assistance.