mirror of https://github.com/sveltejs/svelte
perf: optimize parser hot paths for ~18% faster compilation (#17811)
## 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()` — `slice` → `startsWith`**: 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_bracket` — `Array.includes` → `Set.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). Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>pull/17815/head
parent
dd5b587bd3
commit
efb651cd33
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
perf: optimize parser hot paths for faster compilation
|
||||
Loading…
Reference in new issue