Skip to content

perf(es/parser): micro-optimize parser hot paths#11859

Draft
hardfist wants to merge 7 commits into
swc-project:mainfrom
hardfist:codex/ecma-parser-micro-optimize
Draft

perf(es/parser): micro-optimize parser hot paths#11859
hardfist wants to merge 7 commits into
swc-project:mainfrom
hardfist:codex/ecma-parser-micro-optimize

Conversation

@hardfist

@hardfist hardfist commented May 16, 2026

Copy link
Copy Markdown
Collaborator

Description:

Micro-optimize swc_ecma_parser hot paths using local Valgrind/Cachegrind simulation data. This builds on the prior parser optimization work and focuses on the TypeScript parser benchmark input.

Changes in this PR:

  • Avoid repeated comment-buffer scans and reserve comment maps before publishing buffered comments.
  • Reuse static atoms for known keyword/identifier tokens instead of slicing and interning source text on hot identifier paths.
  • Fast-path the common primary-expression identifier path and outline rarer arrow-expression recovery.
  • Avoid moving boxed expressions for simple variant checks in hot expression parser paths.
  • Fast-path plain primary identifiers before the slower rest parser path when Flow pattern matching is not enabled.
  • Use a direct token-set check for unary-prefix expression starts.
  • Skip and narrow the plain ( subscript path when no_call already forbids consuming calls.
  • Keep token/input accessor hot paths inline where measurements showed the combined parser path benefited.

Micro-Optimization Progress

Target benchmark: swc_ecma_parser TypeScript parser input (crates/swc_ecma_parser/benches/files/typescript.js)
Measurement mode: isolated Valgrind Cachegrind run with parser-harness client requests around the parse loop
Primary metric: Cachegrind I refs
Baseline commit: 25b2b1fb5c
Optimized commit: ae6ab6d2eb

Commit Benchmark Tool Metric Before After Delta Notes
ef0a84219e typescript.js parse, 1 iter Cachegrind I refs 688,371,852 653,923,388 -34,448,464 (-5.005%) First optimized pass
621283573d typescript.js parse, 1 iter Cachegrind I refs 688,371,852 653,872,357 -34,499,495 (-5.012%) Adds boxed expression ref checks
d10b5e49b6 typescript.js parse, 1 iter Cachegrind I refs 688,371,852 653,859,937 -34,511,915 (-5.014%) Adds unary-prefix token-set check
5016fa25fa typescript.js parse, 1 iter Cachegrind I refs 688,371,852 650,545,675 -37,826,177 (-5.495%) Adds primary identifier fast path
e2a78b422c typescript.js parse, 1 iter Cachegrind I refs 688,371,852 650,245,133 -38,126,719 (-5.539%) Skips plain calls in no-call subscript parse
ae6ab6d2eb typescript.js parse, 1 iter Cachegrind I refs 688,371,852 649,450,821 -38,921,031 (-5.654%) Narrows the no-call subscript start check
ae6ab6d2eb typescript.js parse, 1 iter Cachegrind I refs 650,245,133 649,450,821 -794,312 (-0.122%) Incremental vs previous PR commit
ae6ab6d2eb typescript.js parse, 1 iter Cachegrind I1 misses 8,294,027 7,444,699 -849,328 (-10.24%) Cache simulation enabled; +118,950 (+1.62%) vs previous commit
ae6ab6d2eb typescript.js parse, 1 iter Cachegrind LL misses 1,946,681 1,804,610 -142,071 (-7.30%) Cache simulation enabled; +75 (+0.004%) vs previous commit

Artifacts kept locally under ignored optimization-artifacts/valgrind/cachegrind/:

  • Baseline: 20260516T045236Z-baseline-isolated-typescript-cache-1iter
  • Previous final: 20260516T082221Z-no-call-lparen-fast-return-typescript-cache-1iter
  • Latest final: 20260516T082607Z-subscript-lparen-branch-typescript-cache-1iter

Validation:

  • Latest commit ae6ab6d2eb: git submodule update --init --recursive
  • Latest commit ae6ab6d2eb: cargo fmt --all
  • Latest commit ae6ab6d2eb: cargo test -p swc_ecma_parser --features verify
  • Latest commit ae6ab6d2eb: cargo test -p swc_ecma_parser --features flow,verify
  • Latest commit ae6ab6d2eb: cargo clippy --all --all-targets -- -D warnings
  • Earlier in this PR: cargo test -p swc_common

Known local baseline issue:

  • cargo test -p swc_ecma_parser without verify fails errors_tests__typescript_errors__type_only_import_specifier__invalid_type_only__input_ts; the same one-test command fails on the clean branch HEAD without the latest local diff and passes with --features verify. CI uses cargo test -p swc_ecma_parser --features verify for this crate.

BREAKING CHANGE:

None.

Related issue (if exists):

None.

@changeset-bot

changeset-bot Bot commented May 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ae6ab6d

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codspeed-hq

codspeed-hq Bot commented May 16, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 2.83%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 11 improved benchmarks
✅ 208 untouched benchmarks
⏩ 31 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
es/large/parser 36.3 ms 35 ms +3.61%
typescript/fast-strip 403 µs 389.5 µs +3.47%
es/parser/angular 14.4 ms 14 ms +2.89%
es/parser/jquery mobile 17.9 ms 17.4 ms +2.92%
es/parser/mootools 9 ms 8.8 ms +2.51%
es/parser/three 56 ms 54.6 ms +2.51%
es/parser/typescript 317.8 ms 308.2 ms +3.09%
es/parser/underscore 2 ms 1.9 ms +2.59%
es/parser/backbone 2.3 ms 2.2 ms +2.24%
es/parser/jquery 11.6 ms 11.3 ms +2.94%
es/parser/yui 8.9 ms 8.7 ms +2.35%

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 hardfist:codex/ecma-parser-micro-optimize (ae6ab6d) with main (2ab1b8a)2

Open in CodSpeed

Footnotes

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

  2. No successful run was found on main (4c44d5d) during the generation of this report, so 2ab1b8a was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant