Skip to content

perf(ecmascript): avoid heap-allocating Math.min/max/imul operands#23941

Merged
camc314 merged 4 commits into
oxc-project:mainfrom
linyiru:perf/ecmascript-math-variadic-arena
Jul 5, 2026
Merged

perf(ecmascript): avoid heap-allocating Math.min/max/imul operands#23941
camc314 merged 4 commits into
oxc-project:mainfrom
linyiru:perf/ecmascript-math-variadic-arena

Conversation

@linyiru

@linyiru linyiru commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

try_fold_math_variadic constant-folds Math.min / Math.max / Math.imul(...) by
collecting the operand values into a Vec<f64>. That is a system allocation on every
foldable call
, on the minifier's constant-folding hot path.

The evaluation context already exposes the arena (ConstantEvaluationCtx: GetAllocator,
and the module already imports ArenaVec), so collect into an ArenaVec instead. The
list is transient and only iterated / indexed (.iter(), .first(), .get()), so the
change is behaviour-identical — the system allocation is simply gone.

Results — cargo allocs (minifier system allocations)

fixture before after
kitchen-sink.tsx 2576 2312 (−264)
antd.js 1040 870 (−170)
pdf.mjs 2569 2476 (−93)
App.tsx 78 72

Tests

  • cargo test -p oxc_ecmascript --lib --tests — pass
  • cargo test -p oxc_minifier --lib --tests — 539 pass

AI disclosure

Prepared with AI assistance (Claude). I located the hotspot with a per-call-site allocation
profile across real files, confirmed the list is transient/iterated-only, and verified the
reduction with cargo allocs plus the test suites.

@codspeed-hq

codspeed-hq Bot commented Jun 29, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 62 untouched benchmarks
⏩ 9 skipped benchmarks1


Comparing linyiru:perf/ecmascript-math-variadic-arena (4d29d1e) with main (e744e79)

Open in CodSpeed

Footnotes

  1. 9 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.

@linyiru
linyiru force-pushed the perf/ecmascript-math-variadic-arena branch from c09ccc4 to 62c4a52 Compare June 29, 2026 15:31
@linyiru
linyiru marked this pull request as ready for review June 29, 2026 15:34
Comment thread crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs Outdated
@linyiru linyiru changed the title perf(ecmascript): collect Math.min/max/imul operands in the arena perf(ecmascript): avoid heap-allocating Math.min/max/imul operands Jun 29, 2026
@linyiru
linyiru requested a review from camc314 June 29, 2026 16:29
@linyiru
linyiru force-pushed the perf/ecmascript-math-variadic-arena branch from b0e170c to d65f2ee Compare July 4, 2026 03:06
linyiru and others added 4 commits July 5, 2026 14:06
`try_fold_math_variadic` constant-folds `Math.min/max/imul(...)` by collecting the
operand values into a `Vec<f64>`, which is a system allocation on every foldable
call — a hot minifier path.

The evaluation context already exposes the arena (`ConstantEvaluationCtx: GetAllocator`),
so collect into an `ArenaVec` instead. The list is transient and only iterated/indexed,
so behaviour is unchanged; the system allocation is gone.

`cargo allocs` (minifier system allocations):
  kitchen-sink.tsx  2576 -> 2312  (-264)
  antd.js           1040 -> 870   (-170)
  pdf.mjs           2569 -> 2476  (-93)
  App.tsx             78 -> 72    (-6)
Addressing review (don't put the transient operand list in the arena). A stack
`SmallVec` also beats `Vec::with_capacity(args.len())`: most `Math.min/max/imul`
calls bail early when an arg isn't a constant, and `Vec::new()` doesn't allocate
on that path while `with_capacity` allocates upfront — so it would *increase*
allocations. `SmallVec` is zero-alloc on both the early-bail and the small-fold
paths, and never touches the arena.

cargo allocs (minifier system allocations, vs main; arena counts unchanged):
  kitchen-sink.tsx  2576 -> 2312
  antd.js           1040 -> 870
  pdf.mjs           2569 -> 2476
@camc314
camc314 force-pushed the perf/ecmascript-math-variadic-arena branch from d65f2ee to 4d29d1e Compare July 5, 2026 13:09

@camc314 camc314 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

@camc314
camc314 merged commit bd49aff into oxc-project:main Jul 5, 2026
39 checks passed
Boshen added a commit that referenced this pull request Jul 6, 2026
### 🚀 Features

- 260425f semantic/examples: Include unresolved references (#24214)
(camc314)
- 2d9b0b3 minifier: Fold boolean-literal ternary branches in value
contexts (#24110) (Dunqing)
- 61fbf10 ast: Implement `ReplaceWith` on all AST types (#24013)
(overlookmotel)
- 7db7a29 allocator: Add `ReplaceWith` trait (#24012) (overlookmotel)
- 4eb074e mangler: Add `reserved` option for names that must not be
mangled (#24041) (Dunqing)
- 2e62012 data_structures: Add `StringExt` trait (#24006)
(overlookmotel)
- 60e7160 minifier: Drop side-effect-free IIFEs whose result is unused
(#23967) (Dunqing)
- 26dd9e2 ast: Add method to widen inherited enum ref to parent ref
(#23961) (overlookmotel)

### 🐛 Bug Fixes

- e8b50ee transformer: Clean up semantics for stripped TypeScript syntax
(#24180) (camc314)
- d966d0b react_compiler: Remove clippy allows (#24168) (Boshen)
- 854ef8d react_compiler: Compile generic functions instead of
over-bailing on type-param hoisting (#24158) (Boshen)
- 093586c react_compiler: Align memoization cache-slot allocation with
Babel (#24157) (Boshen)
- 09c8f59 react_compiler: Normalize snapshot fixture paths (#24142)
(camc314)
- f13df97 react_compiler: Drop stray empty statement from catch bindings
(#24133) (Boshen)
- cb2a505 react_compiler: Codegen destructuring reassignment targets
(#24131) (Boshen)
- b82c394 react_compiler: Propagate codegen invariants instead of
emitting empty bodies (#24128) (Boshen)
- 5771982 react_compiler: Render unchanged programs as source in fixture
snapshots (#24129) (Boshen)
- 4b16e1a transformer/async-to-generator: Preserve direct eval scope
flags (#24136) (camc314)
- 4e9194f react_compiler: Lower `delete obj.prop` to
Property/ComputedDelete (#24123) (Boshen)
- 0b25582 ast: Type binding node `typeAnnotation` as `TSTypeAnnotation |
null` (#23113) (Boshen)
- 018c0e5 transformer: Hoist lowered async declarations (#22770)
(camc314)
- 652fbaf mangler: Keep names of destructured exported bindings (#24036)
(Dunqing)
- e274415 minifier: Don't drop global calls that throw despite pure
arguments (#23917) (Dunqing)
- 59abb30 minifier: Only merge string literals in `try_fold_add` when
the inner operator is `+` (#23622) (Jerry Zhao)

### ⚡ Performance

- c5ca77b transformer: Avoid cloning refresh options (#24191) (camc314)
- bf1a151 react_compiler: Compile out debug printers (#24184) (Boshen)
- abb44a0 transformer: Build fixed object-rest arguments (#24190)
(camc314)
- a4db731 isolated_declarations: Use `ReplaceWith` instead of `TakeIn`
(#24016) (overlookmotel)
- ff10855 transformer: Use `ReplaceWith` instead of `TakeIn` (#24015)
(overlookmotel)
- bd49aff ecmascript: Avoid heap-allocating Math.min/max/imul operands
(#23941) (Lawrence Lin)
- e4b708b react_compiler: Skip compiled files before prefilters (#24171)
(Boshen)
- c59f2fe rust: Return impl ExactSizeIterator from slice-backed
accessors (#24144) (Boshen)
- 5d6d04a codegen: SWAR-skip boring byte runs in sourcemap line/column
scan (#24023) (Boshen)
- a55e0be traverse: Reduce string operations in `get_var_name_from_node`
(#24007) (overlookmotel)
- e6d48e1 transformer/nullish_coalescing: Move cold path into separate
function (#23989) (overlookmotel)
- c4e35b5 transformer/object_rest_spread: Pre-allocate capacity in `Vec`
(#23988) (overlookmotel)
- 527b8e5 transformer/decorators: Narrow type earlier (#23987)
(overlookmotel)

### 📚 Documentation

- 30d17f5 allocator: Clarify docs for `TakeIn::take_in_box` (#24093)
(overlookmotel)
- 675e6a8 ast: Correct doc comment for `PrivateFieldExpression` (#24008)
(overlookmotel)
- e4c30e6 minifier: Explain what `dce` mode means (#23994) (Dunqing)
- 37cbf88 ast_macros: Document fields of `StructDetails` (#23959)
(overlookmotel)
- 4de3e54 ast: Correct doc comment (#23948) (overlookmotel)

Co-authored-by: Boshen <[email protected]>
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