Skip to content

Commit 7672ad0

Browse files
committed
fix mut slots
Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
1 parent 6d12def commit 7672ad0

4 files changed

Lines changed: 87 additions & 60 deletions

File tree

vortex-array/src/array/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,22 @@ impl dyn DynArray + '_ {
365365
slots[slot_idx] = Some(replacement.clone());
366366
})
367367
}
368+
369+
/// Take a child out of a slot, setting it to `None`.
370+
///
371+
/// Requires unique ownership of the `ArrayRef` (panics if Arc refcount > 1).
372+
pub fn take_slot(self: &mut ArrayRef, slot_idx: usize) -> Option<ArrayRef> {
373+
let vtable = self.vtable().clone_boxed();
374+
vtable.take_slot(self, slot_idx)
375+
}
376+
377+
/// Put a child into a slot, replacing whatever was there.
378+
///
379+
/// Requires unique ownership of the `ArrayRef` (panics if Arc refcount > 1).
380+
pub fn put_slot(self: &mut ArrayRef, slot_idx: usize, value: ArrayRef) {
381+
let vtable = self.vtable().clone_boxed();
382+
vtable.put_slot(self, slot_idx, value);
383+
}
368384
}
369385

370386
/// Trait for converting a type into a Vortex [`ArrayRef`].

vortex-array/src/executor.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ impl dyn DynArray + '_ {
123123
ctx.log(format_args!("-> {}", current));
124124
return Ok(current);
125125
}
126-
Some((parent, slot_idx, _)) => {
127-
current = parent.with_slot(slot_idx, current)?;
128-
current = current.optimize()?;
126+
Some((mut parent, slot_idx, _)) => {
127+
parent.put_slot(slot_idx, current);
128+
current = parent.optimize()?;
129129
continue;
130130
}
131131
}
@@ -139,9 +139,9 @@ impl dyn DynArray + '_ {
139139
ctx.log(format_args!("-> canonical (unmatched) {}", current));
140140
return Ok(current);
141141
}
142-
Some((parent, slot_idx, _)) => {
143-
current = parent.with_slot(slot_idx, current)?;
144-
current = current.optimize()?;
142+
Some((mut parent, slot_idx, _)) => {
143+
parent.put_slot(slot_idx, current);
144+
current = parent.optimize()?;
145145
continue;
146146
}
147147
}
@@ -159,11 +159,11 @@ impl dyn DynArray + '_ {
159159

160160
// Execute the array itself.
161161
let result = execute_step(current, ctx)?;
162-
let (array, step) = result.into_parts();
162+
let (mut array, step) = result.into_parts();
163163
match step {
164164
ExecutionStep::ExecuteSlot(i, done) => {
165-
let child = array.slots()[i]
166-
.clone()
165+
let child = array
166+
.take_slot(i)
167167
.vortex_expect("ExecuteSlot index in bounds");
168168
ctx.log(format_args!(
169169
"ExecuteSlot({i}): pushing {}, focusing on {}",
@@ -338,11 +338,13 @@ impl Executable for ArrayRef {
338338
Ok(array)
339339
}
340340
ExecutionStep::ExecuteSlot(i, _) => {
341-
// For single-step execution, handle ExecuteSlot by executing the slot,
342-
// replacing it, and returning the updated array.
343-
let child = array.slots()[i].clone().vortex_expect("valid slot index");
341+
// Take the child out of the slot, execute it, and put the result back.
342+
// This avoids cloning the child or the parent array.
343+
let mut array = array;
344+
let child = array.take_slot(i).vortex_expect("valid slot index");
344345
let executed_child = child.execute::<ArrayRef>(ctx)?;
345-
array.with_slot(i, executed_child)
346+
array.put_slot(i, executed_child);
347+
Ok(array)
346348
}
347349
}
348350
}

vortex-array/src/optimizer/mod.rs

Lines changed: 14 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,16 @@ pub trait ArrayOptimizer {
2626

2727
impl ArrayOptimizer for ArrayRef {
2828
fn optimize(&self) -> VortexResult<ArrayRef> {
29-
Ok(try_optimize(self)?.unwrap_or_else(|| self.clone()))
29+
try_optimize(self.clone())
3030
}
3131

3232
fn optimize_recursive(&self) -> VortexResult<ArrayRef> {
33-
Ok(try_optimize_recursive(self)?.unwrap_or_else(|| self.clone()))
33+
try_optimize_recursive(self.clone())
3434
}
3535
}
3636

37-
fn try_optimize(array: &ArrayRef) -> VortexResult<Option<ArrayRef>> {
38-
let mut current_array = array.clone();
39-
let mut any_optimizations = false;
37+
fn try_optimize(array: ArrayRef) -> VortexResult<ArrayRef> {
38+
let mut current_array = array;
4039

4140
// Apply reduction rules to the current array until no more rules apply.
4241
let mut loop_counter = 0;
@@ -48,7 +47,6 @@ fn try_optimize(array: &ArrayRef) -> VortexResult<Option<ArrayRef>> {
4847

4948
if let Some(new_array) = current_array.vtable().reduce(&current_array)? {
5049
current_array = new_array;
51-
any_optimizations = true;
5250
continue;
5351
}
5452

@@ -61,59 +59,28 @@ fn try_optimize(array: &ArrayRef) -> VortexResult<Option<ArrayRef>> {
6159
.vtable()
6260
.reduce_parent(child, &current_array, slot_idx)?
6361
{
64-
// If the parent was replaced, then we attempt to reduce it again.
6562
current_array = new_array;
66-
any_optimizations = true;
67-
68-
// Continue to the start of the outer loop
6963
continue 'outer;
7064
}
7165
}
7266

73-
// No more optimizations can be applied
7467
break;
7568
}
7669

77-
if any_optimizations {
78-
Ok(Some(current_array))
79-
} else {
80-
Ok(None)
81-
}
70+
Ok(current_array)
8271
}
8372

84-
fn try_optimize_recursive(array: &ArrayRef) -> VortexResult<Option<ArrayRef>> {
85-
let mut current_array = array.clone();
86-
let mut any_optimizations = false;
87-
88-
if let Some(new_array) = try_optimize(&current_array)? {
89-
current_array = new_array;
90-
any_optimizations = true;
91-
}
73+
fn try_optimize_recursive(array: ArrayRef) -> VortexResult<ArrayRef> {
74+
let mut current_array = try_optimize(array)?;
9275

93-
// Collect optimized children first, then apply mutations.
94-
let slots_snapshot: Vec<_> = current_array.slots().to_vec();
95-
let mut optimized_slots: Vec<(usize, ArrayRef)> = Vec::new();
96-
for (i, slot) in slots_snapshot.iter().enumerate() {
97-
if let Some(child) = slot
98-
&& let Some(new_child) = try_optimize_recursive(child)?
99-
{
100-
optimized_slots.push((i, new_child));
76+
// Optimize each child slot in-place.
77+
let nslots = current_array.slots().len();
78+
for i in 0..nslots {
79+
if let Some(child) = current_array.take_slot(i) {
80+
let optimized = try_optimize_recursive(child)?;
81+
current_array.put_slot(i, optimized);
10182
}
10283
}
10384

104-
if !optimized_slots.is_empty() {
105-
let vtable = current_array.vtable().clone_boxed();
106-
current_array = vtable.with_slots_mut(current_array, &mut |slots| {
107-
for (i, new_child) in optimized_slots.drain(..) {
108-
slots[i] = Some(new_child);
109-
}
110-
})?;
111-
any_optimizations = true;
112-
}
113-
114-
if any_optimizations {
115-
Ok(Some(current_array))
116-
} else {
117-
Ok(None)
118-
}
85+
Ok(current_array)
11986
}

vortex-array/src/vtable/dyn_.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ pub trait DynVTable: 'static + Send + Sync + Debug {
5858
f: &mut dyn FnMut(&mut [Option<ArrayRef>]),
5959
) -> VortexResult<ArrayRef>;
6060

61+
/// Take a child out of a slot, setting it to `None`.
62+
///
63+
/// Requires unique ownership of the `ArrayRef` (Arc refcount == 1).
64+
fn take_slot(&self, array: &mut ArrayRef, slot_idx: usize) -> Option<ArrayRef>;
65+
66+
/// Put a child back into a slot.
67+
///
68+
/// Requires unique ownership of the `ArrayRef` (Arc refcount == 1).
69+
fn put_slot(&self, array: &mut ArrayRef, slot_idx: usize, value: ArrayRef);
70+
6171
/// See [`VTable::reduce`]
6272
fn reduce(&self, array: &ArrayRef) -> VortexResult<Option<ArrayRef>>;
6373

@@ -131,6 +141,28 @@ impl<V: VTable> DynVTable for V {
131141
Ok(inner.into_array())
132142
}
133143

144+
fn take_slot(&self, array: &mut ArrayRef, slot_idx: usize) -> Option<ArrayRef> {
145+
// If we have unique ownership, take in-place. Otherwise, clone the child.
146+
if let Some(inner) = downcast_mut::<V>(array) {
147+
V::slots_mut(&mut inner.array)[slot_idx].take()
148+
} else {
149+
array.slots()[slot_idx].clone()
150+
}
151+
}
152+
153+
fn put_slot(&self, array: &mut ArrayRef, slot_idx: usize, value: ArrayRef) {
154+
// If we have unique ownership, put in-place. Otherwise, rebuild via with_slots_mut.
155+
if let Some(inner) = downcast_mut::<V>(array) {
156+
V::slots_mut(&mut inner.array)[slot_idx] = Some(value);
157+
} else {
158+
*array = self
159+
.with_slots_mut(array.clone(), &mut |slots| {
160+
slots[slot_idx] = Some(value.clone());
161+
})
162+
.vortex_expect("put_slot with_slots_mut failed");
163+
}
164+
}
165+
134166
fn reduce(&self, array: &ArrayRef) -> VortexResult<Option<ArrayRef>> {
135167
let Some(reduced) = V::reduce(downcast::<V>(array))? else {
136168
return Ok(None);
@@ -240,6 +272,16 @@ fn downcast<V: VTable>(array: &ArrayRef) -> &Array<V> {
240272
.vortex_expect("Failed to downcast array to expected encoding type")
241273
}
242274

275+
/// Try to get `&mut Array<V>` from an `ArrayRef` if the Arc is uniquely owned.
276+
///
277+
/// Returns `None` if the Arc has other references or the type doesn't match.
278+
fn downcast_mut<V: VTable>(array: &mut ArrayRef) -> Option<&mut Array<V>> {
279+
let ptr = Arc::get_mut(array)? as *mut dyn DynArray as *mut Array<V>;
280+
// SAFETY: we verified unique ownership via Arc::get_mut, and the vtable on DynVTable<V>
281+
// guarantees the concrete type is Array<V>.
282+
Some(unsafe { &mut *ptr })
283+
}
284+
243285
/// Downcast an `ArrayRef` into an `Arc<Array<V>>`.
244286
fn downcast_owned<V: VTable>(array: ArrayRef) -> Arc<Array<V>> {
245287
let any_arc = array.as_any_arc();

0 commit comments

Comments
 (0)