@@ -19,11 +19,13 @@ use num_traits::FromPrimitive;
1919use rand:: RngExt ;
2020use rand:: SeedableRng ;
2121use rand:: rngs:: StdRng ;
22+ use vortex_array:: ArrayRef ;
2223use vortex_array:: ExecutionCtx ;
2324use vortex_array:: IntoArray ;
2425use vortex_array:: RecursiveCanonical ;
2526use vortex_array:: VortexSessionExecute ;
2627use vortex_array:: array_session;
28+ use vortex_array:: arrays:: ChunkedArray ;
2729use vortex_array:: arrays:: ConstantArray ;
2830use vortex_array:: arrays:: FixedSizeListArray ;
2931use 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 ) ]
105212fn 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