Skip to content

perf: optimize parser hot paths for ~18% faster compilation#17811

Merged
Rich-Harris merged 1 commit intosveltejs:mainfrom
MathiasWP:perf/parser-hot-path-optimizations
Feb 26, 2026
Merged

perf: optimize parser hot paths for ~18% faster compilation#17811
Rich-Harris merged 1 commit intosveltejs:mainfrom
MathiasWP:perf/parser-hot-path-optimizations

Conversation

@MathiasWP
Copy link
Copy Markdown
Contributor

@MathiasWP MathiasWP commented Feb 26, 2026

Summary

Optimizes the Svelte compiler's parser phase — the hot path that runs on every file compilation during development (HMR). The changes are surgical micro-optimizations to 7 files in packages/svelte/src/compiler/phases/1-parse/, with no architectural changes.

Changes

  • Parser.match()slicestartsWith: Avoids creating a new substring on every multi-char match call (called thousands of times per compilation)
  • Parser.match_regex()slice → sticky flag: Uses lastIndex + y flag instead of slicing the entire template string on every regex match. All 14 regex patterns passed to match_regex/read across 4 files updated with y flag.
  • Parser.allow_whitespace() / require_whitespace() — regex → charCode: Replaces regex_whitespace.test() with numeric charCode comparisons in a tight loop (called hundreds of times per compilation). Covers the full \s character set with a fast path for common ASCII whitespace.
  • Text parser — string concatenation → slice: Replaced data += template[index++] loop (O(n²)) with tracking start position + single slice() call (O(n))
  • read_sequence — string concatenation → slice: Same pattern — derive raw from template.slice() in flush() instead of char-by-char concatenation
  • is_valid_element_name() — inline regexes → module-level constants: Extracted 2 regex literals to module scope
  • Dynamic new RegExp()indexOf: Replaced parser.read_until(new RegExp(...)) with template.indexOf() for script/style closing tags
  • match_bracketArray.includesSet.has: Pre-computes close bracket values as a Set for O(1) lookups

Benchmark

A benchmark script was written that:

  1. Compiles 5 real test components of varying complexity (a11y validators, form bindings, component migration output, rich selects)
  2. Compiles 2 synthetic stress-test templates (500 elements and 2000 elements with expressions and entities)
  3. Runs 50 warmup iterations for parse, 20 for compile (JIT warmup)
  4. Forces GC between benchmarks via --expose-gc
  5. Measures both parse-only and full compilation (parse + analyze + transform + codegen)

Parse-only results

Component Before After Speedup
a11y-role-supports-aria-props (371 lines) 2.410ms 2.129ms -11.7%
form-default-value-spread (199 lines) 1.726ms 1.360ms -21.2%
svelte-component (251 lines) 1.925ms 1.648ms -14.4%
select-with-rich-content (157 lines) 0.795ms 0.636ms -20.0%
rich-select (173 lines) 0.807ms 0.637ms -21.1%
synthetic-large (500 elements) 2.560ms 1.547ms -39.6%
synthetic-huge (2000 elements) 136.849ms 114.115ms -16.6%
Total 147.071ms 122.072ms -17.0%

Full compile results

Component Before After Speedup
a11y-role-supports-aria-props 25.407ms 21.990ms -13.4%
form-default-value-spread 10.711ms 9.422ms -12.0%
svelte-component 10.304ms 8.481ms -17.7%
select-with-rich-content 5.986ms 4.694ms -21.6%
rich-select 6.010ms 5.059ms -15.8%
synthetic-large (500 elements) 30.511ms 25.196ms -17.4%
synthetic-huge (2000 elements) 932.801ms 764.794ms -18.0%
Total 1021.729ms 839.637ms -17.8%

Benchmarked on macOS, Node.js, Apple Silicon. The parse improvements carry through to end-to-end compile time since the parser is a significant portion of the compilation pipeline. Text-heavy templates benefit the most (up to 40% faster parse).

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Feb 26, 2026

🦋 Changeset detected

Latest commit: 020a683

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

This PR includes changesets to release 1 package
Name Type
svelte Patch

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

@MathiasWP MathiasWP force-pushed the perf/parser-hot-path-optimizations branch from 0a90e01 to 1498be9 Compare February 26, 2026 16:04
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@MathiasWP MathiasWP force-pushed the perf/parser-hot-path-optimizations branch from 1498be9 to 020a683 Compare February 26, 2026 16:06
Copy link
Copy Markdown
Member

@Rich-Harris Rich-Harris left a comment

Choose a reason for hiding this comment

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

this is awesome!

@Rich-Harris Rich-Harris merged commit efb651c into sveltejs:main Feb 26, 2026
18 checks passed
@github-actions github-actions bot mentioned this pull request Feb 26, 2026
@MathiasWP
Copy link
Copy Markdown
Contributor Author

this is awesome!

I’ll do more investigation tomorrow, i have some findings that may be very interesting

Rich-Harris pushed a commit that referenced this pull request Feb 27, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## svelte@5.53.6

### Patch Changes

- perf: optimize parser hot paths for faster compilation
([#17811](#17811))

- fix: `SvelteMap` incorrectly handles keys with `undefined` values
([#17826](#17826))

- fix: SvelteURL `search` setter now returns the normalized value,
matching native URL behavior
([#17828](#17828))

- fix: visit synthetic value node during ssr
([#17824](#17824))

- fix: always case insensitive event handlers during ssr
([#17822](#17822))

- chore: more efficient effect scheduling
([#17808](#17808))

- perf: optimize compiler analysis phase
([#17823](#17823))

- fix: skip redundant batch.apply
([#17816](#17816))

- chore: null out current_batch before committing branches
([#17809](#17809))

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

2 participants