Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-plan/src/plans/optimizer/stack_opt.rs
6940 views
1
use polars_core::prelude::PolarsResult;
2
use polars_core::schema::Schema;
3
4
use crate::plans::aexpr::AExpr;
5
use crate::plans::ir::IR;
6
use crate::plans::{get_input, get_input_schema};
7
use crate::prelude::{Arena, Node};
8
9
/// Optimizer that uses a stack and memory arenas in favor of recursion
10
pub struct StackOptimizer {}
11
12
impl StackOptimizer {
13
pub fn optimize_loop(
14
&self,
15
rules: &mut [Box<dyn OptimizationRule>],
16
expr_arena: &mut Arena<AExpr>,
17
lp_arena: &mut Arena<IR>,
18
lp_top: Node,
19
) -> PolarsResult<Node> {
20
let mut changed = true;
21
22
// Nodes of expressions and lp node from which the expressions are a member of.
23
let mut plans = vec![];
24
let mut exprs = vec![];
25
let mut scratch = vec![];
26
27
// Run loop until reaching fixed point.
28
#[allow(clippy::field_reassign_with_default)]
29
while changed {
30
// Recurse into sub plans and expressions and apply rules.
31
changed = false;
32
plans.push(lp_top);
33
while let Some(current_node) = plans.pop() {
34
// Apply rules
35
for rule in rules.iter_mut() {
36
// keep iterating over same rule
37
while let Some(x) = rule.optimize_plan(lp_arena, expr_arena, current_node)? {
38
lp_arena.replace(current_node, x);
39
changed = true;
40
}
41
}
42
43
let plan = lp_arena.get(current_node);
44
45
// traverse subplans and expressions and add to the stack
46
plan.copy_exprs(&mut scratch);
47
plan.copy_inputs(&mut plans);
48
49
if scratch.is_empty() {
50
continue;
51
}
52
53
while let Some(expr_ir) = scratch.pop() {
54
exprs.push(expr_ir.node());
55
}
56
57
let input_schema = get_input_schema(lp_arena, current_node);
58
let mut ctx = OptimizeExprContext::default();
59
#[cfg(feature = "python")]
60
{
61
use crate::dsl::python_dsl::PythonScanSource;
62
ctx.in_pyarrow_scan = matches!(plan, IR::PythonScan { options } if options.python_source == PythonScanSource::Pyarrow);
63
ctx.in_io_plugin = matches!(plan, IR::PythonScan { options } if options.python_source == PythonScanSource::IOPlugin);
64
};
65
ctx.in_filter = matches!(plan, IR::Filter { .. });
66
ctx.has_inputs = !get_input(lp_arena, current_node).is_empty();
67
68
// process the expressions on the stack and apply optimizations.
69
while let Some(current_expr_node) = exprs.pop() {
70
{
71
let expr = unsafe { expr_arena.get_unchecked(current_expr_node) };
72
if expr.is_leaf() {
73
continue;
74
}
75
}
76
for rule in rules.iter_mut() {
77
// keep iterating over same rule
78
while let Some(x) =
79
rule.optimize_expr(expr_arena, current_expr_node, &input_schema, ctx)?
80
{
81
expr_arena.replace(current_expr_node, x);
82
changed = true;
83
}
84
}
85
86
let expr = unsafe { expr_arena.get_unchecked(current_expr_node) };
87
// traverse subexpressions and add to the stack
88
expr.inputs_rev(&mut exprs)
89
}
90
}
91
}
92
Ok(lp_top)
93
}
94
}
95
96
#[derive(Default, Clone, Copy)]
97
pub struct OptimizeExprContext {
98
pub in_pyarrow_scan: bool,
99
pub in_io_plugin: bool,
100
pub in_filter: bool,
101
pub has_inputs: bool,
102
}
103
104
pub trait OptimizationRule {
105
/// Optimize (subplan) in LogicalPlan
106
///
107
/// * `lp_arena` - LogicalPlan memory arena
108
/// * `expr_arena` - Expression memory arena
109
/// * `node` - node of the current LogicalPlan node
110
fn optimize_plan(
111
&mut self,
112
_lp_arena: &mut Arena<IR>,
113
_expr_arena: &mut Arena<AExpr>,
114
_node: Node,
115
) -> PolarsResult<Option<IR>> {
116
Ok(None)
117
}
118
fn optimize_expr(
119
&mut self,
120
expr_arena: &mut Arena<AExpr>,
121
expr_node: Node,
122
schema: &Schema,
123
ctx: OptimizeExprContext,
124
) -> PolarsResult<Option<AExpr>> {
125
_ = (expr_arena, expr_node, schema, ctx);
126
Ok(None)
127
}
128
}
129
130