perf(lexer): dispatch separators by character - #727
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The changes are localized, preserve existing token shapes/ordering, and are backed by a targeted regression test covering all stream split positions for the updated separator logic.
Pull request overview
This PR optimizes the lexer’s separator scanning by dispatching on the next character (space/tab/newline/comment) instead of attempting multiple separator regexes before each token, aiming to reduce overhead on the hot path while preserving existing token emission behavior.
Changes:
- Replaces the separator-handling loop in
src/N3Lexer.jswith a character-dispatch implementation usingSPACE/TAB/LF/CR/HASHconstants and a_commentLineregex for full comment lines. - Adds a regression test that exercises all possible stream split points around mixed separators and comment lines, verifying stable token output with
commentsboth enabled and disabled.
File summaries
| File | Description |
|---|---|
| src/N3Lexer.js | Reworks separator consumption to use character dispatch (with a comment-line regex) to reduce repeated regex work in the main tokenization loop. |
| test/N3Lexer-test.js | Adds coverage ensuring separators/comments behave consistently across arbitrary stream chunk splits. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
jeswr
force-pushed
the
codex/perf-lexer-separator-dispatch
branch
from
September 5, 2026 23:01
a895a11 to
2db4209
Compare
Contributor
|
🎉 This PR is included in version 2.7.10 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Avoid three separator regex attempts before every token by dispatching on the next character. Single spaces/tabs and line endings use direct checks; longer horizontal runs retain regex scanning. Complete comment lines are consumed in one match, and unfinished comments remain buffered until their line ending or EOF arrives.
The remaining
_whitespace.exec()and_commentLine.exec()calls intentionally scan runs in native regex code. Comparing direct JavaScript loops with this implementation improved some short runs, but screening found about 19% more parser CPU for 128-character indentation and 31% more for 512-character comments. A bounded short-whitespace loop improved lexer-only timing in seven-round confirmations without establishing an end-to-end parser gain, so the simpler scanner is retained.This extracts the general separator optimization from #721. Named
SPACE,TAB,LF,CR, andHASHconstants, explicitseparatorLength, and a_commentLinepattern make the hot path readable while keeping each operation local. The scanner preserves main's existing token objects, callback timing, and line-mode behavior; the coordinate and split-CRLF corrections belong to #721.Merge this PR before #721, then rebase #721 to combine its range bookkeeping with this scanner. The PRs both target main and touch the same separator block; their combined implementation has already passed the range tests and generated coordinate checks.
Validation: all 6,898 tests pass with 100% statement, branch, function, and line coverage, plus ESLint and Node/browser IIFE/ESM builds. Differential checks against main cover 3,600 generated documents, 29,220 two-chunk splits, and one-character chunks, including complete token objects, errors, and callback boundaries. The combined scanner and #721 range fixes pass 6,915 tests with 100% coverage, plus 5,654 independent coordinate checks, 2,231 streaming comparisons, and 28 lexer-reuse pairs.
Performance compares the published source with main
4607e09using production Babel builds on macOS arm64 / Node 25.1.0. Seven paired rounds rotate fresh-process order, with 36 warmup and 36 measured iterations over 8,000-triple documents; streamed lexers use fresh instances and 64 KiB chunks. Full token/quad digests agree. Values are median paired CPU changes with bootstrap 95% intervals; negative means less CPU.The comments-off streamed result is inconclusive; it does not establish an improvement or regression. These synthetic timings are sensitive to runtime warmup and workload and are not application throughput guarantees. The earlier comment-body scanner showed a comment-heavy parser slowdown in one run; consuming each complete comment line in one match removed that observed slowdown in the final confirmation.
Additional seven-round checks (24 warmup and measured iterations):
The current branch is rebased on main
e4e2148, retaining the merged #726 direction-marker optimization. The benchmark tables above describe the original comparison against4607e09; they have not been relabeled as measurements of the new base. The rebased source passes the full test suite at 100% coverage, lint, Node/browser builds, and all GitHub checks.