Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
992e769
Engine: Price a Card-Space Collection Leaf's Compose Build Separately…
jbylund Aug 23, 2026
dc3587f
Engine: Expose collection_broadcast_printings to the Python Binding
jbylund Aug 28, 2026
34466eb
Engine: Fix Two Printing/Card-Space Precision Bugs in compose_printin…
jbylund Aug 28, 2026
42c434b
Engine: Give ComposeEstimate Real card/artwork Slots Instead of One O…
jbylund Aug 28, 2026
b52a1f6
Engine: Make ComposeEstimate's result/candidate Real {printing, card,…
jbylund Aug 29, 2026
c238d1a
Engine: Fix scan_units's Card-Mode Over-Counting, the Actual #1 Cost-…
jbylund Aug 29, 2026
97dc30c
Engine: Price Card-Invariant Bare Leaves At Their True One-Printing S…
jbylund Aug 29, 2026
95410a2
Docs: Extract GatheredScan/card Item Into Its Own Iteration Ledger
jbylund Aug 29, 2026
f3f4a01
Engine: Price the Printing-Varying GatheredScan Fallback by Match Den…
jbylund Aug 29, 2026
ef78a98
Docs: Log Round 2's Rejected domain_cards Independence-Product Attempt
jbylund Aug 29, 2026
31c5a4f
Engine: A Second Clustering-Bias Constant for Cross-Index Range Ands
jbylund Aug 29, 2026
8ab0b4c
Engine: A Downward Scan-Units Scale for the Broad Cross-Index Range-A…
jbylund Aug 29, 2026
068003c
Docs: Round 5 Diagnostic -- Re-Bucket GatheredScan/card Error by AST …
jbylund Aug 29, 2026
ce86033
Engine: A Downward Scan-Units Scale for the Bare Range Broad Guard
jbylund Aug 29, 2026
e1c4046
Engine: A Broad-Guard Scale for PrintingCompose's Own Bare/Fused Rang…
jbylund Aug 29, 2026
dbe63ba
Docs: Round 8 Diagnostic -- Bucket Candidates-Acquire GatheredScan/ca…
jbylund Aug 29, 2026
58eebfd
Engine: A Lower Fixed Cost for GatheredScan's Zero-Match Candidates R…
jbylund Aug 29, 2026
d9377f3
Docs: Record the FAIL-to-PASS Milestone After Round 9
jbylund Aug 29, 2026
f9b5f2a
Docs: Round 11 -- Re-Verify #852 Against the Printing-Varying-Depth Loop
jbylund Aug 29, 2026
e8b7fbc
Docs: Round 12 -- PrintingCompose's Plane-Acquire Miscosting Is Prova…
jbylund Aug 29, 2026
ce3772d
Docs: Round 13 -- PrintingCompose Never Beats PlanePopcountOrder Unde…
jbylund Aug 29, 2026
726870c
Docs: Close Round 13's Limit-Ceiling Hedge -- 1,000,000 Is the Real M…
jbylund Aug 29, 2026
708d2c1
Docs: Close the Last Gap in Round 13 -- PlanePopcountOrder Is Always …
jbylund Aug 29, 2026
e7f8c1f
Docs: GatheredScan Under-Costed for cmc-Range + Existential-Leaf Ands
jbylund Aug 30, 2026
363dd71
Engine: Scope the PrintingCompose-Acquire Mode::Card Verify Bypass Of…
jbylund Aug 30, 2026
41d3cfb
Engine: Generalize the Compose-Acquire Verify-Tier Bypass Off Field I…
jbylund Aug 30, 2026
365f9f3
Docs: Round 17 -- Existential Depth Fix Attempted, Discarded
jbylund Aug 30, 2026
0dbe656
Docs: Card-vs-Printing Property Classifier Unification -- Investigation
jbylund Aug 30, 2026
ec1ef11
Merge costcell/trunk (Round 17) into Round 18's branch before integra…
jbylund Aug 30, 2026
0ec4bd6
Docs: Round 19 -- Compound-Plane Leaf-Count Fix Attempted, Discarded
jbylund Aug 30, 2026
bb5798b
Docs: Round 20 -- Joint Floor/Leaf-Rate Refit Blocked by a Domain-Est…
jbylund Aug 30, 2026
cc17e03
Docs: Round 21 -- Eval-Domain Existential-Arith-And Root Cause Found
jbylund Aug 30, 2026
188a0ee
Engine: Fix best_other Gate for Lone-Existential-Leaf AND (Round 22)
jbylund Aug 30, 2026
68f2cd7
Docs: Round 23 -- Cheap Estimate Investigated and Rejected, Exact Dis…
jbylund Aug 31, 2026
eab2b3f
Engine: Extend PairTotals to cmc/power/toughness (Round 24)
jbylund Aug 31, 2026
f5aed0a
Docs: Round 25 -- Domain Confound Cleared, Joint Refit Retested on Cl…
jbylund Aug 31, 2026
ddba298
Docs: Round 26 -- Whole-Engine Cost Model State, Ranked by Real Regre…
jbylund Aug 31, 2026
865fb03
Docs: Round 27 -- Final Head-to-Head Against Main, Aggregate Verdict
jbylund Aug 31, 2026
288402a
Engine: Scope Broad-Guard scan_units Scales to Mode::Card (Round 28)
jbylund Aug 31, 2026
4e101d7
Docs: Round 29 -- Post-Fix Comprehensive State Check
jbylund Aug 31, 2026
9668dfa
Engine: Price StreamedSelect's Small-Total Redo Pass (Round 30)
jbylund Aug 31, 2026
a677e24
Engine: Ground StreamedSelect's Redo Bias in a Real Counter (Round 31)
jbylund Aug 31, 2026
4d6db48
Engine: Bound StreamedSelect's Permutation-Walk Estimate to the Sort-…
jbylund Aug 31, 2026
d8bc623
Engine: Set:X + Collector-Number Range Density Estimate (Round 33)
jbylund Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 84 additions & 13 deletions card_engine/src/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,20 @@ pub(crate) struct PlanFeatures {
pub limit: u32,
/// Page offset.
pub offset: u32,
/// The permutation segment `StreamedSelect`'s emission walk is actually bounded to —
/// `walk_bounds(...).len()` for `(sort_col, descending)`, computed once at acquire from the SAME
/// inputs the executor uses (`QueryParams::sort_bound`, the filter's own interval on the sort
/// column). `n_cards` when the filter constrains nothing about the sort column, and also when no
/// permutation exists for this `(sort_col, descending)` pair — `StreamedSelect` is inapplicable
/// there and never reads this field, but `mk_plan_feats` sets it uniformly for every acquire
/// branch (the shared feats have to cost a competing `StreamedSelect` honestly regardless of which
/// branch produced them), so the fallback must still be a value, not an absent one.
///
/// Round 32 of the printing-varying-depth ledger
/// (docs/issues/local-engine-gathered-scan-card-printing-varying-depth.md): `perm_steps` used to
/// multiply by `n_cards` unconditionally, which is right only for the unbounded case. See
/// `perm_steps`'s own doc for the regrade this field closes.
pub perm_walk_span: u32,
/// Printings the legality **broadcast-down** synthesizes (card ∃-plane → printing bitmap) in
/// `PrintingCompose`. `0` for border/rarity (precomputed planes) and for bare ranges (no broadcast).
/// Costed at `LINEAR_PASS_PER_PRINTING_NS`.
Expand Down Expand Up @@ -210,6 +224,11 @@ pub(crate) struct PlanFeatures {
/// in ~`page_span/selectivity` steps), while the permutation-free gather visits every match — so
/// the formula branches on this rather than assuming one. Ignored by every other plan.
pub compose_paging: super::ComposePaging,
/// Printings a CARD-SPACE collection leaf's build broadcasts (`ids_of` +
/// `broadcast_card_ids_to_printings`). See `ComposeEstimate::collection_broadcast`'s doc for why
/// this is not just folded into `scatter_printings`. `0` for everything except `PrintingCompose`
/// on a card-space `subtypes`/`keywords`/`oracle_tags` leaf.
pub collection_broadcast_printings: u32,
}

// ─── P1: PrintingRangeScan ──────────────────────────────────────────────────
Expand Down Expand Up @@ -610,6 +629,34 @@ const GATHER_COLLECT_PER_PAGE_ROW_NS: f64 = 9.79;
/// and drift as either query sizes or the corpus change. The fix is a term for the curvature -- see the
/// corpus-size note in `bench_gather_loop` -- not a smaller constant.
const GATHER_FIXED_COST_NS: f64 = 169.6;
/// `GATHER_FIXED_COST_NS`'s own value when `matches == 0` -- a `Prep::Candidates`-acquired zero-match
/// round, where every other term in this arm is already provably zero (`eval_domain`, `scan_units`,
/// `page_span`/`page_rows`, `artwork_seen_printings` all vanish with the candidate list itself), so
/// the whole prediction collapses to this one constant alone. `GATHER_FIXED_COST_NS` was fit against
/// the general population and reads 169.6 there; a zero-candidate round pays none of the loop/verify
/// work that constant was priced to cover, so charging it here is a straight ~4x over-charge, not a
/// rounding difference.
///
/// Fit as the calibration half's median measured `plan_self_ns` (not a mean, and not per-mode --
/// `PlanFeatures` carries no `unique`/mode field this arm can read, so one pooled constant is what
/// this branch can express; see the doc issue's Round 9 section for the residual mode split this
/// leaves on the table for card/printing vs. artwork). 9,890 sampled `GatheredScan`/`candidates`
/// zero-match rows (31.9% of the sampled `candidates` population), hash-of-query split:
///
/// calibration (n=4,944): median measured_ns = 42.0 -> this constant
/// held-out (n=4,946): 4,577 improved / 369 regressed / 0 tied
/// total abs ns error 530,256 -> 103,110 (5.1x)
/// median ratio (measured/predicted) 0.248 -> 1.000
/// within-25% 0.1% -> 57.7%
///
/// The held-out gain is not uniform across mode: card/printing land almost exactly on 1.00 (83-90%
/// within 25%), while artwork's real zero-match cost reads a flat ~2x higher (84ns vs. card/printing's
/// ~42ns -- plausibly `exec_gathered_scan`'s unconditional per-printing dedupe check setup, per its own
/// comment on `artwork_seen_printings` above), so artwork's ratio moves from 0.495 (over-cost) to 2.0
/// (under-cost) -- roughly the same LOG-ratio magnitude, just flipped sign, and still a net win on
/// absolute ns error (|84-169.6| = 85.6 -> |84-42| = 42.0). Splitting this properly by mode needs a
/// `PlanFeatures` field this arm does not have; out of scope for a `cost.rs`-only round.
const GATHER_FIXED_COST_ZERO_MATCH_NS: f64 = 42.0;

// --- PrintingCompose's own rates -------------------------------------------------------------
//
Expand Down Expand Up @@ -637,6 +684,22 @@ const GATHER_FIXED_COST_NS: f64 = 169.6;
pub(crate) const COMPOSE_LINEAR_PASS_PER_PRINTING_NS: f64 = 1.93;
/// Range-slice scatter into the printing bitmap during build.
pub(crate) const COMPOSE_SCATTER_PER_PRINTING_NS: f64 = 0.48;

/// A CARD-SPACE collection leaf's build (`ids_of` + `broadcast_card_ids_to_printings`) used to ride
/// `COMPOSE_SCATTER_PER_PRINTING_NS`, on the assumption that it was the same shape of operation as a
/// range's contiguous slice-scatter. It measurably is not: a card-cursor lookup per id (`offsets[c]`/
/// `offsets[c+1]`) plus a variable-width printing-range fill, against a range's single contiguous
/// write.
///
/// Backed out of `otag:triggered-ability`/`otag:cycle`/`otag:activated-ability` (`unique=printing`,
/// EDHREC): with `printings_walked` corrected (`WalkCheckpoints`) and every other term in
/// `PhysicalPlan::PrintingCompose`'s formula computed from measured features, the residual against real
/// wall time scaled cleanly with `collection_broadcast_printings` (not a flat offset), implying 1.41,
/// 1.30, and 1.31 ns/printing -- tight enough (2.7-2.9x `COMPOSE_SCATTER_PER_PRINTING_NS`, all three
/// within 8% of each other) to be a real rate and not sampling noise, but from 3 points on one corpus
/// size, not the corpus-scaling sweep the rates above this comment were fit with. Revisit if a wider
/// measurement disagrees.
pub(crate) const COMPOSE_COLLECTION_BROADCAST_PER_PRINTING_NS: f64 = 1.34;
/// Result-space bitmap words popcounted for the total.
const COMPOSE_POPCOUNT_PER_WORD_NS: f64 = 1.07;
/// Per printing stepped over by the Perm / OrderbyWalk page fill.
Expand Down Expand Up @@ -758,6 +821,7 @@ pub(crate) fn plan_cost(plan: PhysicalPlan, f: &PlanFeatures) -> f64 {
PhysicalPlan::PrintingCompose => {
let build = f64::from(f.broadcast_printings) * COMPOSE_LINEAR_PASS_PER_PRINTING_NS // legality broadcast-down into the printing bitmap (border/rarity read a plane → 0)
+ f64::from(f.scatter_printings) * COMPOSE_SCATTER_PER_PRINTING_NS // range-slice scatter into the printing bitmap (cheap: no card cursor)
+ f64::from(f.collection_broadcast_printings) * COMPOSE_COLLECTION_BROADCAST_PER_PRINTING_NS // card-space collection leaf's build (ids_of + broadcast_card_ids_to_printings) — a card cursor per id, pricier than a range's contiguous scatter
+ f64::from(f.project_printings) * COMPOSE_LINEAR_PASS_PER_PRINTING_NS // second pass: project printing→card/artwork (0 for printing mode) — the pass CardRangePopcount fuses away
+ f64::from(f.popcount_words) * COMPOSE_POPCOUNT_PER_WORD_NS; // popcount the result-space bitmap for the total (printing/card/artwork words)
let page = match f.compose_paging {
Expand Down Expand Up @@ -833,19 +897,25 @@ pub(crate) fn plan_cost(plan: PhysicalPlan, f: &PlanFeatures) -> f64 {
let walks_permutation = !runs_small_gather && f.matches > 0 && u64::from(f.offset) < u64::from(f.matches);
let perm_steps = if walks_permutation {
// Entries visited to accumulate `page_span` matches, when matches are spread uniformly
// through the permutation: one match per `n_cards / matches` entries. Bounded by the
// corpus, since the walk cannot step past the end of the permutation.
// through the WALKED SEGMENT: one match per `perm_walk_span / matches` entries. Bounded
// by that segment, since the walk cannot step past its end.
//
// The executor starts and ends the walk at the segment its filter's bound on the SORT
// COLUMN admits (`walk_bounds`), and `perm_walk_span` is that same segment's length,
// computed once at acquire by `mk_plan_feats` calling the identical `walk_bounds` helper
// over the identical `QueryParams::sort_bound` the executor reads -- not re-derived by a
// second path that could silently disagree with what dispatch actually walks. Before
// Round 32 this multiplied by `n_cards` unconditionally, which is right only when the
// filter constrains nothing about the sort column; `perm_walk_span` already collapses to
// `n_cards` in exactly that case (and when no permutation exists at all), so this is a
// strict generalization, not a second code path with its own edge cases.
//
// The executor now starts and ends the walk at the segment its filter's bound on the
// SORT COLUMN admits (`walk_bounds`), which this cannot see -- no `PlanFeatures` field
// carries the filter's shape. The uniform-spread assumption absorbs it: the expected gap
// before the first match is one `n_cards / matches` stride, negligible against
// `page_span` of them. What the regrade showed is that the assumption's remaining error
// is a DIFFERENT shape. Realized/estimated over ~12.5k walking rows, same seed and
// sample length:
// Realized/estimated `perm_steps` over ~12.5k walking rows, same seed and sample length
// (docs/issues/local-engine-gathered-scan-card-printing-varying-depth.md, Round 32 for
// the held-out re-check against current traffic):
//
// unbounded walk p10 0.13 median 1.00 p90 6.43
// sort-column bound p10 0.11 median 0.96 p90 5.31
// unbounded walk (pre-Round-32) p10 0.13 median 1.00 p90 6.43
// sort-column bound (shipped) p10 0.11 median 0.96 p90 5.31
// realized inv_perm min/max p10 0.08 median 0.90 p90 4.26
//
// The third row is not shipped -- it cost 0.51 ns per matching card -- but it bounds how
Expand All @@ -854,7 +924,8 @@ pub(crate) fn plan_cost(plan: PhysicalPlan, f: &PlanFeatures) -> f64 {
// catches only what the predicate names. What is left in BOTH is non-matching entries
// INTERIOR to the walked segment, which no start position reaches by construction. That
// is the popcount-skip mechanism's territory.
(page_span * n_cards / f64::from(f.matches)).min(n_cards)
let perm_walk_span = f64::from(f.perm_walk_span);
(page_span * perm_walk_span / f64::from(f.matches)).min(perm_walk_span)
} else {
0.0
};
Expand Down Expand Up @@ -898,7 +969,7 @@ pub(crate) fn plan_cost(plan: PhysicalPlan, f: &PlanFeatures) -> f64 {
+ page_span * GATHER_SELECT_PER_PAGE_SLOT_NS
+ page_rows * GATHER_COLLECT_PER_PAGE_ROW_NS
+ f64::from(f.artwork_seen_printings) * GATHER_ARTWORK_PER_PRINTING_NS
+ GATHER_FIXED_COST_NS
+ if matches > 0.0 { GATHER_FIXED_COST_NS } else { GATHER_FIXED_COST_ZERO_MATCH_NS }
}
}
}
Loading
Loading