Skip to content

perf: write the custom-rule host payload natively and decode facts lazily - #1135

Merged
kvlonge merged 3 commits into
mainfrom
perf/native-custom-rule-payload
Oct 18, 2026
Merged

kvlonge merged 3 commits into
mainfrom
perf/native-custom-rule-payload

Conversation

@kvlonge

@kvlonge kvlonge commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Why

Custom rules spent most of their time preparing data rather than evaluating rules: the main process built Python records for the whole project and pickled them into one payload before the host could start (about 3 s of main-thread work on the dense 10k project).

Changes

  • The custom-rule host payload is written natively as one file: model names, paths, keys, dependencies, plain config values, authored and expanded SQL, inferred and declared columns are stored without building Python objects; remaining sections are pickled per value.
  • The host reads the file before the hermeticity guard, parses only the header and decodes each fact lazily on first read. Fact views read through small protocols, so unread sections are never decoded.
  • Read tracking, rule cache keys, digests and evidence stay in the main process and are unchanged.
  • AGENTS.md: performance measurement lessons (instruction counts under load, verifying the built binary, critical paths of concurrent phases, verify-or-discard speculation).

Verification

  • Fact identity tests: every public fact decoded from the payload equals the full project value, for native and hand-built records.
  • New CLI tests (every-fact rule with cold/warm/edit cache counts, failing, crashing and slow rules) pass unchanged against the previous code.
  • Focused rule engine, rules, prepared-artifact and custom-rule E2E tests pass; 1k output identity holds.
  • Dense 10k, 4 paired cold runs: custom_rules_ms 7.3-9.5 s to 4.0-4.6 s, CPU 58.8-60.0 s to 56.3-56.7 s; wall gain is smaller because built-in rules now bound the rules phase.
  • Independent review: no blockers.
  • make check-ci passes.

…zily

Custom-rule hosts received the whole compiled project as Python records
pickled on the compiling thread, then unpickled every value before running
any rule. The payload is now written by native code: model names, paths,
keys, SQL text, config values, inferred columns and natively parsed header
columns are stored without building Python objects, and only values that
Python alone holds are pickled one by one. The host reads the header and
decodes each fact the first time a rule reads it, so a rule touching one
fact per model decodes only that fact.

Fact views read the host project through narrow protocols; collections and
declarations are read on first access. Read recording, cache keys and the
host process lifecycle are unchanged.
…e native payload

Rules reading every public fact report the compiled values cold and warm and rerun only the edited model's invocation; failing, host-exiting and slow rules keep their results, messages and payload cleanup.
@kvlonge
kvlonge enabled auto-merge (squash) October 18, 2026 02:01
@kvlonge
kvlonge merged commit c7817ab into main Oct 18, 2026
43 checks passed
@kvlonge
kvlonge deleted the perf/native-custom-rule-payload branch October 18, 2026 02:39
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