Skip to content

Speed up parsing by avoiding keyword and token allocations - #318

Closed
git-hulk wants to merge 1 commit into
masterfrom
perf/lexer-peek-cache-and-keyword-lookup
Closed

Speed up parsing by avoiding keyword and token allocations#318
git-hulk wants to merge 1 commit into
masterfrom
perf/lexer-peek-cache-and-keyword-lookup

Conversation

@git-hulk

@git-hulk git-hulk commented Sep 2, 2026

Copy link
Copy Markdown
Member

Profiling the large PostHog queries showed the parser was bound by
allocations rather than by grammar work. The clause-starter lookahead
peeked up to 14 times per select item and each peek lexed the next
token from scratch. Every unquoted identifier also paid a
strings.ToUpper for the keyword lookup, every token was a separate heap
object, and the precedence lookup that runs after each primary
expression chained ~25 sequential match calls.

  • Peek once in the clause-starter check and compare the token against
    the keyword list, instead of peeking once per keyword.

  • Add lookupFold/containsFold, which upper-case into a stack buffer
    and index the map with string(buf) so no copy is allocated. Use it
    for keyword detection, reserved-keyword checks, interval units and
    keyword-argument function names. Token.ToString returns the interned
    canonical keyword instead of a fresh upper-cased copy.

  • Carve tokens from a slab sized by the remaining input, so tiny
    statements do not pay for a large chunk.

  • Switch getNextPrecedence on the token kind once, let matchKeyword
    skip the variadic matchTokenKind, and add an ASCII fast path to
    skipSpace.

Results on an Apple M-series laptop (see docs/benchmarks.md):

posthog_huge_0 5.4 ms / 59k allocs -> 2.5 ms / 14.9k allocs
posthog_huge_1 4.7 ms / 50k allocs -> 2.0 ms / 12.9k allocs
window_function 57 us / 595 allocs -> 20 us / 101 allocs

No public API changes. Golden AST and format fixtures are unchanged.

🤖 Generated with Claude Code

Profiling the large PostHog queries showed the parser was bound by
allocations rather than by grammar work. About 60% of allocated bytes
came from peekToken: the clause-starter lookahead peeked up to 14 times
per select item and each peek lexed the next token from scratch. Every
unquoted identifier also paid a strings.ToUpper for the keyword lookup,
every token was a separate heap object, and the precedence lookup that
runs after each primary expression chained ~25 sequential match calls.

  - Cache the last peekToken transition in the lexer, keyed on the full
    lexer state (offset and previous token), so repeated peeks and the
    following consumeToken reuse it. Backtracking to a saved state hits
    the cache again and unary-minus disambiguation stays exact.

  - Add lookupFold/containsFold, which upper-case into a stack buffer
    and index the map with string(buf) so no copy is allocated. Use it
    for keyword detection, reserved-keyword checks, interval units and
    keyword-argument function names. Token.ToString returns the interned
    canonical keyword instead of a fresh upper-cased copy.

  - Carve tokens from a slab sized by the remaining input, so tiny
    statements do not pay for a large chunk.

  - Switch getNextPrecedence on the token kind once, let matchKeyword
    skip the variadic matchTokenKind, peek once in the clause-starter
    check, and add an ASCII fast path to skipSpace.

Results on an Apple M-series laptop (see docs/benchmarks.md):

  posthog_huge_0   5.4 ms / 59k allocs  ->  1.9 ms / 14.5k allocs
  posthog_huge_1   4.7 ms / 50k allocs  ->  1.6 ms / 12.6k allocs
  window_function   57 us / 595 allocs  ->   15 us /   93 allocs

No public API changes. Golden AST and format fixtures are unchanged.

Assistant By Claude Fable 5.1
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T13:40:51.162318Z 5c3bba7 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@git-hulk
git-hulk marked this pull request as draft September 2, 2026 13:38
@git-hulk git-hulk closed this Sep 2, 2026
@git-hulk git-hulk changed the title Speed up parsing by caching peeks and avoiding keyword allocations Speed up parsing by avoiding keyword and token allocations Sep 3, 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