perf(transformer/jsx): pre-allocate enough memory for arguments of createElement/jsx function#9915
Merged
Conversation
Member
Author
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. |
CodSpeed Instrumentation Performance ReportMerging #9915 will not alter performanceComparing Summary
|
Member
Author
Member
Merge activity
|
…`createElement`/`jsx` function (#9915) This is a hot path for JSX transform, allocating sufficient memory can avoid reserving in `push`, every bit of optimization that adds up can be huge.
831c8f2 to
374050a
Compare
camc314
added a commit
that referenced
this pull request
Jun 16, 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
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>
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.


This is a hot path for JSX transform, allocating sufficient memory can avoid reserving in
push, every bit of optimization that adds up can be huge.