Path: blob/main/crates/polars-plan/src/plans/optimizer/stack_opt.rs
6940 views
use polars_core::prelude::PolarsResult;1use polars_core::schema::Schema;23use crate::plans::aexpr::AExpr;4use crate::plans::ir::IR;5use crate::plans::{get_input, get_input_schema};6use crate::prelude::{Arena, Node};78/// Optimizer that uses a stack and memory arenas in favor of recursion9pub struct StackOptimizer {}1011impl StackOptimizer {12pub fn optimize_loop(13&self,14rules: &mut [Box<dyn OptimizationRule>],15expr_arena: &mut Arena<AExpr>,16lp_arena: &mut Arena<IR>,17lp_top: Node,18) -> PolarsResult<Node> {19let mut changed = true;2021// Nodes of expressions and lp node from which the expressions are a member of.22let mut plans = vec![];23let mut exprs = vec![];24let mut scratch = vec![];2526// Run loop until reaching fixed point.27#[allow(clippy::field_reassign_with_default)]28while changed {29// Recurse into sub plans and expressions and apply rules.30changed = false;31plans.push(lp_top);32while let Some(current_node) = plans.pop() {33// Apply rules34for rule in rules.iter_mut() {35// keep iterating over same rule36while let Some(x) = rule.optimize_plan(lp_arena, expr_arena, current_node)? {37lp_arena.replace(current_node, x);38changed = true;39}40}4142let plan = lp_arena.get(current_node);4344// traverse subplans and expressions and add to the stack45plan.copy_exprs(&mut scratch);46plan.copy_inputs(&mut plans);4748if scratch.is_empty() {49continue;50}5152while let Some(expr_ir) = scratch.pop() {53exprs.push(expr_ir.node());54}5556let input_schema = get_input_schema(lp_arena, current_node);57let mut ctx = OptimizeExprContext::default();58#[cfg(feature = "python")]59{60use crate::dsl::python_dsl::PythonScanSource;61ctx.in_pyarrow_scan = matches!(plan, IR::PythonScan { options } if options.python_source == PythonScanSource::Pyarrow);62ctx.in_io_plugin = matches!(plan, IR::PythonScan { options } if options.python_source == PythonScanSource::IOPlugin);63};64ctx.in_filter = matches!(plan, IR::Filter { .. });65ctx.has_inputs = !get_input(lp_arena, current_node).is_empty();6667// process the expressions on the stack and apply optimizations.68while let Some(current_expr_node) = exprs.pop() {69{70let expr = unsafe { expr_arena.get_unchecked(current_expr_node) };71if expr.is_leaf() {72continue;73}74}75for rule in rules.iter_mut() {76// keep iterating over same rule77while let Some(x) =78rule.optimize_expr(expr_arena, current_expr_node, &input_schema, ctx)?79{80expr_arena.replace(current_expr_node, x);81changed = true;82}83}8485let expr = unsafe { expr_arena.get_unchecked(current_expr_node) };86// traverse subexpressions and add to the stack87expr.inputs_rev(&mut exprs)88}89}90}91Ok(lp_top)92}93}9495#[derive(Default, Clone, Copy)]96pub struct OptimizeExprContext {97pub in_pyarrow_scan: bool,98pub in_io_plugin: bool,99pub in_filter: bool,100pub has_inputs: bool,101}102103pub trait OptimizationRule {104/// Optimize (subplan) in LogicalPlan105///106/// * `lp_arena` - LogicalPlan memory arena107/// * `expr_arena` - Expression memory arena108/// * `node` - node of the current LogicalPlan node109fn optimize_plan(110&mut self,111_lp_arena: &mut Arena<IR>,112_expr_arena: &mut Arena<AExpr>,113_node: Node,114) -> PolarsResult<Option<IR>> {115Ok(None)116}117fn optimize_expr(118&mut self,119expr_arena: &mut Arena<AExpr>,120expr_node: Node,121schema: &Schema,122ctx: OptimizeExprContext,123) -> PolarsResult<Option<AExpr>> {124_ = (expr_arena, expr_node, schema, ctx);125Ok(None)126}127}128129130