Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a special optimizer pipeline for constant INSERTs #30666

Merged
merged 2 commits into from
Dec 3, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,10 @@ def benchmark(self) -> MeasurementSource:


class InsertMultiRow(DML):
"""Measure the time it takes for a single multi-row INSERT statement to return."""
"""Measure the time it takes for a single multi-row INSERT statement to return.
When `sequence_insert` calls `constant_optimizer`, it should be able to reach a constant. Otherwise, we run the full
logical optimizer, which makes this test show a regression.
"""

SCALE = 4 # FATAL: request larger than 2.0 MB

Expand Down
3 changes: 1 addition & 2 deletions src/adapter/src/catalog/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1200,13 +1200,12 @@ impl CatalogState {
}
Some(_) | None => {
let optimizer_features = optimizer_config.features.clone();
// Build an optimizer for this VIEW.
// TODO(aalexandrov): ideally this should be a materialized_view::Optimizer.
let mut optimizer = optimize::view::Optimizer::new(optimizer_config, None);

let raw_expr = materialized_view.expr;
let optimized_expr = match optimizer.optimize(raw_expr.clone()) {
Ok(optimzed_expr) => optimzed_expr,
Ok(optimized_expr) => optimized_expr,
Err(err) => return Err((err.into(), cached_expr)),
};

Expand Down
1 change: 0 additions & 1 deletion src/adapter/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,6 @@ impl SessionClient {

// Collect optimizer parameters.
let optimizer_config = optimize::OptimizerConfig::from(conn_catalog.system_vars());
// Build an optimizer for this VIEW.
let mut optimizer = optimize::view::Optimizer::new(optimizer_config, None);

let result: Result<_, AdapterError> =
Expand Down
2 changes: 1 addition & 1 deletion src/adapter/src/coord/sequencer/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2647,7 +2647,7 @@ impl Coordinator {
// Collect optimizer parameters.
let optimizer_config = optimize::OptimizerConfig::from(self.catalog().system_config());

// Build an optimizer for this VIEW.
// (`optimize::view::Optimizer` has a special case for constant queries.)
let mut optimizer = optimize::view::Optimizer::new(optimizer_config, None);

// HIR ⇒ MIR lowering and MIR ⇒ MIR optimization (local)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ impl Coordinator {
// Collect optimizer parameters.
let optimizer_config = optimize::OptimizerConfig::from(self.catalog().system_config());

// Build an optimizer for this VIEW.
let mut optimizer = optimize::view::Optimizer::new(optimizer_config, None);

let span = Span::current();
Expand Down
16 changes: 16 additions & 0 deletions src/adapter/src/optimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,22 @@ fn optimize_mir_local(
Ok::<_, OptimizerError>(expr)
}

/// This is just a wrapper around [mz_transform::Optimizer::constant_optimizer],
/// running it, and tracing the result plan.
#[mz_ore::instrument(target = "optimizer", level = "debug", name = "constant")]
fn optimize_mir_constant(
expr: MirRelationExpr,
ctx: &mut TransformCtx,
) -> Result<MirRelationExpr, OptimizerError> {
let optimizer = mz_transform::Optimizer::constant_optimizer(ctx);
let expr = optimizer.optimize(expr, ctx)?;

// Trace the result of this phase.
mz_repr::explain::trace_plan(expr.as_inner());

Ok::<_, OptimizerError>(expr.0)
}

macro_rules! trace_plan {
(at: $span:literal, $plan:expr) => {
tracing::debug_span!(target: "optimizer", $span).in_scope(|| {
Expand Down
30 changes: 25 additions & 5 deletions src/adapter/src/optimize/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

//! Optimizer implementation for `CREATE VIEW` statements.
//! An Optimizer that
//! 1. Optimistically calls `optimize_mir_constant`.
//! 2. Then, if we haven't arrived at a constant, it calls `optimize_mir_local`, i.e., the
//! logical optimizer.
//!
//! This is used for `CREATE VIEW` statements and in various other situations where no physical
//! optimization is needed, such as for `INSERT` statements.

use std::time::Instant;

Expand All @@ -18,7 +24,10 @@ use mz_transform::dataflow::DataflowMetainfo;
use mz_transform::typecheck::{empty_context, SharedContext as TypecheckContext};
use mz_transform::TransformCtx;

use crate::optimize::{optimize_mir_local, trace_plan, Optimize, OptimizerConfig, OptimizerError};
use crate::optimize::{
optimize_mir_constant, optimize_mir_local, trace_plan, Optimize, OptimizerConfig,
OptimizerError,
};

pub struct Optimizer {
/// A typechecking context to use throughout the optimizer pipeline.
Expand Down Expand Up @@ -52,13 +61,24 @@ impl Optimize<HirRelationExpr> for Optimizer {
trace_plan!(at: "raw", &expr);

// HIR ⇒ MIR lowering and decorrelation
let expr = expr.lower(&self.config, self.metrics.as_ref())?;
let mut expr = expr.lower(&self.config, self.metrics.as_ref())?;

// MIR ⇒ MIR optimization (local)
let mut df_meta = DataflowMetainfo::default();
let mut transform_ctx =
TransformCtx::local(&self.config.features, &self.typecheck_ctx, &mut df_meta);
let expr = optimize_mir_local(expr, &mut transform_ctx)?;

// First, we run a very simple optimizer pipeline, which only folds constants. This takes
// care of constant INSERTs. (This optimizer is also used for INSERTs, not just VIEWs.)
expr = optimize_mir_constant(expr, &mut transform_ctx)?;

// MIR ⇒ MIR optimization (local)
let expr = if expr.as_const().is_some() {
// No need to optimize further, because we already have a constant.
OptimizedMirRelationExpr(expr)
} else {
// Call the real optimization.
optimize_mir_local(expr, &mut transform_ctx)?
};

if let Some(metrics) = &self.metrics {
metrics.observe_e2e_optimization_time("view", time.elapsed());
Expand Down
17 changes: 14 additions & 3 deletions src/transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,9 +552,11 @@ pub fn fuse_and_collapse_fixpoint() -> Fixpoint {
}
}

/// Does constant folding idempotently. This needs to call `FoldConstants` together with
/// `NormalizeLets` in a fixpoint loop, because currently `FoldConstants` doesn't inline CTEs, so
/// these two need to alternate until fixpoint.
/// Does constant folding to a fixpoint: An expression all of whose leaves are constants, of size
/// small enough to be inlined and folded should reach a single `MirRelationExpr::Constant`.
///
/// This needs to call `FoldConstants` together with `NormalizeLets` in a fixpoint loop, because
/// currently `FoldConstants` doesn't inline CTEs, so these two need to alternate until fixpoint.
///
/// Also note that `FoldConstants` can break the normalized form by removing all references to a
/// Let.
Expand Down Expand Up @@ -819,6 +821,15 @@ impl Optimizer {
}
}

/// Builds a tiny optimizer, which just folds constants. For more details, see
/// [fold_constants_fixpoint].
pub fn constant_optimizer(_ctx: &mut TransformCtx) -> Self {
Self {
name: "fast_path_optimizer",
transforms: vec![Box::new(fold_constants_fixpoint())],
}
}

/// Optimizes the supplied relation expression.
///
/// These optimizations are performed with no information about available arrangements,
Expand Down
Loading