Skip to content

Commit eb7a1f1

Browse files
robert3005claude
andcommitted
Widen list take piece bounds to u64 before dispatch
The piecewise list take compiled its gather kernels for every combination of piece start, piece length, list offset and output offset type (4 x 4 x 4 x 4). The kernels read each piece start and length only once per piece, so the take now widens them to u64 and dispatches only on the offset types. The widening does not copy when the pieces are already u64, which is the type that the list take produces. vortex-array release __text: 14,012,988 -> 12,798,204 bytes (-1.21 MB). Signed-off-by: Robert Kruszewski <robert@spiraldb.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 756f1eb commit eb7a1f1

1 file changed

Lines changed: 94 additions & 135 deletions

File tree

  • vortex-array/src/arrays/list/compute

‎vortex-array/src/arrays/list/compute/take.rs‎

Lines changed: 94 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

44
use itertools::Itertools as _;
5+
use num_traits::AsPrimitive;
6+
use vortex_buffer::Buffer;
57
use vortex_buffer::BufferMut;
68
use vortex_error::VortexExpect;
79
use vortex_error::VortexResult;
@@ -27,6 +29,7 @@ use crate::arrays::piecewise_sequence::constant_unsigned_usize;
2729
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2830
use crate::arrays::primitive::PrimitiveArrayExt;
2931
use crate::dtype::IntegerPType;
32+
use crate::dtype::PType;
3033
use crate::dtype::UnsignedPType;
3134
use crate::executor::ExecutionCtx;
3235
use crate::match_each_unsigned_integer_ptype;
@@ -162,152 +165,54 @@ fn take_slices(
162165
let offsets = offsets.reinterpret_cast(offsets.ptype().to_unsigned());
163166
let output_len = indices_ref.len();
164167

168+
// The piece starts and lengths are read once per piece, so they are widened to `u64` instead
169+
// of compiling the gather kernels for each of their widths.
170+
let starts = pieces_as_u64(&starts);
165171
let taken = match lengths {
166172
Columnar::Constant(lengths) => {
167173
let length = constant_unsigned_usize(&lengths);
168-
take_slices_constant_start_dispatch(
169-
array,
170-
&starts,
171-
length,
172-
&offsets,
173-
indices_ref,
174-
output_len,
175-
&data_validity,
176-
)?
174+
match_each_unsigned_integer_ptype!(offsets.ptype(), |O| {
175+
take_slices_constant_length::<u64, O>(
176+
array,
177+
&starts,
178+
length,
179+
offsets.as_slice::<O>(),
180+
indices_ref,
181+
output_len,
182+
&data_validity,
183+
)
184+
})?
177185
}
178186
Columnar::Canonical(lengths) => {
179-
let lengths = lengths.into_primitive();
180-
take_slices_start_dispatch(
181-
array,
182-
&starts,
183-
&lengths,
184-
&offsets,
185-
indices_ref,
186-
output_len,
187-
&data_validity,
188-
)?
187+
let lengths = pieces_as_u64(&lengths.into_primitive());
188+
match_each_unsigned_integer_ptype!(offsets.ptype(), |O| {
189+
take_slices_typed::<u64, u64, O>(
190+
array,
191+
&starts,
192+
&lengths,
193+
offsets.as_slice::<O>(),
194+
indices_ref,
195+
output_len,
196+
&data_validity,
197+
)
198+
})?
189199
}
190200
};
191201
Ok(Some(taken))
192202
}
193203

194-
fn take_slices_constant_start_dispatch(
195-
array: ArrayView<'_, List>,
196-
starts: &PrimitiveArray,
197-
length: usize,
198-
offsets: &PrimitiveArray,
199-
indices_ref: &ArrayRef,
200-
output_len: usize,
201-
data_validity: &Mask,
202-
) -> VortexResult<ArrayRef> {
203-
match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
204-
take_slices_constant_offset_dispatch::<S>(
205-
array,
206-
starts,
207-
length,
208-
offsets,
209-
indices_ref,
210-
output_len,
211-
data_validity,
212-
)
213-
})
214-
}
215-
216-
fn take_slices_constant_offset_dispatch<S>(
217-
array: ArrayView<'_, List>,
218-
starts: &PrimitiveArray,
219-
length: usize,
220-
offsets: &PrimitiveArray,
221-
indices_ref: &ArrayRef,
222-
output_len: usize,
223-
data_validity: &Mask,
224-
) -> VortexResult<ArrayRef>
225-
where
226-
S: UnsignedPType,
227-
{
228-
match_each_unsigned_integer_ptype!(offsets.ptype(), |O| {
229-
take_slices_constant_length::<S, O>(
230-
array,
231-
starts.as_slice::<S>(),
232-
length,
233-
offsets.as_slice::<O>(),
234-
indices_ref,
235-
output_len,
236-
data_validity,
237-
)
238-
})
239-
}
240-
241-
fn take_slices_start_dispatch(
242-
array: ArrayView<'_, List>,
243-
starts: &PrimitiveArray,
244-
lengths: &PrimitiveArray,
245-
offsets: &PrimitiveArray,
246-
indices_ref: &ArrayRef,
247-
output_len: usize,
248-
data_validity: &Mask,
249-
) -> VortexResult<ArrayRef> {
250-
match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
251-
take_slices_length_dispatch::<S>(
252-
array,
253-
starts,
254-
lengths,
255-
offsets,
256-
indices_ref,
257-
output_len,
258-
data_validity,
259-
)
260-
})
261-
}
262-
263-
fn take_slices_length_dispatch<S>(
264-
array: ArrayView<'_, List>,
265-
starts: &PrimitiveArray,
266-
lengths: &PrimitiveArray,
267-
offsets: &PrimitiveArray,
268-
indices_ref: &ArrayRef,
269-
output_len: usize,
270-
data_validity: &Mask,
271-
) -> VortexResult<ArrayRef>
272-
where
273-
S: UnsignedPType,
274-
{
275-
match_each_unsigned_integer_ptype!(lengths.ptype(), |L| {
276-
take_slices_offset_dispatch::<S, L>(
277-
array,
278-
starts,
279-
lengths,
280-
offsets,
281-
indices_ref,
282-
output_len,
283-
data_validity,
284-
)
285-
})
286-
}
204+
/// Returns the unsigned piece values as `u64`. This does not copy when they are already `u64`.
205+
fn pieces_as_u64(pieces: &PrimitiveArray) -> Buffer<u64> {
206+
if pieces.ptype() == PType::U64 {
207+
return pieces.to_buffer::<u64>();
208+
}
287209

288-
fn take_slices_offset_dispatch<S, L>(
289-
array: ArrayView<'_, List>,
290-
starts: &PrimitiveArray,
291-
lengths: &PrimitiveArray,
292-
offsets: &PrimitiveArray,
293-
indices_ref: &ArrayRef,
294-
output_len: usize,
295-
data_validity: &Mask,
296-
) -> VortexResult<ArrayRef>
297-
where
298-
S: UnsignedPType,
299-
L: UnsignedPType,
300-
{
301-
match_each_unsigned_integer_ptype!(offsets.ptype(), |O| {
302-
take_slices_typed::<S, L, O>(
303-
array,
304-
starts.as_slice::<S>(),
305-
lengths.as_slice::<L>(),
306-
offsets.as_slice::<O>(),
307-
indices_ref,
308-
output_len,
309-
data_validity,
310-
)
210+
match_each_unsigned_integer_ptype!(pieces.ptype(), |P| {
211+
pieces
212+
.as_slice::<P>()
213+
.iter()
214+
.map(|&piece| piece.as_())
215+
.collect()
311216
})
312217
}
313218

@@ -849,9 +754,11 @@ mod test {
849754
use std::sync::Arc;
850755

851756
use rstest::rstest;
757+
use vortex_buffer::Buffer;
852758
use vortex_buffer::buffer;
853759
use vortex_error::VortexResult;
854760

761+
use crate::ArrayRef;
855762
use crate::IntoArray as _;
856763
use crate::VortexSessionExecute;
857764
use crate::array_session;
@@ -1114,6 +1021,58 @@ mod test {
11141021
);
11151022
}
11161023

1024+
/// The take widens narrow piece starts and lengths to `u64`.
1025+
#[rstest]
1026+
#[case::u8_pieces(
1027+
buffer![1u8, 0].into_array(),
1028+
buffer![2u8, 1].into_array(),
1029+
buffer![2i32, 3, 4, 0, 1],
1030+
buffer![0u32, 3, 3, 5]
1031+
)]
1032+
#[case::mixed_pieces(
1033+
buffer![1u16, 0].into_array(),
1034+
buffer![2u32, 1].into_array(),
1035+
buffer![2i32, 3, 4, 0, 1],
1036+
buffer![0u32, 3, 3, 5]
1037+
)]
1038+
#[case::constant_length(
1039+
buffer![3u16, 0].into_array(),
1040+
ConstantArray::new(1u8, 2).into_array(),
1041+
buffer![5i32, 6, 0, 1],
1042+
buffer![0u32, 2, 4]
1043+
)]
1044+
fn piecewise_sequence_take_narrow_pieces(
1045+
#[case] starts: ArrayRef,
1046+
#[case] lengths: ArrayRef,
1047+
#[case] expected_elements: Buffer<i32>,
1048+
#[case] expected_offsets: Buffer<u32>,
1049+
) -> VortexResult<()> {
1050+
let mut ctx = array_session().create_execution_ctx();
1051+
let list = ListArray::try_new(
1052+
buffer![0i32, 1, 2, 3, 4, 5, 6].into_array(),
1053+
buffer![0u32, 2, 5, 5, 7].into_array(),
1054+
Validity::NonNullable,
1055+
&mut ctx,
1056+
)?
1057+
.into_array();
1058+
let output_len = expected_offsets.len() - 1;
1059+
let indices = PiecewiseSequenceArray::try_new(
1060+
starts,
1061+
lengths,
1062+
ConstantArray::new(1u64, 2).into_array(),
1063+
output_len,
1064+
)?;
1065+
let expected = ListArray::try_new(
1066+
expected_elements.into_array(),
1067+
expected_offsets.into_array(),
1068+
Validity::NonNullable,
1069+
&mut ctx,
1070+
)?;
1071+
1072+
assert_arrays_eq!(list.take(indices.into_array())?, expected, &mut ctx);
1073+
Ok(())
1074+
}
1075+
11171076
#[test]
11181077
fn piecewise_sequence_take() {
11191078
let mut ctx = array_session().create_execution_ctx();

0 commit comments

Comments
 (0)