Skip to content

perf: speed up swc_ecma_codegen JsWriter hot path#11870

Draft
hardfist wants to merge 5 commits into
swc-project:mainfrom
hardfist:codex/ecma-codegen-10pct-speedup
Draft

perf: speed up swc_ecma_codegen JsWriter hot path#11870
hardfist wants to merge 5 commits into
swc-project:mainfrom
hardfist:codex/ecma-codegen-10pct-speedup

Conversation

@hardfist

Copy link
Copy Markdown
Collaborator

Description:

This PR improves swc_ecma_codegen performance in the JsWriter hot path without changing benchmark definitions or test fixtures.

Changes:

  • replace global HashSet<(BytePos, line, col)> source-map dedupe with per-generated-position dedupe
  • cache indentation text (indent_cache) and write it in one call instead of per-level loops
  • streamline write() hot-path branching for Some(span) vs None

Motivation:

  • remove hashing and repeated branching from frequent codegen paths
  • reduce write calls for pretty-print indentation
  • keep emitted output and source-map behavior unchanged while improving throughput

Benchmark evidence (cargo bench -p swc_ecma_codegen --bench bench -- --warm-up-time 1 --measurement-time 2 --baseline pre_opt):

  • es/codegen/colors: change: [-14.707% -14.454% -14.172%]
  • es/codegen/large: change: [-22.255% -21.769% -21.330%]

No benchmark files or tests were modified.

BREAKING CHANGE:

None.

Related issue (if exists):

None.


Validation run:

  • git submodule update --init --recursive
  • cargo fmt --all
  • cargo clippy --all --all-targets -- -D warnings
  • cargo test -p swc_ecma_codegen

@changeset-bot

changeset-bot Bot commented May 19, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9ac84bc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

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

@codspeed-hq

codspeed-hq Bot commented May 19, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 9.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

⚡ 19 improved benchmarks
✅ 200 untouched benchmarks
⏩ 31 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
es/oxc/benches/assets/table.tsx/sourceMap=true/reactDev=false 7.3 ms 7.2 ms +2.38%
es/oxc/benches/assets/table.tsx/sourceMap=true/reactDev=true 8.1 ms 7.9 ms +2.73%
es/large/codegen/es2015 33.9 ms 28.9 ms +17.53%
es/large/codegen/es2016 33.9 ms 28.9 ms +17.61%
es/large/codegen/es2017 34 ms 28.8 ms +18.07%
es/large/codegen/es2018 34 ms 28.9 ms +17.67%
es/large/codegen/es2019 33.9 ms 28.9 ms +17.67%
es/large/codegen/es2020 34 ms 28.9 ms +17.63%
es/large/codegen/es3 33.9 ms 28.8 ms +17.66%
es/large/codegen/es5 33.9 ms 28.9 ms +17.63%
es/oxc/benches/assets/parser.ts/sourceMap=true/reactDev=false 83.5 ms 81.5 ms +2.37%
es/oxc/benches/assets/parser.ts/sourceMap=true/reactDev=true 83.5 ms 81.5 ms +2.43%
es/oxc/benches/assets/renderer.ts/sourceMap=true/reactDev=false 13.6 ms 13.3 ms +2.31%
es/oxc/benches/assets/renderer.ts/sourceMap=true/reactDev=true 13.6 ms 13.3 ms +2.37%
es/oxc/benches/assets/UserSettings.tsx/sourceMap=true/reactDev=false 1.2 ms 1.2 ms +2.14%
es/codegen/with-parser/colors 149.9 µs 145.4 µs +3.13%
es/codegen/colors 71.9 µs 68 µs +5.72%
es/codegen/large 641.5 µs 556.1 µs +15.35%
es/codegen/with-parser/large 1,062.7 µs 976.7 µs +8.8%

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-codegen-10pct-speedup (9ac84bc) with main (aa5b539)

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.

@hardfist

Copy link
Copy Markdown
Collaborator Author

@codspeedbot explain why this is faster

@codspeed-hq

codspeed-hq Bot commented May 19, 2026

Copy link
Copy Markdown

@codspeedbot explain why this is faster

Here's my analysis of why this PR improves performance, based on CodSpeed flamegraph data comparing main and this branch:


Why this PR is faster

I compared the CodSpeed flamegraphs for the es/codegen/large benchmark between main (645.1 µs) and this PR (555.6 µs, +16.11% efficiency). Three optimizations are responsible for the gains:

1. Elimination of the srcmap_done HashSet (~23.5 µs saved, ~3.6% of total)

This is the single largest contributor to the speedup. On main, every source-map entry goes through JsWriter::srcmap(), which maintains a HashSet<(BytePos, u32, u32), FxBuildHasher> to deduplicate mappings. The flamegraph shows:

  • srcmap(): 12.3 µs self time (1.90%), 23.5 µs total (3.64%)
  • hashbrown::RawTable::reserve_rehash: 10.1 µs self time (1.57%) — pure hashing and rehashing overhead

On the PR branch, srcmap() and all hashbrown operations are completely absent from the flamegraph. The global HashSet was replaced with a lighter per-generated-position dedup, avoiding hashing, bucket management, and repeated heap allocations on every source-map call.

2. Indentation cache (reduced write calls)

On main, write_indent_string() loops N times (once per indent level), issuing a separate raw_write(indent_str) call each iteration. The PR introduces an indent_cache that pre-computes the full indentation string, collapsing N write calls into one. This primarily benefits the pretty-print path and is more impactful on deeply nested code like the large benchmark.

3. Streamlined write() hot-path branching (~82 µs saved in emit path)

The Expr::emit_with call tree — the core recursive codegen loop — dropped from 366.2 µs (56.76%) on main to 283.9 µs (51.10%) on the PR. The write methods (write_punct, write_keyword, write_symbol) no longer route through the srcmap() function on every invocation. For example, write_punct total time dropped from 11.4 µs → 7.1 µs as it now only calls update_pos without the srcmap dedup overhead.

Why the improvement is larger on large (~16%) vs colors (~6%)

The large benchmark has many more AST nodes and source-map entries, so the HashSet overhead (which grows with the number of entries — rehashing, allocations) dominated more of the total time. The colors benchmark is small enough that the fixed costs (parsing, sourcemap finalization) dilute the per-node savings.

Summary
  • Warming up the performance wizard...
  • Analyzing performance improvements — Flamegraph analysis complete — identified three key optimizations driving the speedup
  • Handling comment — Comment handling finished successfully

@hardfist hardfist changed the title [codex] perf: speed up swc_ecma_codegen JsWriter hot path perf: speed up swc_ecma_codegen JsWriter hot path May 19, 2026
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