Skip to content

perf: per-line gating for the line-anchored rules the literal prefilter can't touch #3072

Description

@squid-protocol

Follow-up to #3069 / PR #3070. Estimated ~4-8% end-to-end on rule-pass-bound repos, on top of #3070's 3-7%.

Why

The required-literal prefilter (#3070) eliminates 62% of rule sweeps corpus-wide, but the per-file most expensive rules are structurally ungateable — their patterns bottom out in operators and generic identifiers every file contains. Measured on a 7.6MB single-segment C header (node/deps/simdjson/simdjson.h), the whole C rule pass is 5.2s and the top ungated rules are:

rule finditer cost anchored?
state_mutation 382ms ✅ `(?:^
args 245ms ❌
pointers 238ms ❌
api 216ms partially (^typedef, ^(?!static…) branches)
func_start 176ms ✅ ^[ \t]*… (C/C++; TS's is unanchored)

A whole-segment substring gate can't help these. But the #3063 precedent ('=' in line before the var-decl backtracking regex, 8.2× on that pass, byte-identical results) generalizes to them as a per-line pass: split the segment into lines once, apply a cheap per-line membership test, and run the regex only on surviving lines.

Proposal

  1. state_mutation first — it is literally perf(detector): skip =-less lines before the var-decl backtracking regex #3063 again. Every alternative of the pattern requires =, ++, or --; a line containing none of '=', '++', '--' cannot match. It is anchored to line-start-or-delimiter, and its classes exclude \n, so per-line evaluation is equivalent (verify the (?![^\n(]{0,300},[ \t]*$) lookahead's $ semantics under re.M when matching line-by-line — likely needs the line passed with its trailing content semantics preserved, or finditer per line rather than .match).
  2. C/C++ func_start: ^[ \t]*-anchored, never spans newlines until the arg-list part — needs a hand-verified cheap line test (e.g. a line must contain ( to open an arg list).
  3. python/go api/globals/decorators: same shape, smaller wins.
  4. Where a rule is anchored but has no cheap universal line test, rule_prefilter.derive_literal_gate can be reused per line — the same one-of literal check against a 80-char line is ~free.

Per-rule opt-in via an explicit registry (e.g. _line_gates = {rule: test} alongside _scope_filters), NOT automatic derivation — each rule's "never spans a newline" and "line test is implied by every branch" claims must be individually argued and tested, exactly as #3063 did.

Correctness bar (same as #3070)

  • Extend tests/tools/gate_parity_audit.py with a per-line-gate leg: for every corpus file × line-gated rule, assert the per-line pass produces the identical match set (offsets included) to the whole-segment finditer.
  • Golden crucible both legs; keyword-rosetta 46/46 via verify_language.py --engine.
  • Micro-benchmark the per-line loop overhead: a Python-level line loop costs more per line than finditer costs per match on dense-hit rules, so each gated rule must show a measured win on hit-sparse and hit-dense files before shipping (the perf(detector): skip =-less lines before the var-decl backtracking regex #3063 var-decl loop set the pattern).

Estimate

state_mutation + func_start ≈ 11% of the C rule pass on the header micro-bench; the rule pass is roughly half of wall time on rule-bound repos (from #3070's A/B), and #3063-style gates historically recover most of a gated rule's cost on typical files. Hence ~4-8% end-to-end, concentrated on C/C++-heavy repos (curl, sokol-zig, node's vendored deps).

Minor extension while in there: positive-lookahead literal harvesting in derive_literal_gate (deliberately deferred in #3070) — (?=…literal…) content is required text; a handful more rules would gate.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature, sensor, or structural signatureperformance

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions