Skip to content

Commit 69f5bc5

Browse files
authored
Add take benchmarks for FSL(Chunked) and Chunked(FSL) arrays (#8887)
Benchmarks take over a FixedSizeListArray with chunked elements and over a chunked array of FixedSizeList chunks, with random and sorted indices.
1 parent cbaa3d7 commit 69f5bc5

1 file changed

Lines changed: 107 additions & 0 deletions

File tree

vortex-array/benches/take_fsl.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ use num_traits::FromPrimitive;
1919
use rand::RngExt;
2020
use rand::SeedableRng;
2121
use rand::rngs::StdRng;
22+
use vortex_array::ArrayRef;
2223
use vortex_array::ExecutionCtx;
2324
use vortex_array::IntoArray;
2425
use vortex_array::RecursiveCanonical;
2526
use vortex_array::VortexSessionExecute;
2627
use vortex_array::array_session;
28+
use vortex_array::arrays::ChunkedArray;
2729
use vortex_array::arrays::ConstantArray;
2830
use vortex_array::arrays::FixedSizeListArray;
2931
use vortex_array::arrays::PiecewiseSequenceArray;
@@ -101,6 +103,111 @@ fn create_random_indices(num_indices: usize, max_index: usize) -> Buffer<u64> {
101103
.collect()
102104
}
103105

106+
/// Number of chunks used for the chunked-elements and chunked-of-FSL benchmarks.
107+
const NUM_CHUNKS: usize = 8;
108+
109+
/// Take widths for the chunked benchmarks.
110+
///
111+
/// Together with [`CHUNKED_LIST_SIZES`] these are sized so that the slowest current take path
112+
/// (per-element expansion through the generic chunked take) stays under 1ms per iteration under
113+
/// codspeed simulation, which reports roughly an order of magnitude above local wall time.
114+
const CHUNKED_NUM_INDICES: &[usize] = &[64];
115+
116+
/// Fixed size list lengths for the chunked benchmarks. See [`CHUNKED_NUM_INDICES`].
117+
const CHUNKED_LIST_SIZES: &[usize] = &[8, 16, 32];
118+
119+
/// Creates a `FixedSizeListArray` whose elements child is a `ChunkedArray` with `NUM_CHUNKS`
120+
/// chunks whose boundaries do not line up with list boundaries.
121+
fn create_fsl_with_chunked_elements(list_size: usize, num_lists: usize) -> FixedSizeListArray {
122+
let total_elements = list_size * num_lists;
123+
let chunk_len = total_elements.div_ceil(NUM_CHUNKS);
124+
let mut chunks = Vec::new();
125+
let mut start = 0usize;
126+
while start < total_elements {
127+
let end = (start + chunk_len).min(total_elements);
128+
let chunk: Buffer<i64> = (start as i64..end as i64).collect();
129+
chunks.push(chunk.into_array());
130+
start = end;
131+
}
132+
let dtype = chunks[0].dtype().clone();
133+
let elements = ChunkedArray::try_new(chunks, dtype).unwrap().into_array();
134+
FixedSizeListArray::new(elements, list_size as u32, Validity::NonNullable, num_lists)
135+
}
136+
137+
/// Creates a `ChunkedArray` of `NUM_CHUNKS` `FixedSizeListArray` chunks.
138+
fn create_chunked_fsl(list_size: usize, num_lists: usize) -> ArrayRef {
139+
let lists_per_chunk = num_lists.div_ceil(NUM_CHUNKS);
140+
let mut chunks = Vec::new();
141+
let mut start = 0usize;
142+
while start < num_lists {
143+
let end = (start + lists_per_chunk).min(num_lists);
144+
let elements: Buffer<i64> =
145+
((start * list_size) as i64..(end * list_size) as i64).collect();
146+
chunks.push(
147+
FixedSizeListArray::new(
148+
elements.into_array(),
149+
list_size as u32,
150+
Validity::NonNullable,
151+
end - start,
152+
)
153+
.into_array(),
154+
);
155+
start = end;
156+
}
157+
let dtype = chunks[0].dtype().clone();
158+
ChunkedArray::try_new(chunks, dtype).unwrap().into_array()
159+
}
160+
161+
fn bench_take_array<const LIST_SIZE: usize>(
162+
bencher: Bencher,
163+
num_indices: usize,
164+
array: ArrayRef,
165+
sorted: bool,
166+
) {
167+
let mut indices = create_random_indices(num_indices, NUM_LISTS)
168+
.as_ref()
169+
.to_vec();
170+
if sorted {
171+
indices.sort_unstable();
172+
}
173+
let indices_array = Buffer::from(indices).into_array();
174+
175+
bencher
176+
.counter(BytesCount::of_many::<i64>(num_indices * LIST_SIZE))
177+
.with_inputs(|| (&array, &indices_array, SESSION.create_execution_ctx()))
178+
.bench_refs(|(array, indices, execution_ctx)| {
179+
array
180+
.take((*indices).clone())
181+
.unwrap()
182+
.execute::<RecursiveCanonical>(execution_ctx)
183+
.unwrap()
184+
});
185+
}
186+
187+
#[divan::bench(args = CHUNKED_NUM_INDICES, consts = CHUNKED_LIST_SIZES)]
188+
fn take_fsl_chunked_elements_random<const LIST_SIZE: usize>(bencher: Bencher, num_indices: usize) {
189+
let fsl = create_fsl_with_chunked_elements(LIST_SIZE, NUM_LISTS);
190+
bench_take_array::<LIST_SIZE>(bencher, num_indices, fsl.into_array(), false);
191+
}
192+
193+
#[divan::bench(args = CHUNKED_NUM_INDICES, consts = CHUNKED_LIST_SIZES)]
194+
fn take_fsl_chunked_elements_sorted<const LIST_SIZE: usize>(bencher: Bencher, num_indices: usize) {
195+
let fsl = create_fsl_with_chunked_elements(LIST_SIZE, NUM_LISTS);
196+
bench_take_array::<LIST_SIZE>(bencher, num_indices, fsl.into_array(), true);
197+
}
198+
199+
#[divan::bench(args = CHUNKED_NUM_INDICES, consts = CHUNKED_LIST_SIZES)]
200+
fn take_chunked_fsl_random<const LIST_SIZE: usize>(bencher: Bencher, num_indices: usize) {
201+
let array = create_chunked_fsl(LIST_SIZE, NUM_LISTS);
202+
bench_take_array::<LIST_SIZE>(bencher, num_indices, array, false);
203+
}
204+
205+
#[divan::bench(args = CHUNKED_NUM_INDICES, consts = CHUNKED_LIST_SIZES)]
206+
fn take_chunked_fsl_sorted<const LIST_SIZE: usize>(bencher: Bencher, num_indices: usize) {
207+
let array = create_chunked_fsl(LIST_SIZE, NUM_LISTS);
208+
bench_take_array::<LIST_SIZE>(bencher, num_indices, array, true);
209+
}
210+
104211
#[divan::bench(args = NUM_INDICES, consts = LIST_SIZES)]
105212
fn take_fsl_random<const LIST_SIZE: usize>(bencher: Bencher, num_indices: usize) {
106213
let fsl = create_i64_fsl_with_validity(LIST_SIZE, NUM_LISTS, Validity::NonNullable);

0 commit comments

Comments
 (0)