Optimizing Parser Performance
Profile release-like parsing against representative valid, malformed, small, large, ASCII,
and Unicode corpora. Preserve grammar behavior, diagnostics, source locations, and resource
limits.
- Read the shared parser data-oriented design audit.
- Scan for copied token text, allocation per node/token, pointer-rich ASTs, scattered
passes, generic Unicode work in ASCII-heavy scans, and JS/native/Wasm crossings per item.
- Change one causal storage or scan-loop cost at a time; define ownership, offsets, and
overflow behavior before compacting data.
- Re-measure CPU, allocations, peak memory, startup, and throughput; test malformed and
deeply nested input alongside the common corpus.
Use optimizing-compiler-performance only for
a profile-proven codegen issue.
SIMD scan loops
A lexer scanning identifiers, strings, template bodies, or whitespace is the case that pays
off for hand SIMD — the optimizer will not autovectorize a data-dependent find-first-of-a-
byte-set scan, so reach for explicit SIMD or a memchr-family primitive. Portable SIMD is
runtime CPU dispatch; do not ship -C target-cpu=native or GOAMD64=v2|v3|v4 in a build
distributed to CPUs you do not control. Pin only to a floor a controlled target guarantees,
and record why, enforced by scripts/fleet/check/build-microarch-is-portable.mts.
- The kernel is compare-and-reduce. Load a 16/32-byte chunk, run the class compares,
OR the class masks, extract to a scalar bitmask, find the first boundary with a
count-trailing-zeros — or NOT then count for the first NON-member — then a scalar tail
handles the sub-stride remainder.
- Byte-identical or it does not ship. A SIMD scan must match its scalar reference
exactly: ship a SIMD-vs-scalar differential test plus an exhaustive delimiter-at-every-
offset-across-the-stride test, and validate end to end.
- The SIMD candidate byte set must match its OWN scalar path exactly. It may be a
SUPERSET of a sister port's set when this port carries extra semantics — e.g. a Go
readString scanning 0xE2 for U+2028/U+2029 handling that the Rust and C++ ports omit.
Correctness beats cross-port delimiter symmetry.
- Make it real and wire it in. An unrolled scalar loop with a per-byte call is NOT SIMD;
a SIMD helper with no non-test caller is dead code. A shipped SIMD function must be a real
vector kernel AND live on the hot path — prefer whatever the sister ports do, in lock-step.
- Measured payoff shape. The acorn-lang Go work landed 1.5-2.4x end-to-end lexer
throughput on string/template-dense JS, ~16x on isolated micro-scans, allocation-neutral.
Language mechanics: Rust,
Go, C++.
1---2name: optimizing-parser-performance3description: Audits parser and tokenizer performance.4---56# Optimizing Parser Performance78Profile release-like parsing against representative valid, malformed, small, large, ASCII,9and Unicode corpora. Preserve grammar behavior, diagnostics, source locations, and resource10limits.11121. Read the shared [parser data-oriented design audit](../optimizing-performance/references/parser-data-oriented-design.md).132. Scan for copied token text, allocation per node/token, pointer-rich ASTs, scattered14 passes, generic Unicode work in ASCII-heavy scans, and JS/native/Wasm crossings per item.153. Change one causal storage or scan-loop cost at a time; define ownership, offsets, and16 overflow behavior before compacting data.174. Re-measure CPU, allocations, peak memory, startup, and throughput; test malformed and18 deeply nested input alongside the common corpus.1920Use [optimizing-compiler-performance](../optimizing-compiler-performance/SKILL.md) only for21a profile-proven codegen issue.2223## SIMD scan loops2425A lexer scanning identifiers, strings, template bodies, or whitespace is the case that pays26off for hand SIMD — the optimizer will not autovectorize a data-dependent find-first-of-a-27byte-set scan, so reach for explicit SIMD or a `memchr`-family primitive. Portable SIMD is28runtime CPU dispatch; do not ship `-C target-cpu=native` or `GOAMD64=v2|v3|v4` in a build29distributed to CPUs you do not control. Pin only to a floor a controlled target guarantees,30and record why, enforced by `scripts/fleet/check/build-microarch-is-portable.mts`.3132- **The kernel is compare-and-reduce.** Load a 16/32-byte chunk, run the class compares,33 OR the class masks, extract to a scalar bitmask, find the first boundary with a34 count-trailing-zeros — or NOT then count for the first NON-member — then a scalar tail35 handles the sub-stride remainder.36- **Byte-identical or it does not ship.** A SIMD scan must match its scalar reference37 exactly: ship a SIMD-vs-scalar differential test plus an exhaustive delimiter-at-every-38 offset-across-the-stride test, and validate end to end.39- **The SIMD candidate byte set must match its OWN scalar path exactly.** It may be a40 SUPERSET of a sister port's set when this port carries extra semantics — e.g. a Go41 `readString` scanning `0xE2` for U+2028/U+2029 handling that the Rust and C++ ports omit.42 Correctness beats cross-port delimiter symmetry.43- **Make it real and wire it in.** An unrolled scalar loop with a per-byte call is NOT SIMD;44 a SIMD helper with no non-test caller is dead code. A shipped SIMD function must be a real45 vector kernel AND live on the hot path — prefer whatever the sister ports do, in lock-step.46- **Measured payoff shape.** The acorn-lang Go work landed 1.5-2.4x end-to-end lexer47 throughput on string/template-dense JS, ~16x on isolated micro-scans, allocation-neutral.48 Language mechanics: [Rust](../optimizing-rust-performance/SKILL.md),49 [Go](../optimizing-go-performance/SKILL.md), [C++](../optimizing-cpp-performance/SKILL.md).