Skip to content

Commit 9a8b653

Browse files
committed
refactor(array): allocate execution outputs through context
Signed-off-by: Nicholas Gates <nick@nickgates.com>
1 parent 9f76879 commit 9a8b653

37 files changed

Lines changed: 872 additions & 345 deletions

File tree

vortex-array/src/arrays/decimal/compute/between.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl BetweenKernel for Decimal {
2727
lower: &ArrayRef,
2828
upper: &ArrayRef,
2929
options: &BetweenOptions,
30-
_ctx: &mut ExecutionCtx,
30+
ctx: &mut ExecutionCtx,
3131
) -> VortexResult<Option<ArrayRef>> {
3232
// NOTE: We know that the precision and scale were already checked to be equal by the main
3333
// `between` entrypoint function.
@@ -41,7 +41,7 @@ impl BetweenKernel for Decimal {
4141
arr.dtype().nullability() | lower.dtype().nullability() | upper.dtype().nullability();
4242

4343
match_each_decimal_value_type!(arr.values_type(), |D| {
44-
between_unpack::<D>(arr, lower, upper, nullability, options)
44+
between_unpack::<D>(arr, lower, upper, nullability, options, ctx)
4545
})
4646
}
4747
}
@@ -52,6 +52,7 @@ fn between_unpack<T: NativeDecimalType>(
5252
upper: Scalar,
5353
nullability: Nullability,
5454
options: &BetweenOptions,
55+
ctx: &mut ExecutionCtx,
5556
) -> VortexResult<Option<ArrayRef>> {
5657
let Some(lower_dv) = lower.as_decimal().decimal_value() else {
5758
// Null lower bound — fall back to canonical path.
@@ -119,6 +120,7 @@ fn between_unpack<T: NativeDecimalType>(
119120
nullability,
120121
lower_op,
121122
upper_op,
123+
ctx,
122124
)))
123125
}
124126

@@ -129,15 +131,20 @@ fn between_impl<T: NativeDecimalType>(
129131
nullability: Nullability,
130132
lower_op: impl Fn(T, T) -> bool,
131133
upper_op: impl Fn(T, T) -> bool,
134+
ctx: &mut ExecutionCtx,
132135
) -> ArrayRef {
133136
let buffer = arr.buffer::<T>();
134137
BoolArray::new(
135-
BitBuffer::collect_bool_multiversioned(buffer.len(), |idx| {
136-
// SAFETY: `collect_bool_multiversioned` invokes the predicate with indices
137-
// `0..buffer.len()` only.
138-
let value = unsafe { *buffer.get_unchecked(idx) };
139-
lower.is_none_or(|l| lower_op(l, value)) & upper.is_none_or(|u| upper_op(value, u))
140-
}),
138+
BitBuffer::collect_bool_multiversioned_in(
139+
buffer.len(),
140+
|idx| {
141+
// SAFETY: `collect_bool_multiversioned` invokes the predicate with indices
142+
// `0..buffer.len()` only.
143+
let value = unsafe { *buffer.get_unchecked(idx) };
144+
lower.is_none_or(|l| lower_op(l, value)) & upper.is_none_or(|u| upper_op(value, u))
145+
},
146+
ctx.allocator().clone(),
147+
),
141148
arr.validity()
142149
.vortex_expect("validity should be derivable")
143150
.union_nullability(nullability),

vortex-array/src/arrays/filter/execute/buffer.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use std::mem::size_of;
2121

2222
use vortex_buffer::Buffer;
23+
use vortex_buffer::BufferAllocatorRef;
2324
use vortex_mask::MaskValues;
2425

2526
use crate::arrays::filter::execute::byte_compress;
@@ -35,6 +36,7 @@ const MIN_SLICES_AVERAGE_RUN_LENGTH: usize = 8;
3536
/// Dense uniquely owned buffers are compacted in place; other buffers allocate a new output.
3637
pub(crate) fn filter_buffer<T: Copy>(buffer: Buffer<T>, mask: &MaskValues) -> Buffer<T> {
3738
assert_eq!(buffer.len(), mask.len());
39+
let allocator = buffer.allocator().clone();
3840

3941
let buffer = if mask.density() >= IN_PLACE_MIN_DENSITY {
4042
match buffer.try_into_mut() {
@@ -49,28 +51,32 @@ pub(crate) fn filter_buffer<T: Copy>(buffer: Buffer<T>, mask: &MaskValues) -> Bu
4951
buffer
5052
};
5153

52-
filter_slice(buffer.as_slice(), mask)
54+
filter_slice(buffer.as_slice(), mask, allocator)
5355
}
5456

55-
fn filter_slice<T: Copy>(values: &[T], mask: &MaskValues) -> Buffer<T> {
57+
fn filter_slice<T: Copy>(
58+
values: &[T],
59+
mask: &MaskValues,
60+
allocator: BufferAllocatorRef,
61+
) -> Buffer<T> {
5662
if let Some(slices) = useful_cached_slices(mask) {
57-
return slice::filter_slice_by_slices(values, slices, mask.true_count());
63+
return slice::filter_slice_by_slices(values, slices, mask.true_count(), allocator);
5864
}
5965

6066
if mask.density() <= CACHED_INDICES_MAX_DENSITY
6167
&& let Some(indices) = mask.cached_indices()
6268
{
63-
return slice::filter_slice_by_indices(values, indices);
69+
return slice::filter_slice_by_indices(values, indices, allocator);
6470
}
6571

66-
if let Some(filtered) = simd_compress::filter_slice_by_bitmap(values, mask) {
72+
if let Some(filtered) = simd_compress::filter_slice_by_bitmap(values, mask, allocator.clone()) {
6773
return filtered;
6874
}
6975

7076
if mask.density() >= byte_compress_density_threshold::<T>() {
71-
byte_compress::filter_buffer(values, mask)
77+
byte_compress::filter_buffer(values, mask, allocator)
7278
} else {
73-
slice::filter_slice_by_bitmap(values, mask)
79+
slice::filter_slice_by_bitmap(values, mask, allocator)
7480
}
7581
}
7682

vortex-array/src/arrays/filter/execute/byte_compress.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
//! permutation table compacts the selected bytes in a single indexed copy,
88
//! avoiding the overhead of materializing indices or slices.
99
10+
use vortex_buffer::Alignment;
1011
use vortex_buffer::Buffer;
12+
use vortex_buffer::BufferAllocatorRef;
1113
use vortex_buffer::BufferMut;
1214
use vortex_mask::MaskValues;
1315

@@ -41,30 +43,35 @@ static BYTE_COMPRESS_LUT: &[([u8; 8], u8); 256] = &{
4143
///
4244
/// Processes the mask one byte at a time (8 source elements per byte),
4345
/// using a precomputed permutation to compact selected elements.
44-
pub(crate) fn filter_buffer<T: Copy>(buffer: impl AsRef<[T]>, mask: &MaskValues) -> Buffer<T> {
46+
pub(crate) fn filter_buffer<T: Copy>(
47+
buffer: impl AsRef<[T]>,
48+
mask: &MaskValues,
49+
allocator: BufferAllocatorRef,
50+
) -> Buffer<T> {
4551
let src = buffer.as_ref();
4652
debug_assert_eq!(src.len(), mask.len());
4753

4854
let true_count = mask.true_count();
4955

5056
if true_count == 0 {
51-
return Buffer::empty();
57+
return BufferMut::empty_aligned_in(Alignment::of::<T>(), allocator).freeze();
5258
}
5359

5460
let mask_buffer = mask.bit_buffer();
5561
let mask_bytes = mask_buffer.inner().as_ref();
5662
let mask_offset = mask_buffer.offset();
5763

58-
filter_bitpacked(src, mask_bytes, mask_offset, true_count)
64+
filter_bitpacked(src, mask_bytes, mask_offset, true_count, allocator)
5965
}
6066

6167
fn filter_bitpacked<T: Copy>(
6268
src: &[T],
6369
mask_bytes: &[u8],
6470
mask_offset: usize,
6571
true_count: usize,
72+
allocator: BufferAllocatorRef,
6673
) -> Buffer<T> {
67-
let mut out = BufferMut::<T>::with_capacity(true_count);
74+
let mut out = BufferMut::<T>::with_capacity_in(true_count, allocator);
6875
let mut write_pos: usize = 0;
6976

7077
if mask_offset == 0 {
@@ -165,6 +172,10 @@ mod tests {
165172

166173
use super::*;
167174

175+
fn filter_buffer<T: Copy>(buffer: impl AsRef<[T]>, mask: &MaskValues) -> Buffer<T> {
176+
super::filter_buffer(buffer, mask, BufferAllocatorRef::statically_allocated())
177+
}
178+
168179
fn mask_values(mask: &Mask) -> &MaskValues {
169180
match mask {
170181
Mask::Values(v) => v.as_ref(),

vortex-array/src/arrays/filter/execute/simd_compress/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use std::ptr;
2828

2929
use vortex_buffer::Buffer;
30+
use vortex_buffer::BufferAllocatorRef;
3031
use vortex_buffer::BufferMut;
3132
use vortex_mask::MaskValues;
3233

@@ -50,12 +51,14 @@ type Kernel = unsafe fn(*const u8, *mut u8, &MaskValues) -> usize;
5051
pub(super) fn filter_slice_by_bitmap<T: Copy>(
5152
values: &[T],
5253
mask: &MaskValues,
54+
allocator: BufferAllocatorRef,
5355
) -> Option<Buffer<T>> {
5456
debug_assert_eq!(values.len(), mask.len());
5557
let kernel = select_kernel::<T, false>(mask)?;
5658

5759
let true_count = mask.true_count();
58-
let mut out = BufferMut::<T>::with_capacity(true_count + SLACK_BYTES / size_of::<T>());
60+
let mut out =
61+
BufferMut::<T>::with_capacity_in(true_count + SLACK_BYTES / size_of::<T>(), allocator);
5962
// SAFETY: `select_kernel` probed the kernel's target features; `values` holds `mask.len()`
6063
// elements and the output has capacity for every selected element plus a full vector of
6164
// slack, so each unmasked store stays in bounds.

vortex-array/src/arrays/filter/execute/simd_compress/tests.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ use vortex_mask::MaskValues;
1111
use super::super::slice;
1212
use super::*;
1313

14+
fn filter_slice_by_bitmap<T: Copy>(values: &[T], mask: &MaskValues) -> Option<Buffer<T>> {
15+
super::filter_slice_by_bitmap(values, mask, BufferAllocatorRef::statically_allocated())
16+
}
17+
1418
fn mask_values(mask: &Mask) -> Option<&MaskValues> {
1519
match mask {
1620
Mask::Values(values) => Some(values.as_ref()),
@@ -43,7 +47,8 @@ fn check<T: Copy + PartialEq + std::fmt::Debug>(values: &[T], mask: &Mask) {
4347
let Some(mask) = mask_values(mask) else {
4448
return;
4549
};
46-
let expected = slice::filter_slice_by_bitmap(values, mask);
50+
let expected =
51+
slice::filter_slice_by_bitmap(values, mask, BufferAllocatorRef::statically_allocated());
4752

4853
if let Some(actual) = filter_slice_by_bitmap(values, mask) {
4954
assert_eq!(actual.as_slice(), expected.as_slice());
@@ -114,7 +119,8 @@ fn avx2_kernels_match_scalar() {
114119
values: &[T],
115120
mask: &MaskValues,
116121
) {
117-
let expected = slice::filter_slice_by_bitmap(values, mask);
122+
let expected =
123+
slice::filter_slice_by_bitmap(values, mask, BufferAllocatorRef::statically_allocated());
118124

119125
let mut out = vec![T::default(); mask.true_count() + SLACK_BYTES / size_of::<T>()];
120126
// SAFETY: AVX2 was detected above and the output has a vector of slack.

vortex-array/src/arrays/filter/execute/slice.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use std::ptr;
1010

1111
use vortex_buffer::Buffer;
12+
use vortex_buffer::BufferAllocatorRef;
1213
use vortex_buffer::BufferMut;
1314
use vortex_mask::MaskValues;
1415

@@ -53,15 +54,19 @@ pub(super) fn low_bits_mask(len: usize) -> u64 {
5354
}
5455

5556
/// Filter a slice from the mask bitmap without materializing indices or ranges.
56-
pub(super) fn filter_slice_by_bitmap<T: Copy>(slice: &[T], mask: &MaskValues) -> Buffer<T> {
57+
pub(super) fn filter_slice_by_bitmap<T: Copy>(
58+
slice: &[T],
59+
mask: &MaskValues,
60+
allocator: BufferAllocatorRef,
61+
) -> Buffer<T> {
5762
assert_eq!(
5863
mask.len(),
5964
slice.len(),
6065
"Selection mask length must equal the buffer length"
6166
);
6267

6368
let output_len = mask.true_count();
64-
let mut out = BufferMut::<T>::with_capacity(output_len);
69+
let mut out = BufferMut::<T>::with_capacity_in(output_len, allocator);
6570
let src_ptr = slice.as_ptr();
6671
let out_ptr = out.spare_capacity_mut().as_mut_ptr().cast::<T>();
6772
let mut write_pos = 0;
@@ -98,8 +103,12 @@ pub(super) fn filter_slice_by_bitmap<T: Copy>(slice: &[T], mask: &MaskValues) ->
98103
}
99104

100105
/// Filter a slice by a set of strictly increasing indices.
101-
pub(super) fn filter_slice_by_indices<T: Copy>(slice: &[T], indices: &[usize]) -> Buffer<T> {
102-
let mut out = BufferMut::<T>::with_capacity(indices.len());
106+
pub(super) fn filter_slice_by_indices<T: Copy>(
107+
slice: &[T],
108+
indices: &[usize],
109+
allocator: BufferAllocatorRef,
110+
) -> Buffer<T> {
111+
let mut out = BufferMut::<T>::with_capacity_in(indices.len(), allocator);
103112
let src_ptr = slice.as_ptr();
104113
let out_ptr = out.spare_capacity_mut().as_mut_ptr().cast::<T>();
105114

@@ -119,8 +128,9 @@ pub(super) fn filter_slice_by_slices<T: Copy>(
119128
slice: &[T],
120129
slices: &[(usize, usize)],
121130
output_len: usize,
131+
allocator: BufferAllocatorRef,
122132
) -> Buffer<T> {
123-
let mut out = BufferMut::<T>::with_capacity(output_len);
133+
let mut out = BufferMut::<T>::with_capacity_in(output_len, allocator);
124134
for (start, end) in slices {
125135
out.extend_from_slice(&slice[*start..*end]);
126136
}

vortex-array/src/arrays/filter/execute/take.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ fn take_impl(
7878
return array.child().filter(mask)?.cast(result_dtype);
7979
}
8080

81-
let translated = translate_indices(array.filter_mask(), indices, None)?;
81+
let translated =
82+
translate_indices(array.filter_mask(), indices, None, ctx.allocator().clone())?;
8283
let translated_indices =
8384
PrimitiveArray::new(translated, indices.validity()?).into_array();
8485

@@ -90,7 +91,12 @@ fn take_impl(
9091
)
9192
.into_array()),
9293
AllOr::Some(buf) => {
93-
let translated = translate_indices(array.filter_mask(), indices, Some(buf))?;
94+
let translated = translate_indices(
95+
array.filter_mask(),
96+
indices,
97+
Some(buf),
98+
ctx.allocator().clone(),
99+
)?;
94100
let translated_indices =
95101
PrimitiveArray::new(translated, indices.validity()?).into_array();
96102

0 commit comments

Comments
 (0)