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

dheres 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 b46fc9287 Help LLVM vectorize comparison kernel ~50-80% faster (#2646)
b46fc9287 is described below

commit b46fc9287da22534f0599651597fc13cb675ddff
Author: Raphael Taylor-Davies <[email protected]>
AuthorDate: Sun Sep 4 18:55:49 2022 +0100

    Help LLVM vectorize comparison kernel ~50-80% faster (#2646)
    
    * Help LLVM vectorize comparison kernel
    
    * Add MutableBuffer::collect_bool
    
    * Add SAFETY comments
---
 arrow/src/buffer/mutable.rs             | 68 ++++++++++++++++++---------------
 arrow/src/compute/kernels/comparison.rs | 19 ++++-----
 2 files changed, 45 insertions(+), 42 deletions(-)

diff --git a/arrow/src/buffer/mutable.rs b/arrow/src/buffer/mutable.rs
index 1c662ec23..96c837922 100644
--- a/arrow/src/buffer/mutable.rs
+++ b/arrow/src/buffer/mutable.rs
@@ -373,6 +373,41 @@ impl MutableBuffer {
         assert!(len <= self.capacity());
         self.len = len;
     }
+
+    /// Invokes `f` with values `0..len` collecting the boolean results into a 
new `MutableBuffer`
+    ///
+    /// This is similar to `from_trusted_len_iter_bool`, however, can be 
significantly faster
+    /// as it eliminates the conditional `Iterator::next`
+    #[inline]
+    pub(crate) fn collect_bool<F: FnMut(usize) -> bool>(len: usize, mut f: F) 
-> Self {
+        let mut buffer = Self::new(bit_util::ceil(len, 8));
+
+        let chunks = len / 8;
+        let remainder = len % 8;
+        for chunk in 0..chunks {
+            let mut packed = 0;
+            for bit_idx in 0..8 {
+                let i = bit_idx + chunk * 8;
+                packed |= (f(i) as u8) << bit_idx;
+            }
+
+            // SAFETY: Already allocated sufficient capacity
+            unsafe { buffer.push_unchecked(packed) }
+        }
+
+        if remainder != 0 {
+            let mut packed = 0;
+            for bit_idx in 0..remainder {
+                let i = bit_idx + chunks * 8;
+                packed |= (f(i) as u8) << bit_idx;
+            }
+
+            // SAFETY: Already allocated sufficient capacity
+            unsafe { buffer.push_unchecked(packed) }
+        }
+
+        buffer
+    }
 }
 
 /// # Safety
@@ -496,38 +531,9 @@ impl MutableBuffer {
         mut iterator: I,
     ) -> Self {
         let (_, upper) = iterator.size_hint();
-        let upper = upper.expect("from_trusted_len_iter requires an upper 
limit");
-
-        let mut result = {
-            let byte_capacity: usize = upper.saturating_add(7) / 8;
-            MutableBuffer::new(byte_capacity)
-        };
+        let len = upper.expect("from_trusted_len_iter requires an upper 
limit");
 
-        'a: loop {
-            let mut byte_accum: u8 = 0;
-            let mut mask: u8 = 1;
-
-            //collect (up to) 8 bits into a byte
-            while mask != 0 {
-                if let Some(value) = iterator.next() {
-                    byte_accum |= match value {
-                        true => mask,
-                        false => 0,
-                    };
-                    mask <<= 1;
-                } else {
-                    if mask != 1 {
-                        // Add last byte
-                        result.push_unchecked(byte_accum);
-                    }
-                    break 'a;
-                }
-            }
-
-            // Soundness: from_trusted_len
-            result.push_unchecked(byte_accum);
-        }
-        result
+        Self::collect_bool(len, |_| iterator.next().unwrap())
     }
 
     /// Creates a [`MutableBuffer`] from an [`Iterator`] with a trusted 
(upper) length or errors
diff --git a/arrow/src/compute/kernels/comparison.rs 
b/arrow/src/compute/kernels/comparison.rs
index dd9d4fc5d..cba2d6e7d 100644
--- a/arrow/src/compute/kernels/comparison.rs
+++ b/arrow/src/compute/kernels/comparison.rs
@@ -59,12 +59,10 @@ where
     let null_bit_buffer =
         combine_option_bitmap(&[left.data_ref(), right.data_ref()], 
left.len())?;
 
-    // Safety:
-    // `i < $left.len()` and $left.len() == $right.len()
-    let comparison = (0..left.len())
-        .map(|i| unsafe { op(left.value_unchecked(i), 
right.value_unchecked(i)) });
-    // same size as $left.len() and $right.len()
-    let buffer = unsafe { 
MutableBuffer::from_trusted_len_iter_bool(comparison) };
+    let buffer = MutableBuffer::collect_bool(left.len(), |i| unsafe {
+        // SAFETY: i in range 0..len
+        op(left.value_unchecked(i), right.value_unchecked(i))
+    });
 
     let data = unsafe {
         ArrayData::new_unchecked(
@@ -91,11 +89,10 @@ where
         .null_buffer()
         .map(|b| b.bit_slice(left.offset(), left.len()));
 
-    // Safety:
-    // `i < $left.len()`
-    let comparison = (0..left.len()).map(|i| unsafe { 
op(left.value_unchecked(i)) });
-    // same as $left.len()
-    let buffer = unsafe { 
MutableBuffer::from_trusted_len_iter_bool(comparison) };
+    let buffer = MutableBuffer::collect_bool(left.len(), |i| unsafe {
+        // SAFETY: i in range 0..len
+        op(left.value_unchecked(i))
+    });
 
     let data = unsafe {
         ArrayData::new_unchecked(

Reply via email to