Skip to main content

arrow/util/
bench_util.rs

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