Skip to content

perf(parser): remove -> Result<T> from all parsing methods#10588

Merged
graphite-app[bot] merged 1 commit intomainfrom
04-24-perf_parser_remove_result_
Apr 24, 2025
Merged

perf(parser): remove -> Result<T> from all parsing methods#10588
graphite-app[bot] merged 1 commit intomainfrom
04-24-perf_parser_remove_result_

Conversation

@Boshen
Copy link
Member

@Boshen Boshen commented Apr 24, 2025

All credits to @overlookmotel

In parser, optimize for common case that file parses successfully.

Instead of parse_* methods returning Results which are checked with ? in the caller, always return a valid node T.

In case of a fatal parsing error:

  • Record the error in fatal_error property of ParserImpl.
  • Also record current length of errors.
  • Fast forward the lexer to EOF, so parsing will come to an end swiftly.
  • Create a dummy node of the required type and return it.
  • At end of parsing, if fatal_error.is_some(), then there was a fatal error. In that case:

Truncate errors to the length it was at time of the fatal error (because more errors may occur as the parser finds it's unexpectedly at EOF).
Add fatal_error to errors.
i.e. all parse methods never fail, they just return nonsense if there's a fatal error. This removes all the branches implicit in ?.


Wall clock benchmark:

oxc  main ❯ hyperfine './target/release/examples/parser-new ./target/typescript.js' './target/release/examples/parser ./target/typescript.js'
Benchmark 1: ./target/release/examples/parser-new ./target/typescript.js
  Time (mean ± σ):      42.7 ms ±   2.1 ms    [User: 36.7 ms, System: 5.3 ms]
  Range (min … max):    41.6 ms …  59.6 ms    68 runs

  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

Benchmark 2: ./target/release/examples/parser ./target/typescript.js
  Time (mean ± σ):      44.2 ms ±   0.7 ms    [User: 38.3 ms, System: 5.4 ms]
  Range (min … max):    43.7 ms …  48.6 ms    64 runs

Summary
  ./target/release/examples/parser-new ./target/typescript.js ran
    1.04 ± 0.05 times faster than ./target/release/examples/parser ./target/typescript.js

/usr/bin/time -al:

old: 517424069  instructions retired
new: 492334561  instructions retired

uses 5% less CPU instructions.


Testing:

@Boshen Boshen marked this pull request as draft April 24, 2025 12:00
@github-actions github-actions bot added the A-parser Area - Parser label Apr 24, 2025
Copy link
Member Author

Boshen commented Apr 24, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

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.

@github-actions github-actions bot added the C-performance Category - Solution not expected to change functional behavior, only performance label Apr 24, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Apr 24, 2025

CodSpeed Instrumentation Performance Report

Merging #10588 will improve performances by 7.9%

Comparing 04-24-perf_parser_remove_result_ (4f56b2c) with main (e228840)

Summary

⚡ 5 improvements
✅ 31 untouched benchmarks

Benchmarks breakdown

Benchmark BASE HEAD Change
parser[RadixUIAdoptionSection.jsx] 93.5 µs 89.3 µs +4.7%
parser[antd.js] 114.2 ms 106.3 ms +7.43%
parser[cal.com.tsx] 30.3 ms 28.9 ms +4.93%
parser[checker.ts] 57.5 ms 54 ms +6.52%
parser[pdf.mjs] 18.3 ms 17 ms +7.9%

@Boshen Boshen marked this pull request as ready for review April 24, 2025 14:27
@graphite-app graphite-app bot added the 0-merge Merge with Graphite Merge Queue label Apr 24, 2025
@graphite-app
Copy link
Contributor

graphite-app bot commented Apr 24, 2025

Merge activity

All credits to @overlookmotel

In parser, optimize for common case that file parses successfully.

Instead of parse_* methods returning Result<T>s which are checked with ? in the caller, always return a valid node T.

In case of a fatal parsing error:

Record the error in fatal_error property of ParserImpl.
Also record current length of errors.
Fast forward the lexer to EOF, so parsing will come to an end swiftly.
Create a dummy node of the required type and return it.
At end of parsing, if fatal_error.is_some(), then there was a fatal error. In that case:

Truncate errors to the length it was at time of the fatal error (because more errors may occur as the parser finds it's unexpectedly at EOF).
Add fatal_error to errors.
i.e. all parse methods never fail, they just return nonsense if there's a fatal error. This removes all the branches implicit in `?`.

---

Wall clock benchmark:

```
oxc  main ❯ hyperfine './target/release/examples/parser-new ./target/typescript.js' './target/release/examples/parser ./target/typescript.js'
Benchmark 1: ./target/release/examples/parser-new ./target/typescript.js
  Time (mean ± σ):      42.7 ms ±   2.1 ms    [User: 36.7 ms, System: 5.3 ms]
  Range (min … max):    41.6 ms …  59.6 ms    68 runs

  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

Benchmark 2: ./target/release/examples/parser ./target/typescript.js
  Time (mean ± σ):      44.2 ms ±   0.7 ms    [User: 38.3 ms, System: 5.4 ms]
  Range (min … max):    43.7 ms …  48.6 ms    64 runs

Summary
  ./target/release/examples/parser-new ./target/typescript.js ran
    1.04 ± 0.05 times faster than ./target/release/examples/parser ./target/typescript.js
```

/usr/bin/time -al:

```
old: 517424069  instructions retired
new: 492334561  instructions retired
```

uses 5% less CPU instructions.

---

Did some fuzzing with https://github.com/oxc-project/fuzz-oxc, found no crashes.
@graphite-app graphite-app bot force-pushed the parser-fatal-error branch from 8f7bb53 to e228840 Compare April 24, 2025 14:28
@graphite-app graphite-app bot force-pushed the 04-24-perf_parser_remove_result_ branch from c44c732 to 4f56b2c Compare April 24, 2025 14:29
Base automatically changed from parser-fatal-error to main April 24, 2025 14:34
@graphite-app graphite-app bot removed the 0-merge Merge with Graphite Merge Queue label Apr 24, 2025
@graphite-app graphite-app bot merged commit 4f56b2c into main Apr 24, 2025
29 checks passed
@graphite-app graphite-app bot deleted the 04-24-perf_parser_remove_result_ branch April 24, 2025 14:38
@Boshen Boshen mentioned this pull request Apr 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-parser Area - Parser C-performance Category - Solution not expected to change functional behavior, only performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant