Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 2 additions & 3 deletions crates/devtools/src/bin/stage_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,10 @@
// `guarantee_check_rejection`, the sketches read and a one-line summary),
// `pruned_candidates` and `search_report` (`SearchReport`: `status`,
// `cost_lower_bound_per_second`, MILP size and timings, and
// `inexact_objective` when the new search's objective and `plan_cost`
// `inexact_objective` when the selection MILP's objective and `plan_cost`
// differ over terms the MILP encodes inexactly). Stage 3 runs #1095's
// selection MILP, which supports cost model 1 only (cost models 2 and 3
// are an `UnsupportedCostModel` error); `ASAP_STAGE3_SEARCH=old` runs the
// old engine instead; `error` when no
// are an `UnsupportedCostModel` error, #888); `error` when no
// plan was selected. `tumbling_window_retention_ms` maps each pane chain the
// selected plan keeps beyond one evaluation (by node id in `P1`) to how
// far back the runtime must keep its tumbling windows (`RealizedPlan::tumbling_window_retention_ms`).
Expand Down
11 changes: 5 additions & 6 deletions crates/devtools/tests/stage_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2553,8 +2553,8 @@ fn memo_section_lists_each_step_the_selected_choice_and_the_enumeration() {
assert!(document.get("memo").is_none());
}

/// `stage_pipeline`'s document with Stage 3's new search forced on
/// (`ASAP_STAGE3_SEARCH=milp`, #1095), set on the child process only.
/// `stage_pipeline`'s document for `args`, run with Stage 3's group
/// selection MILP (#1095).
fn document_with_milp_search(args: &[&str]) -> Value {
static CALLS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
let call = CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Expand All @@ -2563,7 +2563,6 @@ fn document_with_milp_search(args: &[&str]) -> Value {
std::process::id(),
));
let status = Command::new(env!("CARGO_BIN_EXE_stage_pipeline"))
.env("ASAP_STAGE3_SEARCH", "milp")
.args(args)
.arg("--out")
.arg(&out)
Expand All @@ -2575,7 +2574,7 @@ fn document_with_milp_search(args: &[&str]) -> Value {
document
}

/// #1095 C5: with the new search on, example 1 and the RangeHeavy T5
/// #1095 C5: with the group selection MILP, example 1 and the RangeHeavy T5
/// q0.5 + q0.9 workload select a plan through the real pipeline, solved to
/// optimality or stopped at the deadline, whose `plan_cost` equals the MILP's objective exactly (the
/// search errors on a difference and reports `inexact_objective` when the
Expand Down Expand Up @@ -2627,7 +2626,7 @@ fn milp_search_objective_equals_plan_cost_on_example1_and_rangeheavy() {
}
}

/// #1127 regressions: with the new search on and guarantees enforced, the
/// #1127 regressions: with the group selection MILP and guarantees enforced, the
/// memo examples on which the A1 rows admitted a plan that `satisfies`
/// rejects now select a plan that passes every query's accuracy check:
/// ratio-of-quantiles (KLL rank errors compose no guarantee through the
Expand Down Expand Up @@ -2691,7 +2690,7 @@ fn milp_search_selects_checked_plans_on_ratio_and_rollupheavy() {
}

/// #1095 C5: on RangeHeavy T8+T5 (`q0.9 / q0.5` with both operands also
/// queried alone), the new search selects a DDSketch plan that passes every
/// queried alone), the group selection MILP selects a DDSketch plan that passes every
/// query's accuracy check (the A1 rows admitted KLL there, which the check
/// rejects). Which chains it shares depends on the deadline; the weights
/// that let one chain serve all three queries are checked in
Expand Down
4 changes: 2 additions & 2 deletions crates/plan-selection/src/groups/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -777,8 +777,8 @@ fn ratio_weights_come_from_the_composition() {

// RangeHeavy T8+T5's shape: `q0.9 / q0.5` with both operands also queried
// alone. One DDSketch pane chain sized for two shares of the budget serves
// both quotient operands and both lone quantiles, as today's engine plans
// it: the check accepts the quotient over it (`(α + α)/(1 − α) ≤ ε` at
// both quotient operands and both lone quantiles, as the old Stage 3
// engine planned it: the check accepts the quotient over it (`(α + α)/(1 − α) ≤ ε` at
// `α = 1 − (1 + ε)^{−1/2}`), so it weighs 1 for every query. A chain sized
// for the whole budget (`α = ε`) never serves the quotient's denominator.
#[test]
Expand Down
34 changes: 18 additions & 16 deletions crates/plan-selection/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
//! physical DAG ([`ResourceUsage`](asap_cost::ResourceUsage)), and the deployment's [`CostModel`](asap_cost::CostModel)
//! chooses among them ("Cost models" below). The rest are reported valid
//! but costlier (or slower, or larger).
//! Candidate-connection MILP search selects and validates workload DAGs
//! without constructing the Cartesian product of local candidates.
//! Dynamic-programming compression is outside the current implementation.
//! The group selection MILP ([`select_plan`], #1095) selects one candidate
//! per memo group and execution context without constructing the Cartesian
//! product of local candidates; it prices cost model 1 only (#888).
//!
//! ## Cost model: cost per second of wall time
//!
Expand Down Expand Up @@ -181,7 +181,10 @@
//!
//! [`CostInputs::cost_model`](asap_cost::CostInputs::cost_model) (the cost inputs [`select_plan`] receives) chooses how
//! usage becomes a choice; the default is cost model 1 with
//! [`ResourceUnitCost::illustrative`](asap_cost::ResourceUnitCost::illustrative):
//! [`ResourceUnitCost::illustrative`](asap_cost::ResourceUnitCost::illustrative).
//! [`select_plan`] searches under cost model 1 only; cost models 2 and 3
//! are an `UnsupportedCostModel` error until #888 (the whole-plan reference
//! pricer still ranks plans under all three):
//!
//! * [`CostModel::UsageOverTime`](asap_cost::CostModel::UsageOverTime) (cost model 1, pay-as-you-go): each
//! node's CPU operations, scanned bytes and memory byte-seconds per second
Expand Down Expand Up @@ -241,9 +244,6 @@
#[cfg(test)]
use asap_types::workload::RepeatedDemand;

use asap_cost::analytical::AnalyticalCostError;
use asap_cost::state_bytes::{sketch_shape, summary_shape, StateInputRows};

mod accuracy_binding;
mod checks;
mod models;
Expand All @@ -253,23 +253,20 @@ mod groups;
// #1095 §2.0: the group selection MILP and the decode of its solution.
mod milp;
mod report;
mod search;
// #1095 C5: the new search behind `select_plan`'s internal switch.
// Stage 3's entry, `select_plan`, over the group selection MILP (#1095).
mod select;
// The whole-plan selector behind `reference_pricer` and the tests.
#[cfg(any(test, feature = "reference-pricer"))]
mod selector;
pub(crate) use accuracy_binding::bound_root_guarantee;
pub(crate) use checks::{accuracy_targets, guarantee_check};
pub use models::AccuracyCheckInputs;
pub(crate) use models::SelectionModels;
pub use report::{
large_peak_memory_warning, AcceptedAccuracy, ErrorSelectingPlan, InexactObjective,
RejectionCause, SearchReport, SearchStatus, Selection,
};
pub(crate) use report::{Rejection, WholePlanSelection};
pub use search::select::select_plan;
pub use select::select_plan;
#[cfg(test)]
pub(crate) use selector::select_among_plans;
pub(crate) use selector::PlanSelector;

/// The whole-workload reference pricer (feature `reference-pricer`): the
/// selector Stage 3 used before the memo search, over whole physical plans
Expand Down Expand Up @@ -318,18 +315,23 @@ pub(crate) fn sized_as_pass1(
.expect("sized summaries rebuild")
}

use asap_cost::{lookup, StateFamily};

#[cfg(test)]
mod tests {
use super::*;
use crate::accuracy_binding::bound_root_guarantee;
use crate::accuracy_binding::{bind_statistics, root_operators, workload_statistic};
use crate::checks::guarantee_check;
use crate::checks::{accuracy_check, CheckFailure};
use crate::report::WholePlanSelection;
use crate::selector::PlanSelector;
use asap_accuracy::composition::INPUT_ROW_COUNT;
use asap_accuracy::estimators::hll;
use asap_accuracy::propagation::root_guarantee;
use asap_cost::analytical::AnalyticalCostError;
use asap_cost::execution_demand::evaluation_rate;
use asap_cost::state_bytes::{sketch_shape, summary_shape, StateInputRows};
use asap_cost::{kept_tumbling_window_chains, node_map, plan_cost, NodeMap};
use asap_cost::{lookup, StateFamily};
use asap_cost::{
CalibrationFit, ClusterLimits, ClusterObjective, CostInputs, CostModel, NodePriceSource,
OperatorKind, PlanCost, ResourceUsage, COST_SOURCE, MEASURED_COST_SOURCE,
Expand Down
20 changes: 9 additions & 11 deletions crates/plan-selection/src/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use asap_cost::analytical::AnalyticalCostError;
use asap_cost::{CostModel, PlanCost};
use asap_types::ir::physical_export::PhysicalASAPNodeId;
use asap_types::ir::properties::AccuracyMetric;
#[cfg(any(test, feature = "reference-pricer"))]
use asap_types::ir::QueryRoot;
use asap_types::stages::{
DagOfSelectedCandidates, PhysicalCandidateId, PrunedCandidate, SelectedNodeId,
Expand Down Expand Up @@ -180,12 +181,12 @@ impl std::fmt::Display for AcceptedAccuracy {

/// The whole-plan selector's result (`PlanSelector`):
/// the selected candidate among whole physical plans and the plan to run,
/// with what it kept of the other candidates. Today's engine evaluates each
/// proposed plan with it, and the reference pricer (feature
/// with what it kept of the other candidates. The reference pricer (feature
/// `reference-pricer`) selects among enumerated plans with it. Keeping
/// every candidate, `costs` and `accepted` list every valid candidate and
/// `rejected` every other one; otherwise only the selected candidate is
/// kept, and `rejected` holds the first rejection of each cause.
#[cfg(any(test, feature = "reference-pricer"))]
#[derive(Debug, Clone, PartialEq)]
pub struct WholePlanSelection {
pub selected: String,
Expand Down Expand Up @@ -220,8 +221,8 @@ pub struct Selection {
pub cost: PlanCost,
/// Per query, the accuracy Stage 3 accepted for the selected plan.
pub accepted_accuracy_per_query: Vec<AcceptedAccuracy>,
/// The candidates Stage 3 ruled out before or during the search (none
/// recorded by today's engine; #1095 fills it).
/// The candidates Stage 3 ruled out before the search: dominated ones and
/// those whose usage could not be costed.
pub pruned_candidates: Vec<PrunedCandidate<PhysicalCandidateId>>,
pub search_report: SearchReport,
}
Expand All @@ -231,7 +232,7 @@ pub struct Selection {
pub struct SearchReport {
pub status: SearchStatus,
/// The solver's lower bound on the selected plan's cost per second;
/// `None` when the search proves none (e.g. under cost model 3).
/// `None` when the solver's dual bound is not finite.
pub cost_lower_bound_per_second: Option<f64>,
/// The largest MILP the search built.
pub milp_variables: usize,
Expand All @@ -243,7 +244,7 @@ pub struct SearchReport {
/// the selection MILP's objective and the model encoded some cost terms
/// inexactly (open questions on #1095, e.g. the working-memory term), so
/// the plan is returned with its cost equality unproven. `None` when
/// the two are equal (within a relative 10⁻⁶) or the old search ran. A
/// the two are equal (within a relative 10⁻⁶). A
/// difference with no inexact term is an error
/// ([`ErrorSelectingPlan::ObjectiveDiffersFromPlanCost`]).
#[serde(default, skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -280,11 +281,8 @@ impl SearchReport {
pub enum SearchStatus {
/// The search proved the plan optimal over the catalog it was given.
Optimal,
/// The search stopped at the deadline (or at its internal limits) with
/// a validated plan; the gap to the lower bound may be above zero.
/// Until #1095 replaces today's engine, this also covers the engine's
/// stop at its internal limit of 128 evaluations, and any other
/// non-optimal stop that still validated a plan.
/// The search stopped at the deadline (or another solver limit) with a
/// validated plan; the gap to the lower bound may be above zero.
DeadlineReached,
}

Expand Down
Loading
Loading