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

agrove pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-comet.git


The following commit(s) were added to refs/heads/main by this push:
     new a05c568e6 perf: use aligned pointer reads for SparkUnsafeRow field 
accessors (#3670)
a05c568e6 is described below

commit a05c568e6e7f20298ee8ec5a531aa644de288335
Author: Andy Grove <[email protected]>
AuthorDate: Thu Mar 12 07:51:42 2026 -0600

    perf: use aligned pointer reads for SparkUnsafeRow field accessors (#3670)
    
    * perf: use aligned pointer reads for SparkUnsafeRow field accessors
    
    SparkUnsafeRow field offsets are always 8-byte aligned (the JVM
    guarantees 8-byte alignment on the base address, bitset_width is a
    multiple of 8, and each field slot is 8 bytes). This means we can
    safely use ptr::read() instead of the from_le_bytes(slice) pattern
    for all typed accesses, avoiding slice creation and try_into overhead.
    
    Move primitive accessor implementations out of the SparkUnsafeObject
    trait defaults and into each concrete impl via a macro parameterized
    on the read method:
    - SparkUnsafeRow uses ptr::read() (aligned)
    - SparkUnsafeArray uses ptr::read_unaligned() (may be unaligned when
      nested in a row's variable-length region)
    
    Also switch is_null_at/set_not_null_at in SparkUnsafeRow from
    read_unaligned/write_unaligned to aligned read/write, since the null
    bitset is always at 8-byte aligned offsets within the row.
    
    * fix: use 8-byte aligned buffer in SparkUnsafeRow Miri test
    
    The test_append_null_struct_field_to_struct_builder test used a plain
    [u8; 16] stack buffer with no alignment guarantee. Since is_null_at
    performs aligned i64 reads, Miri flags this as undefined behavior when
    the buffer lands at a non-8-byte-aligned address.
    
    Wrap the buffer in a #[repr(align(8))] struct to match the alignment
    that real Spark UnsafeRow data always has from JVM memory.
---
 .../src/execution/shuffle/spark_unsafe/list.rs     |   9 +-
 .../core/src/execution/shuffle/spark_unsafe/row.rs | 226 +++++++++++----------
 2 files changed, 132 insertions(+), 103 deletions(-)

diff --git a/native/core/src/execution/shuffle/spark_unsafe/list.rs 
b/native/core/src/execution/shuffle/spark_unsafe/list.rs
index 72610d2d8..d9c93b1c6 100644
--- a/native/core/src/execution/shuffle/spark_unsafe/list.rs
+++ b/native/core/src/execution/shuffle/spark_unsafe/list.rs
@@ -19,7 +19,10 @@ use crate::{
     errors::CometError,
     execution::shuffle::spark_unsafe::{
         map::append_map_elements,
-        row::{append_field, downcast_builder_ref, SparkUnsafeObject, 
SparkUnsafeRow},
+        row::{
+            append_field, downcast_builder_ref, impl_primitive_accessors, 
SparkUnsafeObject,
+            SparkUnsafeRow,
+        },
     },
 };
 use arrow::array::{
@@ -101,6 +104,10 @@ impl SparkUnsafeObject for SparkUnsafeArray {
     fn get_element_offset(&self, index: usize, element_size: usize) -> *const 
u8 {
         (self.element_offset + (index * element_size) as i64) as *const u8
     }
+
+    // SparkUnsafeArray base address may be unaligned when nested within a 
row's variable-length
+    // region, so we must use ptr::read_unaligned() for all typed accesses.
+    impl_primitive_accessors!(read_unaligned);
 }
 
 impl SparkUnsafeArray {
diff --git a/native/core/src/execution/shuffle/spark_unsafe/row.rs 
b/native/core/src/execution/shuffle/spark_unsafe/row.rs
index 4d937db76..7ebf18d8d 100644
--- a/native/core/src/execution/shuffle/spark_unsafe/row.rs
+++ b/native/core/src/execution/shuffle/spark_unsafe/row.rs
@@ -71,6 +71,15 @@ const NESTED_TYPE_BUILDER_CAPACITY: usize = 100;
 /// safe to call as long as:
 /// - The index is within bounds (caller's responsibility)
 /// - The object was constructed from valid Spark UnsafeRow/UnsafeArray data
+///
+/// # Alignment
+///
+/// Primitive accessor methods are implemented separately for each type 
because they have
+/// different alignment guarantees:
+/// - `SparkUnsafeRow`: All field offsets are 8-byte aligned (bitset width is 
a multiple of 8,
+///   and each field slot is 8 bytes), so accessors use aligned `ptr::read()`.
+/// - `SparkUnsafeArray`: The array base address may be unaligned when nested 
within a row's
+///   variable-length region, so accessors use `ptr::read_unaligned()`.
 pub trait SparkUnsafeObject {
     /// Returns the address of the row.
     fn get_row_addr(&self) -> i64;
@@ -78,6 +87,16 @@ pub trait SparkUnsafeObject {
     /// Returns the offset of the element at the given index.
     fn get_element_offset(&self, index: usize, element_size: usize) -> *const 
u8;
 
+    fn get_boolean(&self, index: usize) -> bool;
+    fn get_byte(&self, index: usize) -> i8;
+    fn get_short(&self, index: usize) -> i16;
+    fn get_int(&self, index: usize) -> i32;
+    fn get_long(&self, index: usize) -> i64;
+    fn get_float(&self, index: usize) -> f32;
+    fn get_double(&self, index: usize) -> f64;
+    fn get_date(&self, index: usize) -> i32;
+    fn get_timestamp(&self, index: usize) -> i64;
+
     /// Returns the offset and length of the element at the given index.
     #[inline]
     fn get_offset_and_len(&self, index: usize) -> (i32, i32) {
@@ -87,79 +106,6 @@ pub trait SparkUnsafeObject {
         (offset, len)
     }
 
-    /// Returns boolean value at the given index of the object.
-    #[inline]
-    fn get_boolean(&self, index: usize) -> bool {
-        let addr = self.get_element_offset(index, 1);
-        // SAFETY: addr points to valid element data within the 
UnsafeRow/UnsafeArray region.
-        // The caller ensures index is within bounds.
-        debug_assert!(
-            !addr.is_null(),
-            "get_boolean: null pointer at index {index}"
-        );
-        unsafe { *addr != 0 }
-    }
-
-    /// Returns byte value at the given index of the object.
-    #[inline]
-    fn get_byte(&self, index: usize) -> i8 {
-        let addr = self.get_element_offset(index, 1);
-        // SAFETY: addr points to valid element data (1 byte) within the 
row/array region.
-        debug_assert!(!addr.is_null(), "get_byte: null pointer at index 
{index}");
-        let slice: &[u8] = unsafe { std::slice::from_raw_parts(addr, 1) };
-        i8::from_le_bytes(slice.try_into().unwrap())
-    }
-
-    /// Returns short value at the given index of the object.
-    #[inline]
-    fn get_short(&self, index: usize) -> i16 {
-        let addr = self.get_element_offset(index, 2);
-        // SAFETY: addr points to valid element data (2 bytes) within the 
row/array region.
-        debug_assert!(!addr.is_null(), "get_short: null pointer at index 
{index}");
-        let slice: &[u8] = unsafe { std::slice::from_raw_parts(addr, 2) };
-        i16::from_le_bytes(slice.try_into().unwrap())
-    }
-
-    /// Returns integer value at the given index of the object.
-    #[inline]
-    fn get_int(&self, index: usize) -> i32 {
-        let addr = self.get_element_offset(index, 4);
-        // SAFETY: addr points to valid element data (4 bytes) within the 
row/array region.
-        debug_assert!(!addr.is_null(), "get_int: null pointer at index 
{index}");
-        let slice: &[u8] = unsafe { std::slice::from_raw_parts(addr, 4) };
-        i32::from_le_bytes(slice.try_into().unwrap())
-    }
-
-    /// Returns long value at the given index of the object.
-    #[inline]
-    fn get_long(&self, index: usize) -> i64 {
-        let addr = self.get_element_offset(index, 8);
-        // SAFETY: addr points to valid element data (8 bytes) within the 
row/array region.
-        debug_assert!(!addr.is_null(), "get_long: null pointer at index 
{index}");
-        let slice: &[u8] = unsafe { std::slice::from_raw_parts(addr, 8) };
-        i64::from_le_bytes(slice.try_into().unwrap())
-    }
-
-    /// Returns float value at the given index of the object.
-    #[inline]
-    fn get_float(&self, index: usize) -> f32 {
-        let addr = self.get_element_offset(index, 4);
-        // SAFETY: addr points to valid element data (4 bytes) within the 
row/array region.
-        debug_assert!(!addr.is_null(), "get_float: null pointer at index 
{index}");
-        let slice: &[u8] = unsafe { std::slice::from_raw_parts(addr, 4) };
-        f32::from_le_bytes(slice.try_into().unwrap())
-    }
-
-    /// Returns double value at the given index of the object.
-    #[inline]
-    fn get_double(&self, index: usize) -> f64 {
-        let addr = self.get_element_offset(index, 8);
-        // SAFETY: addr points to valid element data (8 bytes) within the 
row/array region.
-        debug_assert!(!addr.is_null(), "get_double: null pointer at index 
{index}");
-        let slice: &[u8] = unsafe { std::slice::from_raw_parts(addr, 8) };
-        f64::from_le_bytes(slice.try_into().unwrap())
-    }
-
     /// Returns string value at the given index of the object.
     fn get_string(&self, index: usize) -> &str {
         let (offset, len) = self.get_offset_and_len(index);
@@ -190,29 +136,6 @@ pub trait SparkUnsafeObject {
         unsafe { std::slice::from_raw_parts(addr as *const u8, len as usize) }
     }
 
-    /// Returns date value at the given index of the object.
-    #[inline]
-    fn get_date(&self, index: usize) -> i32 {
-        let addr = self.get_element_offset(index, 4);
-        // SAFETY: addr points to valid element data (4 bytes) within the 
row/array region.
-        debug_assert!(!addr.is_null(), "get_date: null pointer at index 
{index}");
-        let slice: &[u8] = unsafe { std::slice::from_raw_parts(addr, 4) };
-        i32::from_le_bytes(slice.try_into().unwrap())
-    }
-
-    /// Returns timestamp value at the given index of the object.
-    #[inline]
-    fn get_timestamp(&self, index: usize) -> i64 {
-        let addr = self.get_element_offset(index, 8);
-        // SAFETY: addr points to valid element data (8 bytes) within the 
row/array region.
-        debug_assert!(
-            !addr.is_null(),
-            "get_timestamp: null pointer at index {index}"
-        );
-        let slice: &[u8] = unsafe { std::slice::from_raw_parts(addr, 8) };
-        i64::from_le_bytes(slice.try_into().unwrap())
-    }
-
     /// Returns decimal value at the given index of the object.
     fn get_decimal(&self, index: usize, precision: u8) -> i128 {
         if precision <= MAX_LONG_DIGITS {
@@ -244,6 +167,94 @@ pub trait SparkUnsafeObject {
     }
 }
 
+/// Generates primitive accessor implementations for `SparkUnsafeObject`.
+///
+/// Uses `$read_method` to read typed values from raw pointers:
+/// - `read` for aligned access (SparkUnsafeRow — all offsets are 8-byte 
aligned)
+/// - `read_unaligned` for potentially unaligned access (SparkUnsafeArray)
+macro_rules! impl_primitive_accessors {
+    ($read_method:ident) => {
+        #[inline]
+        fn get_boolean(&self, index: usize) -> bool {
+            let addr = self.get_element_offset(index, 1);
+            debug_assert!(
+                !addr.is_null(),
+                "get_boolean: null pointer at index {index}"
+            );
+            // SAFETY: addr points to valid element data within the row/array 
region.
+            unsafe { *addr != 0 }
+        }
+
+        #[inline]
+        fn get_byte(&self, index: usize) -> i8 {
+            let addr = self.get_element_offset(index, 1);
+            debug_assert!(!addr.is_null(), "get_byte: null pointer at index 
{index}");
+            // SAFETY: addr points to valid element data (1 byte) within the 
row/array region.
+            unsafe { *(addr as *const i8) }
+        }
+
+        #[inline]
+        fn get_short(&self, index: usize) -> i16 {
+            let addr = self.get_element_offset(index, 2) as *const i16;
+            debug_assert!(!addr.is_null(), "get_short: null pointer at index 
{index}");
+            // SAFETY: addr points to valid element data (2 bytes) within the 
row/array region.
+            unsafe { addr.$read_method() }
+        }
+
+        #[inline]
+        fn get_int(&self, index: usize) -> i32 {
+            let addr = self.get_element_offset(index, 4) as *const i32;
+            debug_assert!(!addr.is_null(), "get_int: null pointer at index 
{index}");
+            // SAFETY: addr points to valid element data (4 bytes) within the 
row/array region.
+            unsafe { addr.$read_method() }
+        }
+
+        #[inline]
+        fn get_long(&self, index: usize) -> i64 {
+            let addr = self.get_element_offset(index, 8) as *const i64;
+            debug_assert!(!addr.is_null(), "get_long: null pointer at index 
{index}");
+            // SAFETY: addr points to valid element data (8 bytes) within the 
row/array region.
+            unsafe { addr.$read_method() }
+        }
+
+        #[inline]
+        fn get_float(&self, index: usize) -> f32 {
+            let addr = self.get_element_offset(index, 4) as *const f32;
+            debug_assert!(!addr.is_null(), "get_float: null pointer at index 
{index}");
+            // SAFETY: addr points to valid element data (4 bytes) within the 
row/array region.
+            unsafe { addr.$read_method() }
+        }
+
+        #[inline]
+        fn get_double(&self, index: usize) -> f64 {
+            let addr = self.get_element_offset(index, 8) as *const f64;
+            debug_assert!(!addr.is_null(), "get_double: null pointer at index 
{index}");
+            // SAFETY: addr points to valid element data (8 bytes) within the 
row/array region.
+            unsafe { addr.$read_method() }
+        }
+
+        #[inline]
+        fn get_date(&self, index: usize) -> i32 {
+            let addr = self.get_element_offset(index, 4) as *const i32;
+            debug_assert!(!addr.is_null(), "get_date: null pointer at index 
{index}");
+            // SAFETY: addr points to valid element data (4 bytes) within the 
row/array region.
+            unsafe { addr.$read_method() }
+        }
+
+        #[inline]
+        fn get_timestamp(&self, index: usize) -> i64 {
+            let addr = self.get_element_offset(index, 8) as *const i64;
+            debug_assert!(
+                !addr.is_null(),
+                "get_timestamp: null pointer at index {index}"
+            );
+            // SAFETY: addr points to valid element data (8 bytes) within the 
row/array region.
+            unsafe { addr.$read_method() }
+        }
+    };
+}
+pub(crate) use impl_primitive_accessors;
+
 pub struct SparkUnsafeRow {
     row_addr: i64,
     row_size: i32,
@@ -265,6 +276,11 @@ impl SparkUnsafeObject for SparkUnsafeRow {
         );
         (self.row_addr + offset) as *const u8
     }
+
+    // SparkUnsafeRow field offsets are always 8-byte aligned: the base 
address is 8-byte
+    // aligned (JVM guarantee), bitset_width is a multiple of 8, and each 
field slot is
+    // 8 bytes. This means we can safely use aligned ptr::read() for all typed 
accesses.
+    impl_primitive_accessors!(read);
 }
 
 impl Default for SparkUnsafeRow {
@@ -328,11 +344,13 @@ impl SparkUnsafeRow {
         // SAFETY: row_addr points to valid Spark UnsafeRow data with at least
         // ceil(num_fields/64) * 8 bytes of null bitset. The caller ensures 
index < num_fields.
         // word_offset is within the bitset region since (index >> 6) << 3 < 
bitset size.
+        // The bitset starts at row_addr (8-byte aligned) and each word is at 
offset 8*k,
+        // so word_offset is always 8-byte aligned — we can use aligned 
ptr::read().
         debug_assert!(self.row_addr != -1, "is_null_at: row not initialized");
         unsafe {
             let mask: i64 = 1i64 << (index & 0x3f);
             let word_offset = (self.row_addr + (((index >> 6) as i64) << 3)) 
as *const i64;
-            let word: i64 = word_offset.read_unaligned();
+            let word: i64 = word_offset.read();
             (word & mask) != 0
         }
     }
@@ -343,12 +361,13 @@ impl SparkUnsafeRow {
         // ceil(num_fields/64) * 8 bytes of null bitset. The caller ensures 
index < num_fields.
         // word_offset is within the bitset region since (index >> 6) << 3 < 
bitset size.
         // Writing is safe because we have mutable access and the memory is 
owned by the JVM.
+        // The bitset is always 8-byte aligned — we can use aligned 
ptr::read()/write().
         debug_assert!(self.row_addr != -1, "set_not_null_at: row not 
initialized");
         unsafe {
             let mask: i64 = 1i64 << (index & 0x3f);
             let word_offset = (self.row_addr + (((index >> 6) as i64) << 3)) 
as *mut i64;
-            let word: i64 = word_offset.read_unaligned();
-            word_offset.write_unaligned(word & !mask);
+            let word: i64 = word_offset.read();
+            word_offset.write(word & !mask);
         }
     }
 }
@@ -1668,9 +1687,12 @@ mod test {
         let mut row = SparkUnsafeRow::new_with_num_fields(1);
         // 8 bytes null bitset + 8 bytes field value = 16 bytes
         // Set bit 0 in the null bitset to mark field 0 as null
-        let mut data = [0u8; 16];
-        data[0] = 1;
-        row.point_to_slice(&data);
+        // Use aligned buffer to match real Spark UnsafeRow layout (8-byte 
aligned)
+        #[repr(align(8))]
+        struct Aligned([u8; 16]);
+        let mut data = Aligned([0u8; 16]);
+        data.0[0] = 1;
+        row.point_to_slice(&data.0);
         append_field(&data_type, &mut struct_builder, &row, 0).expect("append 
field");
         struct_builder.append_null();
         let struct_array = struct_builder.finish();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to