This is an automated email from the ASF dual-hosted git repository.

tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 9131c30a4 feat: take kernel for RunArray (#3622)
9131c30a4 is described below

commit 9131c30a40cfb14d3e9454eac0f9b80e000b152b
Author: askoa <[email protected]>
AuthorDate: Mon Feb 6 06:03:02 2023 -0500

    feat: take kernel for RunArray (#3622)
    
    * Rebase to master branch
    
    * Add take_run kernel and include benchmarks for take_run and primitive run 
accessor.
    
    * fix ci issues
    
    * fix ci issues
    
    * fix clippy issues
    
    * fix clippy
    
    * Alternative approach to find physical indices for given logical indices
    
    * Remove unused code, refactor benchmarks
    
    * minor fixes
    
    * some refactor
    
    * add some comments
    
    * doc fixes
    
    * change benchmkar parameters
    
    * add test for run_iterator
    
    * add comments
    
    * Fix some PR suggestions
    
    * incorporte pr suggestion
    
    ---------
    
    Co-authored-by: ask <ask@local>
    Co-authored-by: Raphael Taylor-Davies <[email protected]>
---
 arrow-array/src/array/run_array.rs                 | 195 ++++++++++++++++-----
 arrow-array/src/cast.rs                            | 126 +++++++++++++
 arrow-array/src/run_iterator.rs                    |  77 +++++++-
 arrow-select/src/take.rs                           |  98 ++++++++++-
 arrow/Cargo.toml                                   |  17 ++
 arrow/benches/primitive_run_accessor.rs            |  57 ++++++
 arrow/benches/primitive_run_take.rs                |  79 +++++++++
 arrow/benches/string_run_builder.rs                |  22 +--
 ...tring_run_builder.rs => string_run_iterator.rs} |  62 ++++---
 arrow/benches/take_kernels.rs                      |   7 +
 arrow/src/util/bench_util.rs                       |  68 +++++++
 11 files changed, 716 insertions(+), 92 deletions(-)

diff --git a/arrow-array/src/array/run_array.rs 
b/arrow-array/src/array/run_array.rs
index 8cc1f676b..2e378c90f 100644
--- a/arrow-array/src/array/run_array.rs
+++ b/arrow-array/src/array/run_array.rs
@@ -143,6 +143,96 @@ impl<R: RunEndIndexType> RunArray<R> {
             values,
         })
     }
+
+    /// Returns index to the physical array for the given index to the logical 
array.
+    /// Performs a binary search on the run_ends array for the input index.
+    #[inline]
+    pub fn get_physical_index(&self, logical_index: usize) -> Option<usize> {
+        if logical_index >= self.len() {
+            return None;
+        }
+        let mut st: usize = 0;
+        let mut en: usize = self.run_ends().len();
+        while st + 1 < en {
+            let mid: usize = (st + en) / 2;
+            if logical_index
+                < unsafe {
+                    // Safety:
+                    // The value of mid will always be between 1 and len - 1,
+                    // where len is length of run ends array.
+                    // This is based on the fact that `st` starts with 0 and
+                    // `en` starts with len. The condition `st + 1 < en` 
ensures
+                    // `st` and `en` differs atleast by two. So the value of 
`mid`
+                    // will never be either `st` or `en`
+                    self.run_ends().value_unchecked(mid - 1).as_usize()
+                }
+            {
+                en = mid
+            } else {
+                st = mid
+            }
+        }
+        Some(st)
+    }
+
+    /// Returns the physical indices of the input logical indices. Returns 
error if any of the logical
+    /// index cannot be converted to physical index. The logical indices are 
sorted and iterated along
+    /// with run_ends array to find matching physical index. The approach used 
here was chosen over
+    /// finding physical index for each logical index using binary search 
using the function
+    /// `get_physical_index`. Running benchmarks on both approaches showed 
that the approach used here
+    /// scaled well for larger inputs.
+    /// See 
<https://github.com/apache/arrow-rs/pull/3622#issuecomment-1407753727> for more 
details.
+    #[inline]
+    pub fn get_physical_indices<I>(
+        &self,
+        logical_indices: &[I],
+    ) -> Result<Vec<usize>, ArrowError>
+    where
+        I: ArrowNativeType,
+    {
+        let indices_len = logical_indices.len();
+
+        // `ordered_indices` store index into `logical_indices` and can be used
+        // to iterate `logical_indices` in sorted order.
+        let mut ordered_indices: Vec<usize> = (0..indices_len).collect();
+
+        // Instead of sorting `logical_idices` directly, sort the 
`ordered_indices`
+        // whose values are index of `logical_indices`
+        ordered_indices.sort_unstable_by(|lhs, rhs| {
+            logical_indices[*lhs]
+                .partial_cmp(&logical_indices[*rhs])
+                .unwrap()
+        });
+
+        let mut physical_indices = vec![0; indices_len];
+
+        let mut ordered_index = 0_usize;
+        for (physical_index, run_end) in 
self.run_ends.values().iter().enumerate() {
+            // Get the run end index of current physical index
+            let run_end_value = run_end.as_usize();
+
+            // All the `logical_indices` that are less than current run end 
index
+            // belongs to current physical index.
+            while ordered_index < indices_len
+                && logical_indices[ordered_indices[ordered_index]].as_usize()
+                    < run_end_value
+            {
+                physical_indices[ordered_indices[ordered_index]] = 
physical_index;
+                ordered_index += 1;
+            }
+        }
+
+        // If there are input values >= run_ends.last_value then we'll not be 
able to convert
+        // all logical indices to physical indices.
+        if ordered_index < logical_indices.len() {
+            let logical_index =
+                logical_indices[ordered_indices[ordered_index]].as_usize();
+            return Err(ArrowError::InvalidArgumentError(format!(
+                "Cannot convert all logical indices to physical indices. The 
logical index cannot be converted is {logical_index}.",
+            )));
+        }
+        Ok(physical_indices)
+    }
 }
 
 impl<R: RunEndIndexType> From<ArrayData> for RunArray<R> {
@@ -348,37 +438,6 @@ impl<'a, R: RunEndIndexType, V> TypedRunArray<'a, R, V> {
     pub fn values(&self) -> &'a V {
         self.values
     }
-
-    /// Returns index to the physcial array for the given index to the logical 
array.
-    /// Performs a binary search on the run_ends array for the input index.
-    #[inline]
-    pub fn get_physical_index(&self, logical_index: usize) -> Option<usize> {
-        if logical_index >= self.run_array.len() {
-            return None;
-        }
-        let mut st: usize = 0;
-        let mut en: usize = self.run_ends().len();
-        while st + 1 < en {
-            let mid: usize = (st + en) / 2;
-            if logical_index
-                < unsafe {
-                    // Safety:
-                    // The value of mid will always be between 1 and len - 1,
-                    // where len is length of run ends array.
-                    // This is based on the fact that `st` starts with 0 and
-                    // `en` starts with len. The condition `st + 1 < en` 
ensures
-                    // `st` and `en` differs atleast by two. So the value of 
`mid`
-                    // will never be either `st` or `en`
-                    self.run_ends().value_unchecked(mid - 1).as_usize()
-                }
-            {
-                en = mid
-            } else {
-                st = mid
-            }
-        }
-        Some(st)
-    }
 }
 
 impl<'a, R: RunEndIndexType, V: Sync> Array for TypedRunArray<'a, R, V> {
@@ -417,7 +476,7 @@ where
     }
 
     unsafe fn value_unchecked(&self, logical_index: usize) -> Self::Item {
-        let physical_index = self.get_physical_index(logical_index).unwrap();
+        let physical_index = 
self.run_array.get_physical_index(logical_index).unwrap();
         self.values().value_unchecked(physical_index)
     }
 }
@@ -447,13 +506,15 @@ mod tests {
 
     use super::*;
     use crate::builder::PrimitiveRunBuilder;
+    use crate::cast::as_primitive_array;
     use crate::types::{Int16Type, Int32Type, Int8Type, UInt32Type};
     use crate::{Array, Int16Array, Int32Array, StringArray};
 
-    fn build_input_array(approx_size: usize) -> Vec<Option<i32>> {
+    fn build_input_array(size: usize) -> Vec<Option<i32>> {
         // The input array is created by shuffling and repeating
         // the seed values random number of times.
         let mut seed: Vec<Option<i32>> = vec![
+            None,
             None,
             None,
             Some(1),
@@ -462,26 +523,32 @@ mod tests {
             Some(4),
             Some(5),
             Some(6),
+            Some(7),
+            Some(8),
+            Some(9),
         ];
+        let mut result: Vec<Option<i32>> = Vec::with_capacity(size);
         let mut ix = 0;
-        let mut result: Vec<Option<i32>> = Vec::with_capacity(approx_size);
         let mut rng = thread_rng();
-        while result.len() < approx_size {
+        // run length can go up to 8. Cap the max run length for smaller 
arrays to size / 2.
+        let max_run_length = 8_usize.min(1_usize.max(size / 2));
+        while result.len() < size {
             // shuffle the seed array if all the values are iterated.
             if ix == 0 {
                 seed.shuffle(&mut rng);
             }
-            // repeat the items between 1 and 7 times.
-            let num = rand::thread_rng().gen_range(1..8);
+            // repeat the items between 1 and 8 times. Cap the length for 
smaller sized arrays
+            let num =
+                
max_run_length.min(rand::thread_rng().gen_range(1..=max_run_length));
             for _ in 0..num {
                 result.push(seed[ix]);
             }
             ix += 1;
-            if ix == 8 {
+            if ix == seed.len() {
                 ix = 0
             }
         }
-        println!("Size of input array: {}", result.len());
+        result.resize(size, None);
         result
     }
 
@@ -718,14 +785,62 @@ mod tests {
         let run_array = builder.finish();
         let typed = run_array.downcast::<PrimitiveArray<Int32Type>>().unwrap();
 
+        // Access every index and check if the value in the input array 
matches returned value.
         for (i, inp_val) in input_array.iter().enumerate() {
             if let Some(val) = inp_val {
                 let actual = typed.value(i);
                 assert_eq!(*val, actual)
             } else {
-                let physical_ix = typed.get_physical_index(i).unwrap();
+                let physical_ix = run_array.get_physical_index(i).unwrap();
                 assert!(typed.values().is_null(physical_ix));
             };
         }
     }
+
+    #[test]
+    fn test_get_physical_indices() {
+        // Test for logical lengths starting from 10 to 250 increasing by 10
+        for logical_len in (0..250).step_by(10) {
+            let input_array = build_input_array(logical_len);
+
+            // create run array using input_array
+            let mut builder = PrimitiveRunBuilder::<Int32Type, 
Int32Type>::new();
+            builder.extend(input_array.clone().into_iter());
+
+            let run_array = builder.finish();
+            let physical_values_array =
+                as_primitive_array::<Int32Type>(run_array.values());
+
+            // create an array consisting of all the indices repeated twice 
and shuffled.
+            let mut logical_indices: Vec<u32> = (0_u32..(logical_len as 
u32)).collect();
+            // add same indices once more
+            logical_indices.append(&mut logical_indices.clone());
+            let mut rng = thread_rng();
+            logical_indices.shuffle(&mut rng);
+
+            let physical_indices =
+                run_array.get_physical_indices(&logical_indices).unwrap();
+
+            assert_eq!(logical_indices.len(), physical_indices.len());
+
+            // check value in logical index in the input_array matches 
physical index in typed_run_array
+            logical_indices
+                .iter()
+                .map(|f| f.as_usize())
+                .zip(physical_indices.iter())
+                .for_each(|(logical_ix, physical_ix)| {
+                    let expected = input_array[logical_ix];
+                    match expected {
+                        Some(val) => {
+                            
assert!(physical_values_array.is_valid(*physical_ix));
+                            let actual = 
physical_values_array.value(*physical_ix);
+                            assert_eq!(val, actual);
+                        }
+                        None => {
+                            
assert!(physical_values_array.is_null(*physical_ix))
+                        }
+                    };
+                });
+        }
+    }
 }
diff --git a/arrow-array/src/cast.rs b/arrow-array/src/cast.rs
index 02d5432c1..4bae4932c 100644
--- a/arrow-array/src/cast.rs
+++ b/arrow-array/src/cast.rs
@@ -95,6 +95,53 @@ macro_rules! downcast_integer {
     };
 }
 
+/// Given one or more expressions evaluating to an integer [`DataType`] 
invokes the provided macro
+/// `m` with the corresponding integer [`RunEndIndexType`], followed by any 
additional arguments
+///
+/// ```
+/// # use arrow_array::{downcast_primitive, ArrowPrimitiveType, 
downcast_run_end_index};
+/// # use arrow_schema::{DataType, Field};
+///
+/// macro_rules! run_end_size_helper {
+///   ($t:ty, $o:ty) => {
+///       std::mem::size_of::<<$t as ArrowPrimitiveType>::Native>() as $o
+///   };
+/// }
+///
+/// fn run_end_index_size(t: &DataType) -> u8 {
+///     match t {
+///         DataType::RunEndEncoded(k, _) => downcast_run_end_index! {
+///             k.data_type() => (run_end_size_helper, u8),
+///             _ => unreachable!(),
+///         },
+///         _ => u8::MAX,
+///     }
+/// }
+///
+/// 
assert_eq!(run_end_index_size(&DataType::RunEndEncoded(Box::new(Field::new("a", 
DataType::Int32, false)), Box::new(Field::new("b", DataType::Utf8, true)))), 4);
+/// 
assert_eq!(run_end_index_size(&DataType::RunEndEncoded(Box::new(Field::new("a", 
DataType::Int64, false)), Box::new(Field::new("b", DataType::Utf8, true)))), 8);
+/// 
assert_eq!(run_end_index_size(&DataType::RunEndEncoded(Box::new(Field::new("a", 
DataType::Int16, false)), Box::new(Field::new("b", DataType::Utf8, true)))), 2);
+/// ```
+///
+/// [`DataType`]: arrow_schema::DataType
+#[macro_export]
+macro_rules! downcast_run_end_index {
+    ($($data_type:expr),+ => ($m:path $(, $args:tt)*), $($($p:pat),+ => 
$fallback:expr $(,)*)*) => {
+        match ($($data_type),+) {
+            $crate::repeat_pat!(arrow_schema::DataType::Int16, 
$($data_type),+) => {
+                $m!($crate::types::Int16Type $(, $args)*)
+            }
+            $crate::repeat_pat!(arrow_schema::DataType::Int32, 
$($data_type),+) => {
+                $m!($crate::types::Int32Type $(, $args)*)
+            }
+            $crate::repeat_pat!(arrow_schema::DataType::Int64, 
$($data_type),+) => {
+                $m!($crate::types::Int64Type $(, $args)*)
+            }
+            $(($($p),+) => $fallback,)*
+        }
+    };
+}
+
 /// Given one or more expressions evaluating to primitive [`DataType`] invokes 
the provided macro
 /// `m` with the corresponding [`ArrowPrimitiveType`], followed by any 
additional arguments
 ///
@@ -449,6 +496,85 @@ where
         .expect("Unable to downcast to dictionary array")
 }
 
+/// Force downcast of an [`Array`], such as an [`ArrayRef`] to
+/// [`RunArray<T>`], panic'ing on failure.
+///
+/// # Example
+///
+/// ```
+/// # use arrow_array::{ArrayRef, RunArray};
+/// # use arrow_array::cast::as_run_array;
+/// # use arrow_array::types::Int32Type;
+///
+/// let arr: RunArray<Int32Type> = vec![Some("foo")].into_iter().collect();
+/// let arr: ArrayRef = std::sync::Arc::new(arr);
+/// let run_array: &RunArray<Int32Type> = as_run_array::<Int32Type>(&arr);
+/// ```
+pub fn as_run_array<T>(arr: &dyn Array) -> &RunArray<T>
+where
+    T: RunEndIndexType,
+{
+    arr.as_any()
+        .downcast_ref::<RunArray<T>>()
+        .expect("Unable to downcast to run array")
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! downcast_run_array_helper {
+    ($t:ty, $($values:ident),+, $e:block) => {{
+        $(let $values = $crate::cast::as_run_array::<$t>($values);)+
+        $e
+    }};
+}
+
+/// Downcast an [`Array`] to a [`RunArray`] based on its [`DataType`], accepts
+/// a number of subsequent patterns to match the data type
+///
+/// ```
+/// # use arrow_array::{Array, StringArray, downcast_run_array, 
cast::as_string_array};
+/// # use arrow_schema::DataType;
+///
+/// fn print_strings(array: &dyn Array) {
+///     downcast_run_array!(
+///         array => match array.values().data_type() {
+///             DataType::Utf8 => {
+///                 for v in array.downcast::<StringArray>().unwrap() {
+///                     println!("{:?}", v);
+///                 }
+///             }
+///             t => println!("Unsupported run array value type {}", t),
+///         },
+///         DataType::Utf8 => {
+///             for v in as_string_array(array) {
+///                 println!("{:?}", v);
+///             }
+///         }
+///         t => println!("Unsupported datatype {}", t)
+///     )
+/// }
+/// ```
+///
+/// [`DataType`]: arrow_schema::DataType
+#[macro_export]
+macro_rules! downcast_run_array {
+    ($values:ident => $e:expr, $($p:pat => $fallback:expr $(,)*)*) => {
+        downcast_run_array!($values => {$e} $($p => $fallback)*)
+    };
+
+    ($values:ident => $e:block $($p:pat => $fallback:expr $(,)*)*) => {
+        match $values.data_type() {
+            arrow_schema::DataType::RunEndEncoded(k, _) => {
+                $crate::downcast_run_end_index! {
+                    k.data_type() => ($crate::downcast_run_array_helper, 
$values, $e),
+                    k => unreachable!("unsupported run end index type: {}", k)
+                }
+            }
+            $($p => $fallback,)*
+        }
+    }
+}
+
 /// Force downcast of an [`Array`], such as an [`ArrayRef`] to
 /// [`GenericListArray<T>`], panic'ing on failure.
 pub fn as_generic_list_array<S: OffsetSizeTrait>(
diff --git a/arrow-array/src/run_iterator.rs b/arrow-array/src/run_iterator.rs
index 6a7b785fe..8bad85a9f 100644
--- a/arrow-array/src/run_iterator.rs
+++ b/arrow-array/src/run_iterator.rs
@@ -149,7 +149,6 @@ where
             // reason the next value can be accessed by decrementing physical 
index once.
             self.current_end_physical -= 1;
         }
-
         Some(if self.array.values().is_null(self.current_end_physical) {
             None
         } else {
@@ -180,6 +179,8 @@ where
 
 #[cfg(test)]
 mod tests {
+    use rand::{seq::SliceRandom, thread_rng, Rng};
+
     use crate::{
         array::{Int32Array, StringArray},
         builder::PrimitiveRunBuilder,
@@ -187,6 +188,48 @@ mod tests {
         Int64RunArray,
     };
 
+    fn build_input_array(size: usize) -> Vec<Option<i32>> {
+        // The input array is created by shuffling and repeating
+        // the seed values random number of times.
+        let mut seed: Vec<Option<i32>> = vec![
+            None,
+            None,
+            None,
+            Some(1),
+            Some(2),
+            Some(3),
+            Some(4),
+            Some(5),
+            Some(6),
+            Some(7),
+            Some(8),
+            Some(9),
+        ];
+        let mut result: Vec<Option<i32>> = Vec::with_capacity(size);
+        let mut ix = 0;
+        let mut rng = thread_rng();
+        // run length can go up to 8. Cap the max run length for smaller 
arrays to size / 2.
+        let max_run_length = 8_usize.min(1_usize.max(size / 2));
+        while result.len() < size {
+            // shuffle the seed array if all the values are iterated.
+            if ix == 0 {
+                seed.shuffle(&mut rng);
+            }
+            // repeat the items between 1 and 8 times. Cap the length for 
smaller sized arrays
+            let num =
+                
max_run_length.min(rand::thread_rng().gen_range(1..=max_run_length));
+            for _ in 0..num {
+                result.push(seed[ix]);
+            }
+            ix += 1;
+            if ix == seed.len() {
+                ix = 0
+            }
+        }
+        result.resize(size, None);
+        result
+    }
+
     #[test]
     fn test_primitive_array_iter_round_trip() {
         let mut input_vec = vec![
@@ -239,6 +282,38 @@ mod tests {
         assert_eq!(None, iter.next());
     }
 
+    #[test]
+    fn test_run_iterator_comprehensive() {
+        // Test forward and backward iterator for different array lengths.
+        let logical_lengths = vec![1_usize, 2, 3, 4, 15, 16, 17, 63, 64, 65];
+
+        for logical_len in logical_lengths {
+            let input_array = build_input_array(logical_len);
+
+            let mut run_array_builder =
+                PrimitiveRunBuilder::<Int32Type, Int32Type>::new();
+            run_array_builder.extend(input_array.iter().copied());
+            let run_array = run_array_builder.finish();
+            let typed_array = run_array.downcast::<Int32Array>().unwrap();
+
+            // test forward iterator
+            let mut input_iter = input_array.iter().copied();
+            let mut run_array_iter = typed_array.into_iter();
+            for _ in 0..logical_len {
+                assert_eq!(input_iter.next(), run_array_iter.next());
+            }
+            assert_eq!(None, run_array_iter.next());
+
+            // test reverse iterator
+            let mut input_iter = input_array.iter().rev().copied();
+            let mut run_array_iter = typed_array.into_iter().rev();
+            for _ in 0..logical_len {
+                assert_eq!(input_iter.next(), run_array_iter.next());
+            }
+            assert_eq!(None, run_array_iter.next());
+        }
+    }
+
     #[test]
     fn test_string_array_iter_round_trip() {
         let input_vec = vec!["ab", "ab", "ba", "cc", "cc"];
diff --git a/arrow-select/src/take.rs b/arrow-select/src/take.rs
index d8989fa48..f8668b56e 100644
--- a/arrow-select/src/take.rs
+++ b/arrow-select/src/take.rs
@@ -19,13 +19,15 @@
 
 use std::sync::Arc;
 
-use arrow_array::types::*;
 use arrow_array::*;
+use arrow_array::{builder::PrimitiveRunBuilder, types::*};
 use arrow_buffer::{bit_util, ArrowNativeType, Buffer, MutableBuffer};
 use arrow_data::{ArrayData, ArrayDataBuilder};
 use arrow_schema::{ArrowError, DataType, Field};
 
-use arrow_array::cast::{as_generic_binary_array, as_largestring_array, 
as_string_array};
+use arrow_array::cast::{
+    as_generic_binary_array, as_largestring_array, as_primitive_array, 
as_string_array,
+};
 use num::{ToPrimitive, Zero};
 
 /// Take elements by index from [Array], creating a new [Array] from those 
indexes.
@@ -201,6 +203,10 @@ where
             values => Ok(Arc::new(take_dict(values, indices)?)),
             t => unimplemented!("Take not supported for dictionary type {:?}", 
t)
         }
+        DataType::RunEndEncoded(_, _) => downcast_run_array! {
+            values => Ok(Arc::new(take_run(values, indices)?)),
+            t => unimplemented!("Take not supported for run type {:?}", t)
+        }
         DataType::Binary => {
             Ok(Arc::new(take_bytes(as_generic_binary_array::<i32>(values), 
indices)?))
         }
@@ -810,6 +816,72 @@ where
     Ok(DictionaryArray::<T>::from(data))
 }
 
+macro_rules! primitive_run_take {
+    ($t:ty, $o:ty, $indices:ident, $value:ident) => {
+        take_primitive_run_values::<$o, $t>(
+            $indices,
+            as_primitive_array::<$t>($value.values()),
+        )
+    };
+}
+
+/// `take` implementation for run arrays
+///
+/// Finds physical indices for the given logical indices and builds output run 
array
+/// by taking values in the input run array at the physical indices.
+/// for e.g. an input `RunArray{ run_ends = [2,4,6,8], values=[1,2,1,2] }` and 
`indices=[2,7]`
+/// would be converted to `physical_indices=[1,3]` which will be used to build
+/// output `RunArray{ run_ends=[2], values=[2] }`
+
+fn take_run<T, I>(
+    run_array: &RunArray<T>,
+    logical_indices: &PrimitiveArray<I>,
+) -> Result<RunArray<T>, ArrowError>
+where
+    T: RunEndIndexType,
+    T::Native: num::Num,
+    I: ArrowPrimitiveType,
+    I::Native: ToPrimitive,
+{
+    match run_array.data_type() {
+        DataType::RunEndEncoded(_, fl) => {
+            let physical_indices =
+                run_array.get_physical_indices(logical_indices.values())?;
+
+            downcast_primitive! {
+                fl.data_type() => (primitive_run_take, T, physical_indices, 
run_array),
+                dt => Err(ArrowError::NotYetImplemented(format!("take_run is 
not implemented for {dt:?}")))
+            }
+        }
+        dt => Err(ArrowError::InvalidArgumentError(format!(
+            "Expected DataType::RunEndEncoded found {dt:?}"
+        ))),
+    }
+}
+
+// Builds a `RunArray` by taking values from given array for the given indices.
+fn take_primitive_run_values<R, V>(
+    physical_indices: Vec<usize>,
+    values: &PrimitiveArray<V>,
+) -> Result<RunArray<R>, ArrowError>
+where
+    R: RunEndIndexType,
+    V: ArrowPrimitiveType,
+{
+    let mut builder = PrimitiveRunBuilder::<R, V>::new();
+    let values_len = values.len();
+    for ix in physical_indices {
+        if ix >= values_len {
+            return Err(ArrowError::InvalidArgumentError("The requested index 
{ix} is out of bounds for values array with length {values_len}".to_string()));
+        } else if values.is_null(ix) {
+            builder.append_null()
+        } else {
+            builder.append_value(values.value(ix))
+        }
+    }
+    Ok(builder.finish())
+}
+
 /// Takes/filters a list array's inner data using the offsets of the list 
array.
 ///
 /// Where a list array has indices `[0,2,5,10]`, taking indices of `[2,0]` 
returns
@@ -2086,6 +2158,28 @@ mod tests {
         assert_eq!(null_buf.as_slice(), &[0b11111111]);
     }
 
+    #[test]
+    fn test_take_runs() {
+        let logical_array: Vec<i32> = vec![1_i32, 1, 2, 2, 1, 1, 2, 2, 1, 1, 
2, 2];
+
+        let mut builder = PrimitiveRunBuilder::<Int32Type, Int32Type>::new();
+        builder.extend(logical_array.into_iter().map(Some));
+        let run_array = builder.finish();
+
+        let take_indices: PrimitiveArray<Int32Type> =
+            vec![2, 7, 10].into_iter().collect();
+
+        let take_out = take_run(&run_array, &take_indices).unwrap();
+
+        assert_eq!(take_out.len(), 3);
+
+        assert_eq!(take_out.run_ends().len(), 1);
+        assert_eq!(take_out.run_ends().value(0), 3);
+
+        let take_out_values = 
as_primitive_array::<Int32Type>(take_out.values());
+        assert_eq!(take_out_values.value(0), 2);
+    }
+
     #[test]
     fn test_take_value_index_from_fixed_list() {
         let list = FixedSizeListArray::from_iter_primitive::<Int32Type, _, _>(
diff --git a/arrow/Cargo.toml b/arrow/Cargo.toml
index 57e3907a2..f86ec09a9 100644
--- a/arrow/Cargo.toml
+++ b/arrow/Cargo.toml
@@ -102,6 +102,8 @@ half = { version = "2.1", default-features = false, 
features = ["num-traits"] }
 rand = { version = "0.8", default-features = false, features = ["std", 
"std_rng"] }
 tempfile = { version = "3", default-features = false }
 
+
+
 [build-dependencies]
 
 [[example]]
@@ -240,6 +242,21 @@ harness = false
 [[bench]]
 name = "string_run_builder"
 harness = false
+required-features = ["test_utils"]
+
+[[bench]]
+name = "string_run_iterator"
+harness = false
+
+[[bench]]
+name = "primitive_run_accessor"
+harness = false
+required-features = ["test_utils"]
+
+[[bench]]
+name = "primitive_run_take"
+harness = false
+required-features = ["test_utils"]
 
 [[bench]]
 name = "substring_kernels"
diff --git a/arrow/benches/primitive_run_accessor.rs 
b/arrow/benches/primitive_run_accessor.rs
new file mode 100644
index 000000000..868c314f9
--- /dev/null
+++ b/arrow/benches/primitive_run_accessor.rs
@@ -0,0 +1,57 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use arrow::datatypes::Int32Type;
+use arrow::{array::PrimitiveArray, 
util::bench_util::create_primitive_run_array};
+use arrow_array::ArrayAccessor;
+use criterion::{criterion_group, criterion_main, Criterion};
+
+fn criterion_benchmark(c: &mut Criterion) {
+    let mut group = c.benchmark_group("primitive_run_accessor");
+
+    let mut do_bench = |physical_array_len: usize, logical_array_len: usize| {
+        group.bench_function(
+            format!(
+                "(run_array_len:{logical_array_len}, 
physical_array_len:{physical_array_len})"),
+            |b| {
+                let run_array = create_primitive_run_array::<Int32Type, 
Int32Type>(
+                    logical_array_len,
+                    physical_array_len,
+                );
+                let typed = run_array
+                    .downcast::<PrimitiveArray<Int32Type>>()
+                    .unwrap();
+                b.iter(|| {
+                    for i in 0..logical_array_len {
+                        let _ = unsafe { typed.value_unchecked(i) };
+                    }
+                })
+            },
+        );
+    };
+
+    do_bench(128, 512);
+    do_bench(256, 1024);
+    do_bench(512, 2048);
+    do_bench(1024, 4096);
+    do_bench(2048, 8192);
+
+    group.finish();
+}
+
+criterion_group!(benches, criterion_benchmark);
+criterion_main!(benches);
diff --git a/arrow/benches/primitive_run_take.rs 
b/arrow/benches/primitive_run_take.rs
new file mode 100644
index 000000000..8c9a3fd04
--- /dev/null
+++ b/arrow/benches/primitive_run_take.rs
@@ -0,0 +1,79 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use arrow::array::UInt32Builder;
+use arrow::compute::take;
+use arrow::datatypes::{Int32Type, Int64Type};
+use arrow::util::bench_util::*;
+use arrow::util::test_util::seedable_rng;
+use arrow_array::UInt32Array;
+use criterion::{criterion_group, criterion_main, Criterion};
+use rand::Rng;
+
+fn create_random_index(size: usize, null_density: f32) -> UInt32Array {
+    let mut rng = seedable_rng();
+    let mut builder = UInt32Builder::with_capacity(size);
+    for _ in 0..size {
+        if rng.gen::<f32>() < null_density {
+            builder.append_null();
+        } else {
+            let value = rng.gen_range::<u32, _>(0u32..size as u32);
+            builder.append_value(value);
+        }
+    }
+    builder.finish()
+}
+
+fn criterion_benchmark(c: &mut Criterion) {
+    let mut group = c.benchmark_group("primitive_run_take");
+
+    let mut do_bench = |physical_array_len: usize,
+                        logical_array_len: usize,
+                        take_len: usize| {
+        let run_array = create_primitive_run_array::<Int32Type, Int64Type>(
+            logical_array_len,
+            physical_array_len,
+        );
+        let indices = create_random_index(take_len, 0.0);
+        group.bench_function(
+            format!(
+                "(run_array_len:{logical_array_len}, 
physical_array_len:{physical_array_len}, take_len:{take_len})"),
+            |b| {
+                b.iter(|| {
+                    criterion::black_box(take(&run_array, &indices, 
None).unwrap());
+                })
+            },
+        );
+    };
+
+    do_bench(64, 512, 512);
+    do_bench(128, 512, 512);
+
+    do_bench(256, 1024, 512);
+    do_bench(256, 1024, 1024);
+
+    do_bench(512, 2048, 512);
+    do_bench(512, 2048, 1024);
+
+    do_bench(1024, 4096, 512);
+    do_bench(1024, 4096, 1024);
+
+    group.finish();
+}
+
+criterion_group!(benches, criterion_benchmark);
+criterion_main!(benches);
diff --git a/arrow/benches/string_run_builder.rs 
b/arrow/benches/string_run_builder.rs
index 2f0401bbe..dda0f35b8 100644
--- a/arrow/benches/string_run_builder.rs
+++ b/arrow/benches/string_run_builder.rs
@@ -17,26 +17,8 @@
 
 use arrow::array::StringRunBuilder;
 use arrow::datatypes::Int32Type;
+use arrow::util::bench_util::create_string_array_for_runs;
 use criterion::{criterion_group, criterion_main, Criterion};
-use rand::{thread_rng, Rng};
-
-fn build_strings(
-    physical_array_len: usize,
-    logical_array_len: usize,
-    string_len: usize,
-) -> Vec<String> {
-    let mut rng = thread_rng();
-    let run_len = logical_array_len / physical_array_len;
-    let mut values: Vec<String> = (0..physical_array_len)
-        .map(|_| (0..string_len).map(|_| rng.gen::<char>()).collect())
-        .flat_map(|s| std::iter::repeat(s).take(run_len))
-        .collect();
-    while values.len() < logical_array_len {
-        let last_val = values[values.len() - 1].clone();
-        values.push(last_val);
-    }
-    values
-}
 
 fn criterion_benchmark(c: &mut Criterion) {
     let mut group = c.benchmark_group("string_run_builder");
@@ -50,7 +32,7 @@ fn criterion_benchmark(c: &mut Criterion) {
                 ),
                 |b| {
                     let strings =
-                        build_strings(physical_array_len, logical_array_len, 
string_len);
+                    create_string_array_for_runs(physical_array_len, 
logical_array_len, string_len);
                     b.iter(|| {
                         let mut builder = 
StringRunBuilder::<Int32Type>::with_capacity(
                             physical_array_len,
diff --git a/arrow/benches/string_run_builder.rs 
b/arrow/benches/string_run_iterator.rs
similarity index 60%
copy from arrow/benches/string_run_builder.rs
copy to arrow/benches/string_run_iterator.rs
index 2f0401bbe..cfa44e66e 100644
--- a/arrow/benches/string_run_builder.rs
+++ b/arrow/benches/string_run_iterator.rs
@@ -15,16 +15,16 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use arrow::array::StringRunBuilder;
+use arrow::array::{Int32RunArray, StringArray, StringRunBuilder};
 use arrow::datatypes::Int32Type;
 use criterion::{criterion_group, criterion_main, Criterion};
 use rand::{thread_rng, Rng};
 
-fn build_strings(
+fn build_strings_runs(
     physical_array_len: usize,
     logical_array_len: usize,
     string_len: usize,
-) -> Vec<String> {
+) -> Int32RunArray {
     let mut rng = thread_rng();
     let run_len = logical_array_len / physical_array_len;
     let mut values: Vec<String> = (0..physical_array_len)
@@ -35,43 +35,47 @@ fn build_strings(
         let last_val = values[values.len() - 1].clone();
         values.push(last_val);
     }
-    values
+    let mut builder = StringRunBuilder::<Int32Type>::with_capacity(
+        physical_array_len,
+        (string_len + 1) * physical_array_len,
+    );
+    builder.extend(values.into_iter().map(Some));
+
+    builder.finish()
 }
 
 fn criterion_benchmark(c: &mut Criterion) {
-    let mut group = c.benchmark_group("string_run_builder");
+    let mut group = c.benchmark_group("string_run_iterator");
 
     let mut do_bench = |physical_array_len: usize,
                         logical_array_len: usize,
                         string_len: usize| {
         group.bench_function(
-                format!(
-                    "(run_array_len:{logical_array_len}, 
physical_array_len:{physical_array_len}, string_len: {string_len})",
-                ),
-                |b| {
-                    let strings =
-                        build_strings(physical_array_len, logical_array_len, 
string_len);
-                    b.iter(|| {
-                        let mut builder = 
StringRunBuilder::<Int32Type>::with_capacity(
-                            physical_array_len,
-                            (string_len + 1) * physical_array_len,
-                        );
+            format!(
+                "(run_array_len:{logical_array_len}, 
physical_array_len:{physical_array_len}, string_len: {string_len})"),
+            |b| {
+                let run_array =
+                    build_strings_runs(physical_array_len, logical_array_len, 
string_len);
+                let typed = run_array.downcast::<StringArray>().unwrap();
+                b.iter(|| {
+                    let iter = typed.into_iter();
+                    for _ in iter {}
+                })
+            },
+        );
+    };
 
-                        for val in &strings {
-                            builder.append_value(val);
-                        }
+    do_bench(256, 1024, 5);
+    do_bench(256, 1024, 25);
+    do_bench(256, 1024, 100);
 
-                        builder.finish();
-                    })
-                },
-            );
-    };
+    do_bench(512, 2048, 5);
+    do_bench(512, 2048, 25);
+    do_bench(512, 2048, 100);
 
-    do_bench(20, 1000, 5);
-    do_bench(100, 1000, 5);
-    do_bench(100, 1000, 10);
-    do_bench(100, 10000, 10);
-    do_bench(100, 10000, 100);
+    do_bench(1024, 4096, 5);
+    do_bench(1024, 4096, 25);
+    do_bench(1024, 4096, 100);
 
     group.finish();
 }
diff --git a/arrow/benches/take_kernels.rs b/arrow/benches/take_kernels.rs
index c4677cc72..731426031 100644
--- a/arrow/benches/take_kernels.rs
+++ b/arrow/benches/take_kernels.rs
@@ -139,6 +139,13 @@ fn add_benchmark(c: &mut Criterion) {
     c.bench_function("take str null values null indices 1024", |b| {
         b.iter(|| bench_take(&values, &indices))
     });
+
+    let values = create_primitive_run_array::<Int32Type, Int32Type>(1024, 512);
+    let indices = create_random_index(1024, 0.0);
+    c.bench_function(
+        "take primitive run logical len: 1024, physical len: 512, indices: 
1024",
+        |b| b.iter(|| bench_take(&values, &indices)),
+    );
 }
 
 criterion_group!(benches, add_benchmark);
diff --git a/arrow/src/util/bench_util.rs b/arrow/src/util/bench_util.rs
index 6420b6346..33552dbe3 100644
--- a/arrow/src/util/bench_util.rs
+++ b/arrow/src/util/bench_util.rs
@@ -22,6 +22,7 @@ use crate::datatypes::*;
 use crate::util::test_util::seedable_rng;
 use arrow_buffer::Buffer;
 use rand::distributions::uniform::SampleUniform;
+use rand::thread_rng;
 use rand::Rng;
 use rand::SeedableRng;
 use rand::{
@@ -145,6 +146,73 @@ pub fn create_string_dict_array<K: ArrowDictionaryKeyType>(
     data.iter().map(|x| x.as_deref()).collect()
 }
 
+/// Create primitive run array for given logical and physical array lengths
+pub fn create_primitive_run_array<R: RunEndIndexType, V: ArrowPrimitiveType>(
+    logical_array_len: usize,
+    physical_array_len: usize,
+) -> RunArray<R> {
+    assert!(logical_array_len >= physical_array_len);
+    // typical length of each run
+    let run_len = logical_array_len / physical_array_len;
+
+    // Some runs should have extra length
+    let mut run_len_extra = logical_array_len % physical_array_len;
+
+    let mut values: Vec<V::Native> = (0..physical_array_len)
+        .flat_map(|s| {
+            let mut take_len = run_len;
+            if run_len_extra > 0 {
+                take_len += 1;
+                run_len_extra -= 1;
+            }
+            std::iter::repeat(V::Native::from_usize(s).unwrap()).take(take_len)
+        })
+        .collect();
+    while values.len() < logical_array_len {
+        let last_val = values[values.len() - 1];
+        values.push(last_val);
+    }
+    let mut builder = PrimitiveRunBuilder::<R, 
V>::with_capacity(physical_array_len);
+    builder.extend(values.into_iter().map(Some));
+
+    builder.finish()
+}
+
+/// Create string array to be used by run array builder. The string array
+/// will result in run array with physial length of `physical_array_len`
+/// and logical length of `logical_array_len`
+pub fn create_string_array_for_runs(
+    physical_array_len: usize,
+    logical_array_len: usize,
+    string_len: usize,
+) -> Vec<String> {
+    assert!(logical_array_len >= physical_array_len);
+    let mut rng = thread_rng();
+
+    // typical length of each run
+    let run_len = logical_array_len / physical_array_len;
+
+    // Some runs should have extra length
+    let mut run_len_extra = logical_array_len % physical_array_len;
+
+    let mut values: Vec<String> = (0..physical_array_len)
+        .map(|_| (0..string_len).map(|_| rng.gen::<char>()).collect())
+        .flat_map(|s| {
+            let mut take_len = run_len;
+            if run_len_extra > 0 {
+                take_len += 1;
+                run_len_extra -= 1;
+            }
+            std::iter::repeat(s).take(take_len)
+        })
+        .collect();
+    while values.len() < logical_array_len {
+        let last_val = values[values.len() - 1].clone();
+        values.push(last_val);
+    }
+    values
+}
+
 /// Creates an random (but fixed-seeded) binary array of a given size and null 
density
 pub fn create_binary_array<Offset: OffsetSizeTrait>(
     size: usize,


Reply via email to