Skip to content

perf(rules): check custom-rule module state with one shared snapshot - #940

Merged
kvlonge merged 1 commit into
mainfrom
perf/custom-rule-state-checks
Oct 4, 2026
Merged

kvlonge merged 1 commit into
mainfrom
perf/custom-rule-state-checks

Conversation

@kvlonge

@kvlonge kvlonge commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Why

After every custom-rule call, the rule host re-fingerprinted the module state of every loaded rule code separately, walking all reachable globals each time. The cost grew with calls × enabled codes × state size: on a ~1,400-model project with 9 rules it dominated host CPU once the rules themselves were cheap (CHI-491).

Changes

  • ModuleStateMonitor takes one shared snapshot over the codes not yet stateful and compares it with the last snapshot at which every code matched its baseline. Any difference, unknown object or error falls back to the exact per-code fingerprint, which still decides classification.
  • The snapshot replays the traced decisions of the per-code walks and never touches or runs user code on objects those walks didn't visit; it is capped like the per-code walk.
  • Codes sharing a module are fingerprinted once; stateful codes are skipped. Both walkers share one dispatch, and module_state_token output is byte-identical.

Verification

  • CLI compile, 1,400 models × 9 rules, cache off: ~84 s → ~11 s CPU. Detector alone 17–24× faster.
  • Equality corpus (31 cases + 40 random mutation programs) against the old detector; real-CLI split-host test; relative performance guard.
  • One independent review plus follow-up; make test, make check-ci.

…per call

After every custom-rule call the host re-fingerprinted the module state of
every loaded rule code, walking all reachable globals once per code. Each
exact fingerprint walk now also traces what it touched: the identity and
type of every object it visited, the classes it descended into, and the
empty instances whose immutability it checked. One shared snapshot then
replays the union of those walks over every code still being monitored and
is compared with the snapshot taken when their fingerprints last matched.

The snapshot descends only where a traced walk descended and treats any
untraced object, any error, or more than the node bound per walk as a
reason to fall back, so it never touches or runs code on an object the
per-code walks did not. An equal snapshot proves no fingerprint changed;
otherwise every remaining code is fingerprinted exactly as before and the
trace is rebuilt. Codes sharing a module are fingerprinted once, and codes
already stateful are no longer checked. Both walkers share one structural
dispatch so they always take the same branch.

Classification is unchanged: equality tests compare the monitor with full
re-fingerprinting after every call over a corpus of stateful and stateless
rule modules, raising and side-effecting attribute hooks the fingerprints
never reach, and seeded random mutations, and a CLI test checks split hosts
report what one host reports.
@kvlonge
kvlonge enabled auto-merge (squash) October 4, 2026 11:07
@kvlonge
kvlonge merged commit 013e687 into main Oct 4, 2026
30 checks passed
@kvlonge
kvlonge deleted the perf/custom-rule-state-checks branch October 4, 2026 11:33
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