perf(allocator): per-platform Arena::new_fixed_size implementations#22088
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 not alter performance
Comparing Footnotes
|
Arena::new_fixed_size implementationsArena::new_fixed_size implementations
There was a problem hiding this comment.
Pull request overview
This PR refactors Arena::new_fixed_size into per-platform implementations to better handle very high alignment requirements (4 GiB) across macOS, Windows, and Linux, reducing unnecessary VM usage on Linux and avoiding Rust std’s Windows high-alignment workaround overhead.
Changes:
- Introduces a new
arena/fixed_size/module with OS-specificArena::new_fixed_sizeimplementations (macos,windows,linuxfallback). - Adjusts Windows fixed-size arena allocation to use a low-alignment backing allocation and manually select a 4 GiB-aligned sub-region.
- Updates CI workflow job/step gating (currently by commenting out
if:conditions).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
crates/oxc_allocator/src/arena/fixed_size/mod.rs |
Adds shared docs/invariants and dispatches to platform-specific implementations via cfg. |
crates/oxc_allocator/src/arena/fixed_size/macos.rs |
Uses 2 GiB-aligned over-allocation strategy to obtain a 4 GiB-aligned chunk on macOS. |
crates/oxc_allocator/src/arena/fixed_size/windows.rs |
Uses a 16-byte-aligned over-allocation and manual pointer rounding to a 4 GiB boundary on Windows. |
crates/oxc_allocator/src/arena/fixed_size/linux.rs |
Requests the desired BLOCK_SIZE + BLOCK_ALIGN directly (and acts as the non-macOS/non-Windows fallback). |
.github/workflows/ci.yml |
Comments out multiple if: conditions, making many jobs/steps run unconditionally. |
Comments suppressed due to low confidence (1)
crates/oxc_allocator/src/arena/fixed_size/macos.rs:63
- The safety comment says “We allocated 4 GiB of memory”, but the actual allocation is
ALLOC_SIZE = BLOCK_SIZE + TWO_GIB(4 GiB - 16). Since this is part of the unsafe reasoning, it should describe the precise size/in-bounds argument (or referenceALLOC_SIZE) to avoid future confusion.
9aefd50 to
a636cb8
Compare
e0da555 to
6900042
Compare
a636cb8 to
40fa8b5
Compare
Merge activity
|
…#22088) Introduce separate `Arena::new_fixed_size` implementations for different platforms. Different platforms have differing abilities to service allocation requests with high alignment, so a different allocation strategy is appropriate for each: - Mac OS: System allocator refuses 4 GiB-aligned allocation requests. Allocate ~4 GiB with 2 GiB alignment, then use either the top or bottom half for the arena chunk. - Windows: System allocator cannot service allocations with any alignment over 16. Allocate ~6 GiB with 16 alignment, and then find a ~2 GiB block within that allocation which is aligned on 4 GiB. This is what `std` was doing already, but implementing it ourself sidesteps an annoyance with `std`'s implementation. - Linux: System allocator can service high-alignment requests, so just ask for exactly what we want (~2 GiB with 4 GiB alignment). This reduces virtual memory usage on Linux, side-steps `std`'s workaround on Windows, and is unchanged for Mac OS. This also prepares the way for using `VirtualAlloc`\-based allocation on Windows, to avoid committing large amounts of memory - the cause of OOM problems in Oxlint with JS plugins on Windows (#19395).
6900042 to
94040ce
Compare
40fa8b5 to
0bf0cb9
Compare
### 💥 BREAKING CHANGES - 0ffbe0d allocator: [**BREAKING**] Remove `Allocator::end_ptr` method (#21871) (overlookmotel) ### 🚀 Features - 9593ec8 transformer/jsx: Add jsxDEV source metadata for fragments (#21932) (Ido Rosenthal) ### 🐛 Bug Fixes - 429deac napi/parser: Export `visitorKeys` from `wasm` entrypoint (#21996) (NullVoxPopuli) - e852911 codegen: Preserve legal comments orphaned by upstream passes (#21575) (Dunqing) - e3399ec transformer/class-properties: Preserve RHS in logical-assignment to static private field (#21950) (Dunqing) - c59c199 transformer/typescript: Emit class fields for parameter properties (#21831) (Dunqing) - aaabde4 parser: Attach legal comments to following token (#21670) (Dunqing) ### ⚡ Performance - 0bf0cb9 allocator: Per-platform `Arena::new_fixed_size` implementations (#22088) (overlookmotel) ### 📚 Documentation - 62ec410 allocator: Correct doc comment for `Allocator::from_raw_parts` (#22093) (overlookmotel) - 3e152c6 allocator: Correct typos in comments (#22092) (overlookmotel) - e220855 allocator: Correct doc comment for `Allocator::set_cursor_ptr` (#21866) (overlookmotel) --------- Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com> Co-authored-by: Cameron Clark <cameron.clark@hey.com>
…#22388) Fixes #22339. #22088 introduced separate implementations for different platforms for creating a fixed-size `Arena` (used in Oxlint JS plugins). The Linux implementation assumed that Linux's system allocator can handle allocation requests with 4 GiB alignment. Unfortunately, it turns out there is an arithmetic overflow bug in Linux MUSL, where the allocation request succeeds, but then produces a segfault when the allocation is later freed. https://www.openwall.com/lists/musl/2026/05/12/1 Work around this by using the same workaround we already have for MacOS - over-allocate 4 GiB with 2 GiB alignment, and then use either the top or bottom half of the allocation - one of which is guaranteed to be 4 GiB-aligned. We could have applied the fix only on Linux MUSL, and left other Linux implementation as is, but it seems simpler to have one unified implementation. Many thanks to @kalvenschraut for finding and tracking down the cause of this bug.
…22124) Fixes #19395 - OOM errors in Oxlint JS plugins on Windows. ### The problem Previously, on all platforms each fixed-size (raw transfer-enabled) allocator obtained a large chunk of memory from the `System` allocator (6 GiB in the case of Windows). This is fine on MacOS and most Linux distros, as they use overcommit allocation accounting, so these allocations consume only virtual memory (address space), not actual physical memory. The available address space is enormous (~128 TiB), so it's hard to exhaust it. Physical memory is only consumed when pages of the allocation are actually touched, which the vast majority never are. Windows behaves differently. Allocations obtained from `System` allocator immediately count towards the memory limit. So, on memory-constrained machines, allocating 6 GiB can immediately fail - OOM just from linting the first file. ### This PR [#22088](#22088), #22122, and #22123 introduced platform-specific code for handling allocation and deallocation of fixed-size `Arena`s, which are used in raw transfer/Oxlint JS plugins. This PR switches to a different allocation method on Windows for fixed-size `Arena`s. It switches to using [VirtualAlloc](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc). This Windows-only API allows reserving address space and committing pages of that reservation as separate operations (similar to `mmap`). It works like this: - Fixed-size allocators now still reserve 6 GiB, but that only consumes address space, and does not count towards max memory limit. - Of that 6 GiB reservation, a 2 GiB "container", aligned on a 4 GiB boundary, will be used as the active part of the memory for storing the AST and other data. - Initially only the last 16 KiB of the container is committed (made into usable memory, with physical pages backing it). - If AST does not fit in the committed region, the region grows downwards, committing further pages to accomodate allocation requests, up to the maximum of 2 GiB. Essentially, this replicates something similar to Linux-style allocation, but on Windows. The only difference is that on Windows it's necessary to explicitly commit pages before touching them, whereas Linux/MacOS commit on demand automatically. ### Implementation details and testing This PR uses the battle-tested [windows-sys](https://crates.io/crates/windows-sys) crate for `VirtualAlloc` and `VirtualFree`. The allocation implementation is written from scratch, but while I was researching to validate that I'd not missed any edge cases, I discovered that it's almost identical to [wasmtime's implementation](https://github.com/bytecodealliance/wasmtime/blob/386a3280dee61f5c4120ce7cde621c1039e383d5/crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs) which uses the same APIs. Comments in the code go over in some detail considerations of Rust's memory model and pointer provenance. As far as I can see, the implementation is fairly watertight. There are tests for the essential functionality and edge cases, and #22121 made these tests run under Miri on Windows. However, I don't have access to a Windows machine to fully test this by running Oxlint on a massive repo. I am hoping members of the community who do can put this through its paces a bit before we merge. ### What's missing In enormous projects, when linting with both JS plugins and `import` plugin, it's possible to end up with a huge number of ASTs in memory simultaneously. With each AST consuming 6 GiB of address space, it's possible to exhaust the entire 128 TiB usable address space, and get OOM on _virtual_ memory. At present we have a workaround to prevent too many fixed-size allocators existing concurrently, so avoiding OOM, but the workaround is a drag on performance. Future work will aim to address this.

Introduce separate
Arena::new_fixed_sizeimplementations for different platforms.Different platforms have differing abilities to service allocation requests with high alignment, so a different allocation strategy is appropriate for each:
stdwas doing already, but implementing it ourself sidesteps an annoyance withstd's implementation.This reduces virtual memory usage on Linux, side-steps
std's workaround on Windows, and is unchanged for Mac OS.This also prepares the way for using
VirtualAlloc-based allocation on Windows, to avoid committing large amounts of memory - the cause of OOM problems in Oxlint with JS plugins on Windows (#19395).