vortex_array/optimizer/
mod.rs1use smallvec::SmallVec;
20use vortex_error::VortexResult;
21use vortex_error::vortex_bail;
22use vortex_session::VortexSession;
23
24use crate::ArrayRef;
25use crate::optimizer::kernels::ArrayKernelsExt;
26use crate::trace_op;
27
28pub mod kernels;
29pub mod rules;
30
31pub trait ArrayOptimizer {
33 fn optimize(&self) -> VortexResult<ArrayRef>;
38
39 fn optimize_ctx(&self, session: &VortexSession) -> VortexResult<ArrayRef>;
47
48 fn optimize_recursive(&self, session: &VortexSession) -> VortexResult<ArrayRef>;
53}
54
55impl ArrayOptimizer for ArrayRef {
56 fn optimize(&self) -> VortexResult<ArrayRef> {
57 Ok(try_optimize(self, None)?.unwrap_or_else(|| self.clone()))
58 }
59
60 fn optimize_ctx(&self, session: &VortexSession) -> VortexResult<ArrayRef> {
61 Ok(try_optimize(self, Some(session))?.unwrap_or_else(|| self.clone()))
62 }
63
64 fn optimize_recursive(&self, session: &VortexSession) -> VortexResult<ArrayRef> {
65 Ok(try_optimize_recursive(self, session)?.unwrap_or_else(|| self.clone()))
66 }
67}
68
69fn try_optimize(
70 array: &ArrayRef,
71 session: Option<&VortexSession>,
72) -> VortexResult<Option<ArrayRef>> {
73 let mut current_array = array.clone();
74 let mut any_optimizations = false;
75 let array_ref = session.map(|s| s.kernels());
76
77 trace_op!(record_optimize_start(array, session.is_some()));
78
79 for _ in 0..=100 {
81 trace_op!(record_optimize_loop_start(¤t_array));
82
83 if let Some(new_array) = current_array.reduce()? {
84 current_array = new_array;
85 any_optimizations = true;
86 trace_op!(record_optimize_loop_end());
87 continue;
88 }
89
90 trace_op!(record_optimize_reduce_none(¤t_array));
91
92 let mut parent_reduced = None;
95 for (slot_idx, slot) in current_array.slots().iter().enumerate() {
96 let Some(child) = slot else { continue };
97
98 if let Some(array_ref) = &array_ref
100 && let Some(plugins) =
101 array_ref.find_reduce_parent(current_array.encoding_id(), child.encoding_id())
102 {
103 #[allow(clippy::unused_enumerate_index)]
104 for (_plugin_idx, plugin) in plugins.as_ref().iter().enumerate() {
105 if let Some(new_array) = plugin(child, ¤t_array, slot_idx)? {
106 trace_op!(record_session_parent_reduce_applied(
107 ¤t_array,
108 child,
109 slot_idx,
110 _plugin_idx,
111 &new_array,
112 ));
113 parent_reduced = Some(new_array);
114 break;
115 }
116 trace_op!(record_session_parent_reduce_declined(
117 ¤t_array,
118 child,
119 slot_idx,
120 _plugin_idx,
121 ));
122 }
123 if parent_reduced.is_some() {
124 break;
125 }
126 }
127
128 if let Some(new_array) = child.reduce_parent(¤t_array, slot_idx)? {
129 parent_reduced = Some(new_array);
130 break;
131 }
132 }
133
134 if let Some(new_array) = parent_reduced {
135 current_array = new_array;
137 any_optimizations = true;
138 trace_op!(record_optimize_loop_end());
139 continue;
140 }
141
142 trace_op!(record_optimize_parent_reduce_none(¤t_array));
143 trace_op!(record_optimize_loop_end());
144
145 trace_op!(record_optimize_done(¤t_array, any_optimizations));
147
148 if any_optimizations {
149 return Ok(Some(current_array));
150 } else {
151 return Ok(None);
152 }
153 }
154
155 vortex_bail!("Exceeded maximum optimization iterations (possible infinite loop)");
156}
157
158fn try_optimize_recursive(
159 array: &ArrayRef,
160 session: &VortexSession,
161) -> VortexResult<Option<ArrayRef>> {
162 let mut current_array = array.clone();
163 let mut any_optimizations = false;
164
165 trace_op!(record_optimize_recursive_start(array));
166
167 if let Some(new_array) = try_optimize(¤t_array, Some(session))? {
168 current_array = new_array;
169 any_optimizations = true;
170 }
171
172 let mut new_slots = SmallVec::with_capacity(current_array.slots().len());
173 let mut any_slot_optimized = false;
174 for slot in current_array.slots() {
175 match slot {
176 Some(child) => {
177 if let Some(new_child) = try_optimize_recursive(child, session)? {
178 trace_op!(record_optimize_recursive_slot(
179 new_slots.len(),
180 child,
181 &new_child,
182 ));
183 new_slots.push(Some(new_child));
184 any_slot_optimized = true;
185 } else {
186 new_slots.push(Some(child.clone()));
187 }
188 }
189 None => new_slots.push(None),
190 }
191 }
192
193 if any_slot_optimized {
194 current_array = unsafe { current_array.with_slots(new_slots) }?;
197 any_optimizations = true;
198 }
199
200 if any_optimizations {
201 Ok(Some(current_array))
202 } else {
203 Ok(None)
204 }
205}