perf(formatter_core): stage IR buffers on the heap to reduce arena memory#24582
Conversation
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. |
Merging this PR will improve performance by 4.94%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Simulation | formatter[types.ts] |
12.5 ms | 11.8 ms | +6.17% |
| ⚡ | Simulation | formatter[App.tsx] |
47.4 ms | 45.7 ms | +3.72% |
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 07-16-perf_formatter_core_stage_ir_buffers_on_the_heap_to_reduce_arena_memory (d7cedab) with main (8eb9eca)2
Footnotes
-
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. ↩
-
No successful run was found on
main(7c46346) during the generation of this report, so 8eb9eca was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
Oxfmt Ecosystem CI
💥 = panic, |
bf07076 to
aa1921c
Compare
aa1921c to
12a1158
Compare
12a1158 to
8039aca
Compare
c880bce to
d7cedab
Compare
Merge activity
|
…mory (#24582) Part of #24558 Adds `HeapVecBuffer` to `oxc_formatter_core`: a staging twin of `VecBuffer` that grows on a pooled heap vector and moves the finished sequence into the arena as one exactly-sized allocation. Converted JS formatter call sites: `Formatter::intern`, `BestFitting` variant construction, and the three grouped-call-argument variant builders. The other formatters would automatically benefit from this as well, and there was no need to make any individual changes. --- TIL: `antd.js` is a bundled file and is not intended for general formatting.
d7cedab to
c191f51
Compare
The JSX child-list builders (`MultilineBuilder` / `FlatBuilder` in `child_list.rs`) were the last IR staging path still growing vectors in the arena. They accumulate elements across the whole child-list loop while arbitrary child content is formatted in between, so every growth stranded its old allocation (the same pattern #24582 removed from `intern` / `BestFitting` / grouped arguments). They can't use `HeapVecBuffer`: the flat and multiline builders write *alternately* for each child, which would interleave their segments in the shared scratch vector and break its LIFO watermark discipline. The thread-local cache behind it has no such requirement though. So each builder now checks out its own `ScratchBuffer` and writes into it through a small private `Buffer` adapter (`ChildListBuffer`); the finished result reaches the arena as one exactly-sized copy via the new `Formatter::intern_elements`, which clears the source so its capacity returns to the cache on drop. Now arena reallocs for `RadixUIAdoptionSection.jsx` is 0.

Part of #24558
Adds
HeapVecBuffertooxc_formatter_core: a staging twin ofVecBufferthat grows on a pooled heap vector and moves the finished sequence into the arena as one exactly-sized allocation.Converted JS formatter call sites:
Formatter::intern,BestFittingvariant construction, and the three grouped-call-argument variant builders.The other formatters would automatically benefit from this as well, and there was no need to make any individual changes.
TIL:
antd.jsis a bundled file and is not intended for general formatting.