8000 perf: push down not/not-join clauses and short-circuit or/or-join branches by adityamukho · Pull Request #344 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content

perf: push down not/not-join clauses and short-circuit or/or-join branches - #344

Merged
adityamukho merged 3 commits into
mainfrom
worktree-or-notjoin-perf-250-248
Aug 24, 2026
Merged

adityamukho merged 3 commits into
mainfrom
worktree-or-notjoin-perf-250-248

Conversation

@adityamukho
Copy link
Copy Markdown
Collaborator

Summary

  • Closes perf: push-down not/not-join clauses to earliest valid position #248: optimizer::plan() now places Not/NotJoin clauses at the earliest position where their required variables are bound by preceding patterns, instead of always deferring them to a post-filter after every pattern (and after Or/OrJoin). A clause that can only be satisfied via an Or/OrJoin binding is still deferred, since plan() has no visibility into those clauses — this preserves correctness for that case exactly as before.
  • Closes perf: short-circuit or/or-join branch evaluation using cost model #250: apply_or_clauses now short-circuits branch evaluation once the branches evaluated so far already cover every incoming binding. Always safe for or-join (branches are projected onto already-bound outer keys before merging, so a skipped branch could only re-derive an existing row). For plain or, only safe when no evaluated branch has introduced a variable beyond the incoming scope — otherwise a later branch could legitimately bind a new variable differently for an already-covered key, so that case still evaluates every branch.
  • The rewrite also shrinks the two large not-filter blocks in executor.rs into small, reusable per-clause helper functions used for both the inline (pushed-down) and deferred (post-filter) application paths.

Test plan

  • cargo test — full suite green (688 unit tests + all integration suites)
  • cargo clippy --all-features -- -D warnings — clean
  • cargo fmt --check — clean
  • New unit tests in optimizer.rs covering not/not-join push-down positioning and deferral (unbound vars, wildcard ?_ join vars, zero-required-vars placement)
  • New integration tests in tests/disjunction_test.rs covering or/or-join short-circuit correctness, including a call-counting UDF predicate proving the expensive branch is actually skipped (not just that results happen to be correct), a test proving skip relies on genuine branch_cost-based reordering rather than declaration order, and a correctness guard proving plain or never skips branches that introduce new variables
  • Independent code review pass (correctness of both short-circuit safety arguments verified against parser invariants; caught and fixed one vacuous test before merge)
  • Benchmarks: negation/not_pushdown_selectivity shows up to ~55% faster at high exclusion rates; disjunction/or_short_circuit and disjunction/or_join_short_circuit show ~11% and ~27% improvement on fully-redundant branches

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
https://claude.ai/code/session_01YMWSutMLJJ7Y5b8hs9ZaCU

adityamukho and others added 2 commits August 24, 2026 09:46
…nches

#248: optimizer::plan() now places Not/NotJoin at the earliest position
where their required variables are bound by preceding patterns, instead
of always deferring them to a post-filter after every pattern (and after
Or/OrJoin). A clause that can only be satisfied via an Or/OrJoin binding
is still deferred, since plan() has no visibility into those clauses.
This shrinks the executor's two large not-filter blocks into small
per-clause helper calls reused for both the inline and deferred paths.

#250: apply_or_clauses now short-circuits once evaluated branches already
cover every incoming binding. Always safe for or-join (branches are
projected to already-bound outer keys before merging, so a skipped
branch could only re-derive an existing row). For plain or, only safe
when no evaluated branch has introduced a variable beyond the incoming
scope, since a later branch legitimately could bind a new variable
differently for an already-covered key otherwise.

Benchmarks: negation/not_pushdown_selectivity shows up to ~55% faster at
high exclusion rates; disjunction/or_short_circuit and
disjunction/or_join_short_circuit show ~11% and ~27% improvement on
fully-redundant branches.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMWSutMLJJ7Y5b8hs9ZaCU
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMWSutMLJJ7Y5b8hs9ZaCU
@codecov
codecov Bot commented Aug 24, 2026 •
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.85906% with 63 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/query/datalog/executor.rs 77.04% 59 Missing ⚠️
src/query/datalog/optimizer.rs 90.24% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

…sh-down

Two nits from ultrareview on PR #344:

- Not/NotJoin clauses landing at the same insertion point came out in
  reverse cost order, since each result.insert(pos, ..) shifts an
  already-inserted clause one slot later. Sorting descending by cost
  before inserting corrects this (the insertion reversal then produces
  the intended ascending/cheapest-first order).
- The Not arm's required-variable computation didn't exempt `?_`-prefixed
  wildcard vars, unlike the NotJoin arm and the parser's own
  outer_vars_from_clause. A `(not [?e :attr ?_x])` body was deferred to
  the post-filter as if ?_x needed to be bound by an outer clause, even
  though the wildcard convention means it never does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMWSutMLJJ7Y5b8hs9ZaCU
@adityamukho
adityamukho merged commit 60367af into main Aug 24, 2026
17 checks passed
@adityamukho
adityamukho deleted the worktree-or-notjoin-perf-250-248 branch August 24, 2026 04:52
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.

perf: short-circuit or/or-join branch evaluation using cost model perf: push-down not/not-join clauses to earliest valid position

1 participant

0