Skip to content

fix(parser): performance issue due to debug code#17834

Merged
JLHwung merged 2 commits intobabel:mainfrom
JLHwung:fix-parser-performance-regression
Feb 27, 2026
Merged

fix(parser): performance issue due to debug code#17834
JLHwung merged 2 commits intobabel:mainfrom
JLHwung:fix-parser-performance-regression

Conversation

@JLHwung
Copy link
Copy Markdown
Contributor

@JLHwung JLHwung commented Feb 26, 2026

Q                       A
Fixed Issues? Babel Parser 8 is 50% slower than Babel Parser 7 since 8.0.0-rc.1 on some benchmark cases
Patch: Bug Fix? Yes
Major: Breaking Change?
Minor: New Feature?
Tests Added + Pass? Yes
Documentation PR Link
Any Dependency Changes?
License MIT

Here is the benchmark result between 7.29.0 and 8.0.0-rc.2 when running benchmark/babel-parser/many-async-arrows/bench.mjs

baseline 256 async arrow functions: 6_922 ops/sec ±2.5% (0.144ms)
baseline 512 async arrow functions: 3_305 ops/sec ±0.47% (0.303ms)
baseline 1024 async arrow functions: 1_649 ops/sec ±1.67% (0.606ms)
baseline 2048 async arrow functions: 825 ops/sec ±0.76% (1.211ms)
current 256 async arrow functions: 2_435 ops/sec ±0.2% (0.411ms)
current 512 async arrow functions: 1_215 ops/sec ±0.19% (0.823ms)
current 1024 async arrow functions: 589 ops/sec ±1.83% (1.697ms)
current 2048 async arrow functions: 303 ops/sec ±0.22% (3.297ms)

In this PR we fixed a performance regression introduced in #17756, where we only inlined the NODE_ENV environment when building the @babel/standalone. This unfortunately impacts @babel/parser because we have debug code in the hot path finishNodeAt, and although the debug code error is probably not activated for end users, the process.env access has to be supported from a C++ runtime, which incurs a great toll on the performance.

Here is the benchmark result between 7.29.0 and this PR, I will investigate if there are any other performance issues and open new PRs.

baseline 256 async arrow functions: 6_875 ops/sec ±0.65% (0.145ms)
baseline 512 async arrow functions: 3_346 ops/sec ±0.48% (0.299ms)
baseline 1024 async arrow functions: 1_636 ops/sec ±1.42% (0.611ms)
baseline 2048 async arrow functions: 825 ops/sec ±0.29% (1.212ms)
current 256 async arrow functions: 5_878 ops/sec ±0.15% (0.17ms)
current 512 async arrow functions: 2_907 ops/sec ±1.18% (0.344ms)
current 1024 async arrow functions: 1_464 ops/sec ±0.2% (0.683ms)
current 2048 async arrow functions: 727 ops/sec ±0.16% (1.375ms)

@JLHwung JLHwung added the PR: Performance (next major) 🏃‍♀️ A type of pull request used for our changelog categories for next major release label Feb 26, 2026
@babel-bot
Copy link
Copy Markdown
Collaborator

babel-bot commented Feb 26, 2026

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/61034

Copy link
Copy Markdown
Member

@liuxingbaoyu liuxingbaoyu left a comment

Choose a reason for hiding this comment

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

Great!

@liuxingbaoyu
Copy link
Copy Markdown
Member

I actually saw this performance change, but mistakenly thought it was unrelated to our code.🤦‍♂️
https://codspeed.io/babel/babel/runs/compare/69734c3b04bce3fcf1fe7e61..69970244b0e5525bd7dc7885

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Feb 26, 2026

Open in StackBlitz

commit: c72814a

@JLHwung
Copy link
Copy Markdown
Contributor Author

JLHwung commented Feb 26, 2026

I have also done a process.env.NODE_ENV search in */src . In this PR there is only one occurrence now and it seems reasonable to me:

return process.env.BABEL_ENV || process.env.NODE_ENV || defaultValue;

At this point I don't think we have other code paths affected by the build configuration change.

@JLHwung
Copy link
Copy Markdown
Contributor Author

JLHwung commented Feb 26, 2026

I actually saw this performance change, but mistakenly thought it was unrelated to our code.🤦‍♂️ https://codspeed.io/babel/babel/runs/compare/69734c3b04bce3fcf1fe7e61..69970244b0e5525bd7dc7885

Thank you. This website is great, I will keep an eye on it.

The optional chains do not affect Babel 7 because they are transpiled.
Comment on lines +21 to +24
if (parser != null) {
if (parser.optionFlags & OptionFlags.Ranges) this.range = [pos, 0];
if (parser.filename) this.loc.filename = parser.filename;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm surprised this affects performance, could this be improved in V8?

Copy link
Copy Markdown
Contributor Author

@JLHwung JLHwung Feb 27, 2026

Choose a reason for hiding this comment

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

This change is guided from the CPU profile of running many-async-arrow-tests compared between Babel 7 and Babel 8. Previously, there will be 4.4% (around 400) ticks spent on the JavaScript function *Node, with this change, V8 more eagerly optimizes the Node constructor to machine code and therefore it will be only 0 or 1 tick spent on *Node.

Note that I can not observe performance changes when running real-case. Inspecting the CPU profile, it seems that V8 eventually will optimize the Node constructor.

In Babel 7 the optional chain is tranpiled so Babel 7 is not affected. In Babel 8 they are not. It seems that optional chain in hot path has an impact on the timing of the optimization. As we know that Node is a hot path, and the second ?. check is useless if the parser is not nullish (most case), we can restructure the code to avoid the duplicate nullish check and also let V8 more eagerly optimize it.

@JLHwung JLHwung merged commit ccf6045 into babel:main Feb 27, 2026
55 checks passed
@JLHwung JLHwung deleted the fix-parser-performance-regression branch February 27, 2026 15:45
@nicolo-ribaudo nicolo-ribaudo added the PR: Performance 🏃‍♀️ A type of pull request used for our changelog categories label Mar 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg: parser PR: Performance (next major) 🏃‍♀️ A type of pull request used for our changelog categories for next major release PR: Performance 🏃‍♀️ A type of pull request used for our changelog categories

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants