Skip to content

perf(decode): skip ,/; pre-scan for large mappings#343

Merged
Boshen merged 1 commit into
mainfrom
perf/decode-skip-prescan
May 26, 2026
Merged

perf(decode): skip ,/; pre-scan for large mappings#343
Boshen merged 1 commit into
mainfrom
perf/decode-skip-prescan

Conversation

@Boshen
Copy link
Copy Markdown
Member

@Boshen Boshen commented May 26, 2026

Summary

Replaces the unconditional O(n) ,/; pre-scan in decode_mapping with a size-gated estimate, factored into a new estimate_token_capacity helper:

  • mapping.len() < 256 — keep the exact ,/; count. Cheap on short mappings, and getting the capacity exact lets the trailing Vec::into_boxed_slice in decode_from_string skip its shrink-realloc (otherwise the realloc dominates per-iteration cost on tiny benchmarks).
  • mapping.len() ≥ 256 — use the O(1) heuristic mapping.len() / 4 + 1. Realistic sourcemaps average ~4-5 bytes per segment, so the estimate is close to typical. Vec::push handles under-estimates via geometric growth; into_boxed_slice shrinks any over-allocation back to exact size, so RSS is unchanged.

No unsafe.

CodSpeed

Net: +3.61% improvement.

Wins (on the decode hot path — the algorithmic target)

Benchmark BASE HEAD Δ
parse[real_xlarge] 1.4 ms 1.3 ms +10.59%
parse[real_large] 50.5 µs 46.5 µs +8.56%
parse[real_medium] 14.9 µs 14.5 µs +2.74%

Regressions (binary-layout artifacts)

Benchmark BASE HEAD Δ
build_single 7.0 µs 7.1 µs -1.23%
lookup_table[real_medium] 1.4 µs 1.5 µs -1.97%

Both regressed functions live in src/sourcemap_builder.rs and src/sourcemap.rs respectively, not in src/decode.rs — they aren't algorithmically touched. Inserting any code into decode.rs shifts the .text section and moves other functions to slightly different addresses, which changes cache behavior in CodSpeed's deterministic simulator. Verified by trying multiple variants (inline if-else, threshold 128 vs 256, helper-fn extraction) — the regression set is the same regardless.

The absolute delta on the regressed benchmarks is ~80ns and ~30ns respectively. The xlarge win is ~150µs (3,750× larger). Net real-world impact on bundler workloads is overwhelmingly positive.

RSS

Measured at 500 parsed xlarge maps:

…an for small

Replace the unconditional O(n) `,` / `;` pre-scan in `decode_mapping` with
a size-gated estimate, factored into a new `estimate_token_capacity` helper:

* mapping.len() < 256 — keep the exact scan. It's cheap on short mappings
  and getting capacity exactly right lets the trailing
  `Vec::into_boxed_slice` in `decode_from_string` skip its shrink-realloc.
  That realloc otherwise dominates per-iteration cost on tiny benchmarks
  (32-byte fixtures), so an over-estimate would regress them.

* mapping.len() ≥ 256 — use the O(1) heuristic `mapping.len() / 4 + 1`.
  Realistic sourcemaps average ~4-5 bytes per segment, so the estimate is
  close to the typical count. `Vec::push` handles under-estimates via
  geometric growth, and the trailing `into_boxed_slice` shrinks any
  over-allocation back to exact size — so RSS is unaffected.

No unsafe. Local bench vs main is dominated by the xlarge win.
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 26, 2026

Merging this PR will improve performance by 3.61%

⚠️ 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

⚡ 3 improved benchmarks
❌ 2 regressed benchmarks
✅ 11 untouched benchmarks
⏩ 5 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
build_single 7 µs 7.1 µs -1.23%
lookup_table[real_medium] 1.4 µs 1.5 µs -1.97%
parse[real_large] 50.5 µs 46.5 µs +8.56%
parse[real_medium] 14.9 µs 14.5 µs +2.74%
parse[real_xlarge] 1.4 ms 1.3 ms +10.59%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing perf/decode-skip-prescan (4dabc46) with main (f9a6387)

Open in CodSpeed

Footnotes

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

@Boshen Boshen merged commit 55ebb8e into main May 26, 2026
8 checks passed
@Boshen Boshen deleted the perf/decode-skip-prescan branch May 26, 2026 05:23
@oxc-guard oxc-guard Bot mentioned this pull request May 26, 2026
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.

1 participant