Repository navigation
perf(linter): allocation-free L007 keyword-case scan [W6-03] - #896
Conversation
L007 (keyword case consistency) built a []wordToken per line with a strings.Builder per word and upper-cased every word with strings.ToUpper before the keyword map lookup. It now scans words as substrings of the line (scanWords) and upper-cases ASCII words into a 16-byte stack buffer for the lookup; words with non-ASCII bytes keep the strings.ToUpper path so Unicode case folding is unchanged. Violations, positions and fixes are identical. Tests: a differential test against the previous implementation (kept in a test file as the oracle) over every statement and file in the real-world corpora plus Unicode, quoting, comment and invalid UTF-8 cases; allocation tests; a guard that every keyword fits the stack buffer. M4, GOGC=off -cpu 1, 10 interleaved rounds: L007 -48.8% to -53.5% per dialect (geomean -51.5%), allocs -88% to -94%; LintWithDialect allocs -12.1% to -15.4%, B/op -7% to -9%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
Claude finished @ajitpratap0's task in 26s —— View job Review of PR #896: allocation-free L007 keyword-case scanI read the diff of
Overall: This looks correct and well scoped. The oracle-based differential test over 3,002 statements plus edge cases is a strong safety net for a refactor like this. What I checked
Suggestions (non-blocking)
PerformanceThe reported −50% on L007 and −12–15% end-to-end allocs are plausible for removing the per-line slice, per-word builder and SecurityNo concerns. There is no unsafe code and no unbounded buffers. The stack buffer is length-checked before indexing. TestsCoverage is good: differential, allocation, bound and Unicode cases. It would be worth adding a fuzz test ( |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
Claude finished @ajitpratap0's task in 22s —— View job Review of PR #896: allocation-free L007 keyword-case scanI read the full diff of Verdict: This looks correct and well scoped. I found no blocking issues. Correctness
Suggestions (non-blocking)
PerformanceRemoving the per-line slice, the per-word builder and the SecurityNo concerns. There is no unsafe code, and the stack buffer is length-checked before indexing. TestsCoverage is strong. It includes a differential test against the old implementation over 3,002 statements plus edge cases, allocation tests, and the keyword length-bound check. |
Perf gate: PASS (shadow mode: report only, this check never fails)Base Runner CPU:
Rules: each arm runs 20 randomized code layouts ( Gated suite (GOGC=off, GOMAXPROCS=1)
A/A' control (base revision, disjoint layout seeds): 0 of 52 benchmarks moved more than 3%; 0 would have been flagged by the time rule; suite geomean -0.16%, p 0.640. Low power4 benchmark(s) have under 80% estimated power to be flagged on their own for a +5% slowdown (their spread across layouts is too wide for 20 layouts at the Benjamini-Hochberg level alpha/m = 0.000192). A slowdown below the last column usually goes unflagged there; instructions/op, allocs/op and the suite geomean still guard them.
Power is estimated with the normal approximation for the U test (Noether): power = Phi(ln(1+shift)/s * sqrt(K/2) * sqrt(3/pi) - z(1 - alpha/(2m))), with s the spread and K the layouts per arm. Instructions per operation (cmd/perfdriver)
Secondary metric: default GC, GOMAXPROCS=1 (informational)
Environmentbenchstat (primary metric, samples pooled across layouts: informational, the verdict uses the per-layout test) |
…nt slow path [W6-03] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
Claude finished @ajitpratap0's task in 26s —— View job Review of PR #896: allocation-free L007 keyword-case scanI read the diff of Verdict: No blocking issues. The change is correct and well scoped. Correctness
Suggestions (non-blocking)
PerformanceThe deterministic numbers are the trustworthy ones: -81% to -90% B/op and -88% to -94% allocs/op on L007, and -12% to -15% end-to-end allocs. The time claim comes only from the earlier quiet run. The CI perf gate shows no parser regression, but its suite has no lint benchmarks, so it does not measure the L007 gain. Adding SecurityNo concerns. There is no unsafe code, and the stack buffer is length-checked before indexing. TestsCoverage is strong: a differential test against the previous implementation (kept as an oracle), allocation tests for |
…W6-03] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
Claude finished @ajitpratap0's task in 19s —— View job Review of PR #896: allocation-free L007 keyword-case scanI read the Verdict: No blocking issues. The change is correct and well scoped. Correctness
Suggestions (non-blocking)
PerformanceThe deterministic numbers hold up: -81% to -90% B/op and -88% to -94% allocs/op on L007, and -12% to -15% end-to-end allocs. The amd64 time gain is still unconfirmed. The shadow perf gate passed, but its suite has no lint benchmarks. It shows the parser path doesn't regress and says nothing about L007. Per the PR notes, add SecurityNo concerns. There is no unsafe code, and the stack buffer is length-checked before indexing. TestsCoverage is strong:
|
#896 (L007), #902 (L010) and #905 (lazy error wrap) target benchmarks the PR-run gate never measured, so none of them could be accepted or closed on an amd64 verdict. long_prefix is included so #905's expected miss is still checked for regressions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
…w suite (#927) * ci(perf): add BenchmarkLintRW to the shadow gate suite PR #906's gate run moved 0 of 26 benchmarks because no linter benchmark was gated. Add it so W6-05b, W6-06 and W6-07 are measured by the PR-run gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2 * ci(perf): gate LintRule L007/L010 and ParseError in the shadow suite #896 (L007), #902 (L010) and #905 (lazy error wrap) target benchmarks the PR-run gate never measured, so none of them could be accepted or closed on an amd64 verdict. long_prefix is included so #905's expected miss is still checked for regressions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Resolve CHANGELOG (and gate-suite) conflicts by placing this PR's entries in main's Unreleased sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
Claude finished @ajitpratap0's task in 15s —— View job Review of PR #896: allocation-free L007 keyword-case scanI read the diff of Verdict: No blocking issues. The change is correct and well scoped. Correctness
Suggestions (non-blocking)
PerformanceThe deterministic numbers hold up: allocs/op on L007 drops from 72565 to 1194, and end-to-end allocs drop about 12%. The amd64 time gain is still unconfirmed. The shadow perf gate passed, but its suite has no lint benchmarks, so it shows no parser regression and says nothing about L007. As the PR notes, run the gate with SecurityNo concerns. There is no unsafe code, and the stack buffer is length-checked before indexing. TestsCoverage is strong:
|
|
Holding for re-gate under the layout-randomized perf gate (same policy as #931). The REGRESSION verdict is |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
|
Claude finished @ajitpratap0's task in 16s —— View job Review of PR #896: allocation-free L007 keyword-case scanI read the diff of Verdict: No blocking issues. The change is correct and well scoped. Correctness
Perf gate noteThe The L007 result is strong in the same run: -64.8% time, 72565 → 1194 allocs/op, and LintRW -6.6% to -10.4% across all 8 dialects. This also covers the amd64 time confirmation that was outstanding. Once the re-gate passes, that item can be closed. Suggestions (non-blocking)
SecurityNo concerns. There is no unsafe code, and the stack buffer is length-checked before indexing. TestsCoverage is strong:
|
|
Claude finished @ajitpratap0's task in 22s —— View job Review of PR #896: allocation-free L007 keyword-case scanI read the diff of Verdict: No blocking issues. The change is correct and well scoped. Correctness
Suggestions (non-blocking)
PerformanceThe latest perf gate run (base
SecurityNo concerns. There is no unsafe code, and the stack buffer is length-checked before indexing. TestsCoverage is strong:
|
|
Claude finished @ajitpratap0's task in 19s —— View job Review of PR #896: allocation-free L007 keyword-case scanI read the diff of Verdict: No blocking issues. The change is correct and well scoped. Correctness
Suggestions (non-blocking)
PerformanceThe latest layout-randomized amd64 gate (head
This closes the outstanding amd64 time confirmation. An earlier comment on this thread reported a later L010 +20.2% regression at head SecurityNo concerns. There is no unsafe code, and the stack buffer is length-checked before indexing. TestsCoverage is strong:
|
Summary
Wave-1 task W6-03 from the 2026-11 performance audit (LINT-2). The prototype was confirmed by an independent verifier; Codex ported it, and Opus reviewed the result and ran the full gate.
L007 (keyword case consistency) built a
[]wordTokenper line, used astrings.Builderper word, and calledstrings.ToUpperon every word before the keyword lookup. Now:scanWordspasses each word to the rule as a substring of the line, with its 1-based byte column. It allocates nothing.isSQLKeywordupper-cases ASCII words into a 16-byte stack buffer before the map lookup.strings.ToUpperpath, so Unicode case folding (e.g.ſ→S) behaves as before.TestKeywordLengthBoundfails if any keyword ever exceeds the buffer.Fixoutput are unchanged.Merged
origin/main(wave 0: goldens, benchmark matrix, perf gate in shadow mode) into the branch without conflicts. The W0-02 goldens pass (task check); no golden changed. The private benchmark file was removed; measurements use the canonical W0-06 benchmarks on main (./pkg/linter ^BenchmarkLintRule$/^L007$,^BenchmarkLintRW$).Test Plan
Differential test (
TestKeywordCaseDifferential). It compares against the previous implementation, kept inkeyword_case_oracle_test.goas the oracle, and requires identical violations: rule, line, column, message and suggestion. Inputs are every statement and every whole file intestdata/realworld/*andtestdata/corpus/*(3,002 statements), plus mixed case, Unicode identifiers, quoted identifiers, comments, string literals and invalid UTF-8.Allocation tests.
TestScanWordsNoAllocationschecks thatscanWordsallocates nothing.TestKeywordCaseCleanASCIIHasNoAllocationschecks that an ASCII line with no violations makes zero allocations.Gate, run outside the Codex sandbox:
go run honnef.co/go/tools/cmd/staticcheck@latest ./pkg/linter/...go test -race -count=1 ./pkg/linter/...task checktask test:cbindinggo test -count=1 ./pkg/compatibility/...Local measurement, canonical benchmarks (Apple M4, shared machine, indicative only):
BenchmarkLintRule/L00772565 -> 1194 allocs/op (-98.4%), 3085041 -> 95208 B/op (-96.9%); time -55% to -83% across 4 interleaved-cpu 1rounds (noisy).BenchmarkLintRWallocs/op: postgresql 60916 -> 53220 (-12.6%), mysql 73216 -> 64136 (-12.4%). Plan targets (L007 <= -45% time, end-to-end allocs <= -10%) met locally.CI perf gate, layout-randomized (amd64 Xeon 6975P-C, head 9659ccb merged into base 6399dc0; 20 randomized layouts per arm, per-layout Mann-Whitney U, Benjamini-Hochberg, A/A' control): PASS. 11 benchmarks faster by 3% or more, no gated time regression, allocs/op not up, B/op not up. A/A' control clean (0 of 52 moved >3%, suite geomean +0.05%, p 0.862).
LintRule/L007(target)LintRule/L010The earlier LintRule/L010 +11.9% flag did not reproduce under the layout-randomized gate (L010 is untouched by this PR; it was a code-layout artifact). Low-power section lists 2 benchmarks (see the CI comment); neither is L007 or L010.
Breaking Changes
None. The exported API and the rule's behaviour are unchanged.
🤖 Generated with Claude Code
https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
Review follow-up (fixer)
select'x'from"t"where"a"='b',SELECT"col"FROM t,select`a`from`b`); the previously surviving mutation (dropping the emit before an opening quote) now fails the suite.isSQLKeywordas defensive.task check,task test:cbinding,go test -count=1 ./pkg/compatibility/..., staticcheck on./pkg/linter/...all pass.keyword_case_bench_test.go(duplicated main's canonical benchmarks). Still outstanding (orchestrator action): authoritative amd64 A/B ofBenchmarkLintRule/L007andBenchmarkLintRW(dispatch Perf Gate with a scratch head adding those two lines to gate-suite.txt); paste the table here. Do not merge before this verdict is pasted here and the pending Race Detector / Test (ubuntu) checks are green.pkg/linter/rules/keywordsvs PLAN 2.2 linter package) to be reconciled when the W0 lint matrix lands.