Optimize the internal expression evaluation order. This method performs several optimizations that are meant to streamline the execution of the `MapFilterProject` instance, but not to alter its semantics. This includes extracting expressions that are used multiple times, inlining those that are not, and removing expressions that are unreferenced. This method will inline all temporal expressions,
(&mut self)
| 975 | /// ); |
| 976 | /// ``` |
| 977 | pub fn optimize(&mut self) { |
| 978 | // Track sizes and iterate as long as they decrease. |
| 979 | let mut prev_size = None; |
| 980 | let mut self_size = usize::max_value(); |
| 981 | // Continue as long as strict improvements occur. |
| 982 | while prev_size.map(|p| self_size < p).unwrap_or(true) { |
| 983 | // Lock in current size. |
| 984 | prev_size = Some(self_size); |
| 985 | |
| 986 | // We have an annoying pattern of mapping literals that already exist as columns (by filters). |
| 987 | // Try to identify this pattern, of a map that introduces an expression equated to a prior column, |
| 988 | // and then replace the mapped expression by a column reference. |
| 989 | // |
| 990 | // We think this is due to `LiteralLifting`, and we might investigate removing the introduciton in |
| 991 | // the first place. The tell-tale that we see when we fix is a diff that look likes |
| 992 | // |
| 993 | // - Project (#0, #2) |
| 994 | // - Filter (#1 = 1) |
| 995 | // - Map (1) |
| 996 | // - Get l0 |
| 997 | // + Filter (#1 = 1) |
| 998 | // + Get l0 |
| 999 | // |
| 1000 | for (index, expr) in self.expressions.iter_mut().enumerate() { |
| 1001 | // If `expr` matches a filter equating it to a column < index + input_arity, rewrite it |
| 1002 | for (_, predicate) in self.predicates.iter() { |
| 1003 | if let Some(col) = |
| 1004 | E::equality_column_alias(predicate, expr, index + self.input_arity) |
| 1005 | { |
| 1006 | *expr = col; |
| 1007 | } |
| 1008 | } |
| 1009 | } |
| 1010 | |
| 1011 | // Optimization memoizes individual `ScalarExpr` expressions that |
| 1012 | // are sure to be evaluated, canonicalizes references to the first |
| 1013 | // occurrence of each, inlines expressions that have a reference |
| 1014 | // count of one, and then removes any expressions that are not |
| 1015 | // referenced. |
| 1016 | self.memoize_expressions(); |
| 1017 | self.predicates.sort(); |
| 1018 | self.predicates.dedup(); |
| 1019 | self.inline_expressions(); |
| 1020 | self.remove_undemanded(); |
| 1021 | |
| 1022 | // Re-build `self` from parts to restore evaluation order invariants. |
| 1023 | let (map, filter, project) = self.as_map_filter_project(); |
| 1024 | *self = Self::new(self.input_arity) |
| 1025 | .map(map) |
| 1026 | .filter(filter) |
| 1027 | .project(project); |
| 1028 | |
| 1029 | self_size = self.size(); |
| 1030 | } |
| 1031 | } |
| 1032 | |
| 1033 | /// Total expression sizes across all expressions. |
| 1034 | pub fn size(&self) -> usize { |