Skip to content

perf: skip redundant lookahead simulations in Condition dispatch and expression lists - #2827

Draft
fudianchn wants to merge 1 commit into
JSQLParser:masterfrom
fudianchn:perf/condition-lookahead
Draft

fudianchn wants to merge 1 commit into
JSQLParser:masterfrom
fudianchn:perf/condition-lookahead

Conversation

@fudianchn

Copy link
Copy Markdown
Contributor

AI disclosure: this change was prepared with AI coding agents.

What

Skip two redundant lookahead simulations on the hottest parse paths: the deep jj_2 phase simulation guarding the main branch of Condition() / SQLCondition(), and the Oracle/PostgresNamedFunctionParameter probes in ComplexExpressionList().

Why

The main branch of Condition() / SQLCondition() is the last alternative of its choice, so its lookahead can only accept or reject what the productions themselves accept or reject immediately after: entering it directly and letting them fail is equivalent, without re-walking the whole expression tree first. Under JFR profiling of parseSQLStatements this simulation collected about 17% of the execution samples, paid by every plain condition on top of the real parse.

The named-parameter probes can never match a dotted name (f(a.b => 1) fails to parse on master with or without this change), so a check of the second token decides them exactly like the 2-token scan did.

How

  • Condition() / SQLCondition(): gate the main alternative with LOOKAHEAD({ !interrupted }) instead of the implicit deep simulation; the EXISTS alternatives ahead of it and every inner lookahead are untouched.
  • ComplexExpressionList(): gate the four named-parameter alternatives with isNamedParameterAssignmentAhead(), a two-kind check on getToken(2).

Root cause

Not applicable (performance).

Testing

  • 26 probe statements covering the EXISTS forms (identifier usage, EXISTS (subquery), exists.col, exists (1), ternary ? EXISTS :arr :), named parameters (a => 1, a := 1, OUTER => 1) and dotted-name rejections: parse results and failures identical to master.
  • Full suite: 10,344 tests, the same 35 environment failures as master on my machine (33 Mockito initialization, 2 file access), zero new failures; AST dump of performance.sql byte-identical to master (54 statements).
  • Benchmark JSQLParserBenchmark.parseSQLStatements (JDK 17, 1 fork, 3x10s warmup, 10x1s, jars interleaved): 21.32-21.40 vs 24.58-24.83 ms/op against master over 3 pairs, error intervals not overlapping (-13% to -14%).
  • Not covered locally: the Mockito-based interruption test cannot run in my environment (it fails on master too); the !interrupted gate follows the existing LOOKAHEAD(n, { !interrupted }) convention used across the grammar. Malformed conditions can now fail inside the productions instead of at the choice dispatch; the failure class is unchanged.

Verification of the original issue

Not applicable (no issue).

…expression lists

The main branch of Condition/SQLCondition is the last alternative of its
choice: entering it and letting the productions reject is equivalent to the
deep jj_2 phase simulation that ran for every plain condition first, so gate
it with a cheap !interrupted check instead. The named-parameter alternatives
of ComplexExpressionList only ever match a single-token name directly
followed by their assignment token, so a first check on the second token is
equivalent to their 2-token lookahead scan.

parseSQLStatements: 21.3-21.4 vs 24.6-24.8 ms/op against master in 3
interleaved JMH pairs (-13% to -14%, error intervals not overlapping);
behavior verified identical by 26 EXISTS/named-parameter probes, the full
10,344-test suite (same 35 pre-existing environment failures as master) and
a byte-identical AST dump of performance.sql.

Signed-off-by: 付典 <fudianchn@gmail.com>

This branch has not been deployed

No deployments
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