Skip to main content

arrow/util/
bench_util.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: See the NOTICE file distributed with this work for additional information regarding copyright ownership
3// SPDX-FileContributor: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements
4
5//! Utils to make benchmarking easier
6
7use crate::array::*;
8use crate::datatypes::*;
9use crate::util::test_util::seedable_rng;
10use arrow_buffer::{Buffer, IntervalMonthDayNano, NullBuffer, i256};
11use arrow_schema::Field;
12use half::f16;
13use rand::RngExt;
14use rand::SeedableRng;
15use rand::distr::uniform::SampleUniform;
16use rand::rng;
17use rand::{
18    distr::{Alphanumeric, Distribution, SampleString, StandardUniform},
19    prelude::StdRng,
20};
21use std::ops::Range;
22use std::sync::Arc;
23
24fn f16_random(rng: &mut StdRng) -> f16 {
25    // Otherwise it can round up to 1.0 even though we should generate in [0, 1)
26    // See: https://github.com/VoidStarKat/half-rs/issues/152
27    let one_next_down = f16::from_bits(0x3BFFu16); // 0.9995117
28    f16::from_f32(rng.random::<f32>()).clamp(f16::ZERO, one_next_down)
29}
30
31/// Creates an random (but fixed-seeded) array of a given size and null density
32pub fn create_primitive_array<T>(size: usize, null_density: f32) -> PrimitiveArray<T>
33where
34    T: ArrowPrimitiveType,
35    StandardUniform: Distribution<T::Native>,
36{
37    let mut rng = seedable_rng();
38
39    (0..size)
40        .map(|_| {
41            if rng.random::<f32>() < null_density {
42                None
43            } else {
44                Some(rng.random())
45            }
46        })
47        .collect()
48}
49
50/// Same as [`create_primitive_array`] but specialized for f16 since it doesn't
51/// implement the required rand traits.
52///
53/// This may be deprecated/removed in the future if half updates to the required
54/// rand version.
55pub fn create_nullable_f16_array(size: usize, null_density: f32) -> Float16Array {
56    let mut rng = seedable_rng();
57
58    (0..size)
59        .map(|_| {
60            if rng.random::<f32>() < null_density {
61                None
62            } else {
63                Some(f16_random(&mut rng))
64            }
65        })
66        .collect()
67}
68
69/// Creates an random (but fixed-seeded) array of a given size and null density,
70/// all the values located in the given range
71pub fn create_primitive_array_range<T>(
72    size: usize,
73    null_density: f32,
74    value_range: Range<T::Native>,
75) -> PrimitiveArray<T>
76where
77    T: ArrowPrimitiveType,
78    StandardUniform: Distribution<T::Native>,
79    T::Native: SampleUniform,
80{
81    let mut rng = seedable_rng();
82
83    (0..size)
84        .map(|_| {
85            if rng.random::<f32>() < null_density {
86                None
87            } else {
88                Some(rng.random_range(value_range.clone()))
89            }
90        })
91        .collect()
92}
93
94/// Creates a [`PrimitiveArray`] of a given `size` and `null_density`
95/// filling it with random numbers generated using the provided `seed`.
96pub fn create_primitive_array_with_seed<T>(
97    size: usize,
98    null_density: f32,
99    seed: u64,
100) -> PrimitiveArray<T>
101where
102    T: ArrowPrimitiveType,
103    StandardUniform: Distribution<T::Native>,
104{
105    let mut rng = StdRng::seed_from_u64(seed);
106
107    (0..size)
108        .map(|_| {
109            if rng.random::<f32>() < null_density {
110                None
111            } else {
112                Some(rng.random())
113            }
114        })
115        .collect()
116}
117
118/// Creates a [`PrimitiveArray`] of a given `size` and `null_density`
119/// filling it with random [`IntervalMonthDayNano`] generated using the provided `seed`.
120pub fn create_month_day_nano_array_with_seed(
121    size: usize,
122    null_density: f32,
123    seed: u64,
124) -> IntervalMonthDayNanoArray {
125    let mut rng = StdRng::seed_from_u64(seed);
126
127    (0..size)
128        .map(|_| {
129            if rng.random::<f32>() < null_density {
130                None
131            } else {
132                Some(IntervalMonthDayNano::new(
133                    rng.random(),
134                    rng.random(),
135                    rng.random(),
136                ))
137            }
138        })
139        .collect()
140}
141
142/// Creates a random (but fixed-seeded) array of a given size and null density
143pub fn create_boolean_array(size: usize, null_density: f32, true_density: f32) -> BooleanArray
144where
145    StandardUniform: Distribution<bool>,
146{
147    let mut rng = seedable_rng();
148    (0..size)
149        .map(|_| {
150            if rng.random::<f32>() < null_density {
151                None
152            } else {
153                let value = rng.random::<f32>() < true_density;
154                Some(value)
155            }
156        })
157        .collect()
158}
159
160/// Creates a random array of a given size and null density based on the provided seed
161pub fn create_boolean_array_with_seed(
162    size: usize,
163    null_density: f32,
164    true_density: f32,
165    seed: u64,
166) -> BooleanArray
167where
168    StandardUniform: Distribution<bool>,
169{
170    let mut rng = StdRng::seed_from_u64(seed);
171    (0..size)
172        .map(|_| {
173            if rng.random::<f32>() < null_density {
174                None
175            } else {
176                let value = rng.random::<f32>() < true_density;
177                Some(value)
178            }
179        })
180        .collect()
181}
182
183/// Creates a random (but fixed-seeded) [`UInt32Array`] of indices in `0..max_value`.
184///
185/// Intended for use as the `indices` argument to take kernels. `null_density`
186/// controls the fraction of null indices.
187pub fn create_random_index(size: usize, null_density: f32, max_value: usize) -> UInt32Array {
188    create_primitive_array_range::<UInt32Type>(size, null_density, 0..max_value as u32)
189}
190
191/// Creates a random (but fixed-seeded) [`Decimal128Array`] with values drawn
192/// from `0..1_000_000_000`.
193pub fn create_decimal128_array(size: usize, precision: u8, scale: i8) -> Decimal128Array {
194    let mut rng = seedable_rng();
195    let mut builder = Decimal128Builder::with_capacity(size);
196    for _ in 0..size {
197        builder.append_value(rng.random_range::<i128, _>(0..1_000_000_000));
198    }
199    builder
200        .finish()
201        .with_precision_and_scale(precision, scale)
202        .unwrap()
203}
204
205/// Creates a random (but fixed-seeded) [`Decimal256Array`] with values drawn
206/// from `0..1_000_000_000` (sign-extended into i256).
207pub fn create_decimal256_array(size: usize, precision: u8, scale: i8) -> Decimal256Array {
208    let mut rng = seedable_rng();
209    let mut builder = Decimal256Builder::with_capacity(size);
210    let mut bytes = [0; 32];
211    for _ in 0..size {
212        let num = rng.random_range::<i128, _>(0..1_000_000_000);
213        bytes[0..16].clone_from_slice(&num.to_le_bytes());
214        builder.append_value(i256::from_le_bytes(bytes));
215    }
216    builder
217        .finish()
218        .with_precision_and_scale(precision, scale)
219        .unwrap()
220}
221
222/// Creates a random (but fixed-seeded) string array of a given size and null density.
223///
224/// Strings have a random length
225/// between 0 and 400 alphanumeric characters. `0..400` is chosen to cover a wide range of common string lengths,
226/// which have a dramatic impact on performance of some queries, e.g. LIKE/ILIKE/regex.
227pub fn create_string_array<Offset: OffsetSizeTrait>(
228    size: usize,
229    null_density: f32,
230) -> GenericStringArray<Offset> {
231    create_string_array_with_max_len(size, null_density, 400)
232}
233
234/// Creates longer string array with same prefix, the prefix should be larger than 4 bytes,
235/// and the string length should be larger than 12 bytes
236/// so that we can compare the performance with StringViewArray, because StringViewArray has 4 bytes inline for view
237pub fn create_longer_string_array_with_same_prefix<Offset: OffsetSizeTrait>(
238    size: usize,
239    null_density: f32,
240) -> GenericStringArray<Offset> {
241    create_string_array_with_len_range_and_prefix(size, null_density, 13, 100, "prefix_")
242}
243
244/// Creates longer string view array with same prefix, the prefix should be larger than 4 bytes,
245/// and the string length should be larger than 12 bytes
246/// so that we can compare the StringArray performance with StringViewArray, because StringViewArray has 4 bytes inline for view
247pub fn create_longer_string_view_array_with_same_prefix(
248    size: usize,
249    null_density: f32,
250) -> StringViewArray {
251    create_string_view_array_with_len_range_and_prefix(size, null_density, 13, 100, "prefix_")
252}
253
254fn create_string_array_with_len_range_and_prefix<Offset: OffsetSizeTrait>(
255    size: usize,
256    null_density: f32,
257    min_str_len: usize,
258    max_str_len: usize,
259    prefix: &str,
260) -> GenericStringArray<Offset> {
261    create_string_array_with_len_range_and_prefix_and_seed(
262        size,
263        null_density,
264        min_str_len,
265        max_str_len,
266        prefix,
267        42,
268    )
269}
270
271/// Creates a random [`GenericStringArray`] of a given `size` and `null_density`
272/// filling it with random strings with lengths in the specified range,
273/// all starting with the provided `prefix`, generated using the provided `seed`.
274///
275/// # Panics
276///
277/// Panics if `min_str_len > max_str_len` or `prefix.len() > max_str_len`
278pub fn create_string_array_with_len_range_and_prefix_and_seed<Offset: OffsetSizeTrait>(
279    size: usize,
280    null_density: f32,
281    min_str_len: usize,
282    max_str_len: usize,
283    prefix: &str,
284    seed: u64,
285) -> GenericStringArray<Offset> {
286    assert!(
287        min_str_len <= max_str_len,
288        "min_str_len must be <= max_str_len"
289    );
290    assert!(
291        prefix.len() <= max_str_len,
292        "Prefix length must be <= max_str_len"
293    );
294
295    let rng = &mut StdRng::seed_from_u64(seed);
296    (0..size)
297        .map(|_| {
298            if rng.random::<f32>() < null_density {
299                None
300            } else {
301                let remaining_len = rng.random_range(
302                    min_str_len.saturating_sub(prefix.len())..=(max_str_len - prefix.len()),
303                );
304
305                let mut value = prefix.to_string();
306                value.extend(
307                    rng.sample_iter(&Alphanumeric)
308                        .take(remaining_len)
309                        .map(char::from),
310                );
311
312                Some(value)
313            }
314        })
315        .collect()
316}
317/// Creates a string view array of a given range, null density and length
318///
319/// Arguments:
320/// - `size`: number of  string view array
321/// - `null_density`: density of nulls in the string view array
322/// - `range`: range size of each string in the string view array
323/// - `seed`: seed for the random number generator
324pub fn create_string_view_array_with_len_range_and_seed(
325    size: usize,
326    null_density: f32,
327    range: Range<usize>,
328    seed: u64,
329) -> StringViewArray {
330    let rng = &mut StdRng::seed_from_u64(seed);
331    (0..size)
332        .map(|_| {
333            if rng.random::<f32>() < null_density {
334                None
335            } else {
336                let str_len = rng.random_range(range.clone());
337                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
338                let value = String::from_utf8(value).unwrap();
339                Some(value)
340            }
341        })
342        .collect()
343}
344
345fn create_string_view_array_with_len_range_and_prefix(
346    size: usize,
347    null_density: f32,
348    min_str_len: usize,
349    max_str_len: usize,
350    prefix: &str,
351) -> StringViewArray {
352    assert!(
353        min_str_len <= max_str_len,
354        "min_str_len must be <= max_str_len"
355    );
356    assert!(
357        prefix.len() <= max_str_len,
358        "Prefix length must be <= max_str_len"
359    );
360
361    let rng = &mut seedable_rng();
362    (0..size)
363        .map(|_| {
364            if rng.random::<f32>() < null_density {
365                None
366            } else {
367                let remaining_len = rng.random_range(
368                    min_str_len.saturating_sub(prefix.len())..=(max_str_len - prefix.len()),
369                );
370
371                let mut value = prefix.to_string();
372                value.extend(
373                    rng.sample_iter(&Alphanumeric)
374                        .take(remaining_len)
375                        .map(char::from),
376                );
377
378                Some(value)
379            }
380        })
381        .collect()
382}
383
384/// Creates a random (but fixed-seeded) array of rand size with a given max size, null density and length
385pub fn create_string_array_with_max_len<Offset: OffsetSizeTrait>(
386    size: usize,
387    null_density: f32,
388    max_str_len: usize,
389) -> GenericStringArray<Offset> {
390    let rng = &mut seedable_rng();
391    (0..size)
392        .map(|_| {
393            if rng.random::<f32>() < null_density {
394                None
395            } else {
396                let str_len = rng.random_range(0..max_str_len);
397                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
398                let value = String::from_utf8(value).unwrap();
399                Some(value)
400            }
401        })
402        .collect()
403}
404
405/// Creates a random (but fixed-seeded) array of a given size, null density and length
406pub fn create_string_array_with_len<Offset: OffsetSizeTrait>(
407    size: usize,
408    null_density: f32,
409    str_len: usize,
410) -> GenericStringArray<Offset> {
411    let rng = &mut seedable_rng();
412
413    (0..size)
414        .map(|_| {
415            if rng.random::<f32>() < null_density {
416                None
417            } else {
418                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
419                let value = String::from_utf8(value).unwrap();
420                Some(value)
421            }
422        })
423        .collect()
424}
425
426/// Creates a random (but fixed-seeded) string view array of a given size and null density.
427///
428/// See `create_string_array` above for more details.
429pub fn create_string_view_array(size: usize, null_density: f32) -> StringViewArray {
430    create_string_view_array_with_max_len(size, null_density, 400)
431}
432
433/// Creates a random (but fixed-seeded) array of rand size with a given max size, null density and length
434pub fn create_string_view_array_with_max_len(
435    size: usize,
436    null_density: f32,
437    max_str_len: usize,
438) -> StringViewArray {
439    let rng = &mut seedable_rng();
440    (0..size)
441        .map(|_| {
442            if rng.random::<f32>() < null_density {
443                None
444            } else {
445                let str_len = rng.random_range(0..max_str_len);
446                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
447                let value = String::from_utf8(value).unwrap();
448                Some(value)
449            }
450        })
451        .collect()
452}
453
454/// Creates a random (but fixed-seeded) array of a given size, null density and length
455pub fn create_string_view_array_with_fixed_len(
456    size: usize,
457    null_density: f32,
458    str_len: usize,
459) -> StringViewArray {
460    let rng = &mut seedable_rng();
461    (0..size)
462        .map(|_| {
463            if rng.random::<f32>() < null_density {
464                None
465            } else {
466                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
467                let value = String::from_utf8(value).unwrap();
468                Some(value)
469            }
470        })
471        .collect()
472}
473
474/// Creates a random (but fixed-seeded) array of a given size, null density and length
475pub fn create_string_view_array_with_len(
476    size: usize,
477    null_density: f32,
478    str_len: usize,
479    mixed: bool,
480) -> StringViewArray {
481    let rng = &mut seedable_rng();
482
483    let mut lengths = Vec::with_capacity(size);
484
485    // if mixed, we creates first half that string length small than 12 bytes and second half large than 12 bytes
486    if mixed {
487        for _ in 0..size / 2 {
488            lengths.push(rng.random_range(1..12));
489        }
490        for _ in size / 2..size {
491            lengths.push(rng.random_range(12..=std::cmp::max(30, str_len)));
492        }
493    } else {
494        lengths.resize(size, str_len);
495    }
496
497    lengths
498        .into_iter()
499        .map(|len| {
500            if rng.random::<f32>() < null_density {
501                None
502            } else {
503                let value: Vec<u8> = rng.sample_iter(&Alphanumeric).take(len).collect();
504                Some(String::from_utf8(value).unwrap())
505            }
506        })
507        .collect()
508}
509
510/// Creates an random (but fixed-seeded) array of a given size and null density
511/// consisting of random 4 character alphanumeric strings
512pub fn create_string_dict_array<K: ArrowDictionaryKeyType>(
513    size: usize,
514    null_density: f32,
515    str_len: usize,
516) -> DictionaryArray<K> {
517    let rng = &mut seedable_rng();
518
519    let data: Vec<_> = (0..size)
520        .map(|_| {
521            if rng.random::<f32>() < null_density {
522                None
523            } else {
524                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
525                let value = String::from_utf8(value).unwrap();
526                Some(value)
527            }
528        })
529        .collect();
530
531    data.iter().map(|x| x.as_deref()).collect()
532}
533
534/// Create a List/LargeList Array  of primitive values
535///
536/// Arguments:
537/// - `size`: number of lists in the array
538/// - `null_density`: density of nulls in the list array
539/// - `list_null_density`: density of nulls in the primitive arrays inside the lists
540/// - `max_list_size`: maximum size of each list (actual size is random between 0 and max_list_size)
541/// - `seed`: seed for the random number generator
542pub fn create_primitive_list_array_with_seed<O, T>(
543    size: usize,
544    null_density: f32,
545    list_null_density: f32,
546    max_list_size: usize,
547    seed: u64,
548) -> GenericListArray<O>
549where
550    O: OffsetSizeTrait,
551    T: ArrowPrimitiveType,
552    StandardUniform: Distribution<T::Native>,
553{
554    let mut rng = StdRng::seed_from_u64(seed);
555
556    let values = (0..size).map(|_| {
557        if rng.random::<f32>() < null_density {
558            None
559        } else {
560            let list_size = rng.random_range(0..=max_list_size);
561            let list_values: Vec<Option<T::Native>> = (0..list_size)
562                .map(|_| {
563                    if rng.random::<f32>() < list_null_density {
564                        None
565                    } else {
566                        Some(rng.random())
567                    }
568                })
569                .collect();
570            Some(list_values)
571        }
572    });
573
574    GenericListArray::<O>::from_iter_primitive::<T, _, _>(values)
575}
576
577/// Create a List/LargeList Array of primitive values using a fixed seed
578///
579/// See [`create_primitive_list_array_with_seed`] for details on arguments.
580pub fn create_primitive_list_array<O, T>(
581    size: usize,
582    null_density: f32,
583    list_null_density: f32,
584    max_list_size: usize,
585) -> GenericListArray<O>
586where
587    O: OffsetSizeTrait,
588    T: ArrowPrimitiveType,
589    StandardUniform: Distribution<T::Native>,
590{
591    let mut rng = seedable_rng();
592
593    let values = (0..size).map(|_| {
594        if rng.random::<f32>() < null_density {
595            None
596        } else {
597            let list_size = rng.random_range(0..=max_list_size);
598            let list_values: Vec<Option<T::Native>> = (0..list_size)
599                .map(|_| {
600                    if rng.random::<f32>() < list_null_density {
601                        None
602                    } else {
603                        Some(rng.random())
604                    }
605                })
606                .collect();
607            Some(list_values)
608        }
609    });
610
611    GenericListArray::<O>::from_iter_primitive::<T, _, _>(values)
612}
613
614/// Create a ListViewArray of primitive values using a fixed seed
615///
616/// See [`create_primitive_list_array_with_seed`] for details on arguments.
617pub fn create_primitive_list_view_array<O, T>(
618    size: usize,
619    null_density: f32,
620    list_null_density: f32,
621    max_list_size: usize,
622) -> GenericListViewArray<O>
623where
624    T: ArrowPrimitiveType,
625    StandardUniform: Distribution<T::Native>,
626    O: OffsetSizeTrait,
627{
628    let mut rng = seedable_rng();
629
630    let values = (0..size).map(|_| {
631        if rng.random::<f32>() < null_density {
632            None
633        } else {
634            let list_size = rng.random_range(0..=max_list_size);
635            let list_values: Vec<Option<T::Native>> = (0..list_size)
636                .map(|_| {
637                    if rng.random::<f32>() < list_null_density {
638                        None
639                    } else {
640                        Some(rng.random())
641                    }
642                })
643                .collect();
644            Some(list_values)
645        }
646    });
647
648    GenericListViewArray::<O>::from_iter_primitive::<T, _, _>(values)
649}
650
651/// Create primitive run array for given logical and physical array lengths
652///
653/// # Panics
654///
655/// Panics if `logical_array_len < physical_array_len`
656pub fn create_primitive_run_array<R: RunEndIndexType, V: ArrowPrimitiveType>(
657    logical_array_len: usize,
658    physical_array_len: usize,
659) -> RunArray<R> {
660    assert!(logical_array_len >= physical_array_len);
661    // typical length of each run
662    let run_len = logical_array_len / physical_array_len;
663
664    // Some runs should have extra length
665    let mut run_len_extra = logical_array_len % physical_array_len;
666
667    let mut values: Vec<V::Native> = (0..physical_array_len)
668        .flat_map(|s| {
669            let mut take_len = run_len;
670            if run_len_extra > 0 {
671                take_len += 1;
672                run_len_extra -= 1;
673            }
674            std::iter::repeat_n(V::Native::from_usize(s).unwrap(), take_len)
675        })
676        .collect();
677    while values.len() < logical_array_len {
678        let last_val = values[values.len() - 1];
679        values.push(last_val);
680    }
681    let mut builder = PrimitiveRunBuilder::<R, V>::with_capacity(physical_array_len);
682    builder.extend(values.into_iter().map(Some));
683
684    builder.finish()
685}
686
687/// Create string array to be used by run array builder. The string array
688/// will result in run array with physical length of `physical_array_len`
689/// and logical length of `logical_array_len`
690///
691/// # Panics
692///
693/// Panics if `logical_array_len < physical_array_len`
694pub fn create_string_array_for_runs(
695    physical_array_len: usize,
696    logical_array_len: usize,
697    string_len: usize,
698) -> Vec<String> {
699    assert!(logical_array_len >= physical_array_len);
700    let mut rng = rng();
701
702    // typical length of each run
703    let run_len = logical_array_len / physical_array_len;
704
705    // Some runs should have extra length
706    let mut run_len_extra = logical_array_len % physical_array_len;
707
708    let mut values: Vec<String> = (0..physical_array_len)
709        .map(|_| (0..string_len).map(|_| rng.random::<char>()).collect())
710        .flat_map(|s| {
711            let mut take_len = run_len;
712            if run_len_extra > 0 {
713                take_len += 1;
714                run_len_extra -= 1;
715            }
716            std::iter::repeat_n(s, take_len)
717        })
718        .collect();
719    while values.len() < logical_array_len {
720        let last_val = values[values.len() - 1].clone();
721        values.push(last_val);
722    }
723    values
724}
725
726/// Creates an random (but fixed-seeded) binary array of a given size and null density
727pub fn create_binary_array<Offset: OffsetSizeTrait>(
728    size: usize,
729    null_density: f32,
730) -> GenericBinaryArray<Offset> {
731    create_binary_array_with_seed(
732        size,
733        null_density,
734        42, // bytes_seed
735        42, // bytes_length_seed
736    )
737}
738
739/// Creates a random [`GenericBinaryArray`] of a given `size` and `null_density`
740/// filling it with random bytes, generated using the provided `seed`s.
741///
742/// the `bytes_seed` is used to seed the RNG for generating the byte values,
743/// while the `bytes_length_seed` is used to seed the RNG for generating the length of an array item
744///
745/// These values can be the same as they are used to seed different RNGs internally.
746pub fn create_binary_array_with_seed<Offset: OffsetSizeTrait>(
747    size: usize,
748    null_density: f32,
749    bytes_seed: u64,
750    bytes_length_seed: u64,
751) -> GenericBinaryArray<Offset> {
752    let rng = &mut StdRng::seed_from_u64(bytes_seed);
753    let range_rng = &mut StdRng::seed_from_u64(bytes_length_seed);
754
755    (0..size)
756        .map(|_| {
757            if rng.random::<f32>() < null_density {
758                None
759            } else {
760                let value = rng
761                    .sample_iter::<u8, _>(StandardUniform)
762                    .take(range_rng.random_range(0..8))
763                    .collect::<Vec<u8>>();
764                Some(value)
765            }
766        })
767        .collect()
768}
769
770/// Creates a random [`GenericBinaryArray`] of a given `size` and `null_density`
771/// filling it with random bytes with lengths in the specified range,
772/// all starting with the provided `prefix`, generated using the provided `seed`.
773///
774///
775/// # Panics
776///
777/// Panics if `min_len > max_len` or `prefix.len() > max_len`
778pub fn create_binary_array_with_len_range_and_prefix_and_seed<Offset: OffsetSizeTrait>(
779    size: usize,
780    null_density: f32,
781    min_len: usize,
782    max_len: usize,
783    prefix: &[u8],
784    seed: u64,
785) -> GenericBinaryArray<Offset> {
786    assert!(min_len <= max_len, "min_len must be <= max_len");
787    assert!(prefix.len() <= max_len, "Prefix length must be <= max_len");
788
789    let rng = &mut StdRng::seed_from_u64(seed);
790    (0..size)
791        .map(|_| {
792            if rng.random::<f32>() < null_density {
793                None
794            } else {
795                let remaining_len = rng
796                    .random_range(min_len.saturating_sub(prefix.len())..=(max_len - prefix.len()));
797
798                let remaining = rng
799                    .sample_iter::<u8, _>(StandardUniform)
800                    .take(remaining_len);
801
802                let value = prefix.iter().copied().chain(remaining).collect::<Vec<u8>>();
803                Some(value)
804            }
805        })
806        .collect()
807}
808
809/// Creates an random (but fixed-seeded) array of a given size and null density
810pub fn create_fsb_array(size: usize, null_density: f32, value_len: usize) -> FixedSizeBinaryArray {
811    create_fsb_array_with_seed(size, null_density, value_len, 42)
812}
813
814/// Creates a random [`FixedSizeBinaryArray`] of a given size and null density
815/// generated using the provided seed.
816pub fn create_fsb_array_with_seed(
817    size: usize,
818    null_density: f32,
819    value_len: usize,
820    seed: u64,
821) -> FixedSizeBinaryArray {
822    let rng = &mut StdRng::seed_from_u64(seed);
823
824    FixedSizeBinaryArray::try_from_sparse_iter_with_size(
825        (0..size).map(|_| {
826            if rng.random::<f32>() < null_density {
827                None
828            } else {
829                let value = rng
830                    .sample_iter::<u8, _>(StandardUniform)
831                    .take(value_len)
832                    .collect::<Vec<u8>>();
833                Some(value)
834            }
835        }),
836        value_len as i32,
837    )
838    .unwrap()
839}
840
841/// Creates a random (but fixed-seeded) dictionary array of a given size and null density
842/// with the provided values array
843pub fn create_dict_from_values<K>(
844    size: usize,
845    null_density: f32,
846    values: &dyn Array,
847) -> DictionaryArray<K>
848where
849    K: ArrowDictionaryKeyType,
850    StandardUniform: Distribution<K::Native>,
851    K::Native: SampleUniform,
852{
853    let min_key = K::Native::from_usize(0).unwrap();
854    let max_key = K::Native::from_usize(values.len()).unwrap();
855    create_sparse_dict_from_values(size, null_density, values, min_key..max_key)
856}
857
858/// Creates a random (but fixed-seeded) dictionary array of a given size and null density
859/// with the provided values array and key range
860pub fn create_sparse_dict_from_values<K>(
861    size: usize,
862    null_density: f32,
863    values: &dyn Array,
864    key_range: Range<K::Native>,
865) -> DictionaryArray<K>
866where
867    K: ArrowDictionaryKeyType,
868    StandardUniform: Distribution<K::Native>,
869    K::Native: SampleUniform,
870{
871    let mut rng = seedable_rng();
872    let data_type =
873        DataType::Dictionary(Box::new(K::DATA_TYPE), Box::new(values.data_type().clone()));
874
875    let keys: Buffer = (0..size)
876        .map(|_| rng.random_range(key_range.clone()))
877        .collect();
878
879    let nulls: Option<Buffer> = (null_density != 0.).then(|| {
880        (0..size)
881            .map(|_| rng.random_bool((1.0 - null_density) as _))
882            .collect()
883    });
884
885    let data = ArrayDataBuilder::new(data_type)
886        .len(size)
887        .null_bit_buffer(nulls)
888        .add_buffer(keys)
889        .add_child_data(values.to_data())
890        .build()
891        .unwrap();
892
893    DictionaryArray::from(data)
894}
895
896/// Creates a random (but fixed-seeded) f16 array of a given size and nan-value density
897pub fn create_f16_array(size: usize, nan_density: f32) -> Float16Array {
898    let mut rng = seedable_rng();
899
900    (0..size)
901        .map(|_| {
902            if rng.random::<f32>() < nan_density {
903                Some(f16::NAN)
904            } else {
905                Some(f16_random(&mut rng))
906            }
907        })
908        .collect()
909}
910
911/// Creates a random (but fixed-seeded) f32 array of a given size and nan-value density
912pub fn create_f32_array(size: usize, nan_density: f32) -> Float32Array {
913    let mut rng = seedable_rng();
914
915    (0..size)
916        .map(|_| {
917            if rng.random::<f32>() < nan_density {
918                Some(f32::NAN)
919            } else {
920                Some(rng.random())
921            }
922        })
923        .collect()
924}
925
926/// Creates a random (but fixed-seeded) f64 array of a given size and nan-value density
927pub fn create_f64_array(size: usize, nan_density: f32) -> Float64Array {
928    let mut rng = seedable_rng();
929
930    (0..size)
931        .map(|_| {
932            if rng.random::<f32>() < nan_density {
933                Some(f64::NAN)
934            } else {
935                Some(rng.random())
936            }
937        })
938        .collect()
939}
940
941/// Creates a random f64 array of a given size and nan-value density based on a given seed
942pub fn create_f64_array_with_seed(size: usize, nan_density: f32, seed: u64) -> Float64Array {
943    let mut rng = StdRng::seed_from_u64(seed);
944
945    (0..size)
946        .map(|_| {
947            if rng.random::<f32>() < nan_density {
948                Some(f64::NAN)
949            } else {
950                Some(rng.random())
951            }
952        })
953        .collect()
954}
955
956/// Create a FixedSizeList array of primitive values
957///
958/// Arguments:
959/// - `size`: number of fixed-size lists in the array
960/// - `null_density`: density of nulls in the fixed-size list array (row-level nulls)
961/// - `value_null_density`: density of nulls in the primitive values inside each list
962/// - `list_size`: fixed size of each list element
963pub fn create_primitive_fixed_size_list_array<T>(
964    size: usize,
965    null_density: f32,
966    value_null_density: f32,
967    list_size: i32,
968) -> FixedSizeListArray
969where
970    T: ArrowPrimitiveType,
971    StandardUniform: Distribution<T::Native>,
972{
973    let mut rng = seedable_rng();
974    let list_size_usize = usize::try_from(list_size).expect("list_size must be non-negative");
975    let values: PrimitiveArray<T> = (0..size * list_size_usize)
976        .map(|_| {
977            if rng.random::<f32>() < value_null_density {
978                None
979            } else {
980                Some(rng.random())
981            }
982        })
983        .collect();
984    let field = Arc::new(Field::new("item", T::DATA_TYPE, value_null_density > 0.0));
985    let nulls = (null_density > 0.0).then(|| {
986        NullBuffer::new(arrow_buffer::BooleanBuffer::collect_bool(size, |_| {
987            rng.random::<f32>() >= null_density
988        }))
989    });
990    FixedSizeListArray::new(field, list_size, Arc::new(values), nulls)
991}
992
993/// Create a Map array with string keys and primitive values
994///
995/// Arguments:
996/// - `size`: number of map entries in the array
997/// - `null_density`: density of nulls in the map array (row-level nulls)
998/// - `max_map_size`: maximum number of key-value pairs per map entry
999///   (actual size is random between 0 and max_map_size)
1000/// - `key_len`: length of each random string key
1001pub fn create_string_map_array<T>(
1002    size: usize,
1003    null_density: f32,
1004    max_map_size: usize,
1005    key_len: usize,
1006) -> MapArray
1007where
1008    T: ArrowPrimitiveType,
1009    StandardUniform: Distribution<T::Native>,
1010{
1011    let mut rng = seedable_rng();
1012    let mut builder = MapBuilder::new(None, StringBuilder::new(), PrimitiveBuilder::<T>::new());
1013    for _ in 0..size {
1014        if rng.random::<f32>() < null_density {
1015            builder.append(false).unwrap();
1016        } else {
1017            let n = rng.random_range(0..=max_map_size);
1018            for _ in 0..n {
1019                builder
1020                    .keys()
1021                    .append_value(Alphanumeric.sample_string(&mut rng, key_len));
1022                builder.values().append_value(rng.random());
1023            }
1024            builder.append(true).unwrap();
1025        }
1026    }
1027    builder.finish()
1028}
1029
1030/// Creates a random array for the given [`DataType`], `size`, and `null_density`.
1031///
1032/// Useful for building arrays and record batches in benchmarks without
1033/// repeating per-type construction logic. Panics on unsupported types.
1034///
1035/// # Panics
1036///
1037/// Panics if `data_type` is not supported
1038pub fn create_array_for_type(data_type: &DataType, size: usize, null_density: f32) -> ArrayRef {
1039    match data_type {
1040        DataType::Boolean => Arc::new(create_boolean_array(size, null_density, 0.5)),
1041        DataType::Int8 => Arc::new(create_primitive_array::<Int8Type>(size, null_density)),
1042        DataType::Int16 => Arc::new(create_primitive_array::<Int16Type>(size, null_density)),
1043        DataType::Int32 => Arc::new(create_primitive_array::<Int32Type>(size, null_density)),
1044        DataType::Int64 => Arc::new(create_primitive_array::<Int64Type>(size, null_density)),
1045        DataType::UInt8 => Arc::new(create_primitive_array::<UInt8Type>(size, null_density)),
1046        DataType::UInt16 => Arc::new(create_primitive_array::<UInt16Type>(size, null_density)),
1047        DataType::UInt32 => Arc::new(create_primitive_array::<UInt32Type>(size, null_density)),
1048        DataType::UInt64 => Arc::new(create_primitive_array::<UInt64Type>(size, null_density)),
1049        DataType::Float32 => Arc::new(create_primitive_array::<Float32Type>(size, null_density)),
1050        DataType::Float64 => Arc::new(create_primitive_array::<Float64Type>(size, null_density)),
1051        DataType::Utf8 => Arc::new(create_string_array::<i32>(size, null_density)),
1052        DataType::LargeUtf8 => Arc::new(create_string_array::<i64>(size, null_density)),
1053        DataType::Utf8View => Arc::new(create_string_view_array(size, null_density)),
1054        DataType::Binary => Arc::new(create_binary_array::<i32>(size, null_density)),
1055        DataType::LargeBinary => Arc::new(create_binary_array::<i64>(size, null_density)),
1056        DataType::FixedSizeBinary(n) => Arc::new(create_fsb_array(size, null_density, *n as usize)),
1057        other => panic!("unsupported data type for create_array_for_type: {other}"),
1058    }
1059}