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

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


The following commit(s) were added to refs/heads/main by this push:
     new 46540d99b4 Use Vec for primitive array unary operations (#10783)
46540d99b4 is described below

commit 46540d99b4ca7a4d23dc60900f35ae217bb76089
Author: Ben Kowanietz <[email protected]>
AuthorDate: Mon Aug 24 11:55:17 2026 +0700

    Use Vec for primitive array unary operations (#10783)
    
    # Which issue does this PR close?
    
    - part of #10245
    
    # Rationale for this change
    
    Replacing `BufferBuilder` with `Vec` improves the performenace of
    primitive array unary operations.
    
    # What changes are included in this PR?
    
    Replaces the `BufferBuilder<...>` usages in `PrimitiveArray::try_unary`
    and `PrimitiveArray::unary_opt` with `Vecs`.
    
    Also adds benchmarks for both operations with and without input nulls
    
    # Are these changes tested?
    
    All tests pass:
    
    - `cargo fmt --all -- --check`
    - `cargo clippy -p arrow-array --all-targets --all-features --no-deps --
    -D warnings`
    - `cargo test -p arrow-array --all-features`
    - `cargo bench -p arrow-array --bench primitive_array -- --test`
    
    Local benchmark results for 65,536 `Int32` values:
    
    | Benchmark | Before | After | Change |
    |---|---:|---:|---:|
    | `try_unary`, no input nulls | 10.600 µs | 8.865 µs | 16.4% faster |
    | `try_unary`, 20% input nulls | 54.486 µs | 54.623 µs | no clear change
    |
    | `unary_opt`, no input nulls | 48.641 µs | 49.630 µs | no clear change
    |
    | `unary_opt`, 20% input nulls | 75.155 µs | 75.903 µs | no clear change
    |
    
    I currently only have a MacBook available so benching this on Linux
    would probably make sense.
    
    # Are there any user-facing changes?
    
    No.
---
 arrow-array/Cargo.toml                   |  4 +++
 arrow-array/benches/primitive_array.rs   | 62 ++++++++++++++++++++++++++++++++
 arrow-array/src/array/primitive_array.rs | 16 ++++-----
 3 files changed, 73 insertions(+), 9 deletions(-)

diff --git a/arrow-array/Cargo.toml b/arrow-array/Cargo.toml
index 126996ee64..f5d26b5356 100644
--- a/arrow-array/Cargo.toml
+++ b/arrow-array/Cargo.toml
@@ -109,5 +109,9 @@ harness = false
 name = "boolean_array"
 harness = false
 
+[[bench]]
+name = "primitive_array"
+harness = false
+
 [lints]
 workspace = true
diff --git a/arrow-array/benches/primitive_array.rs 
b/arrow-array/benches/primitive_array.rs
new file mode 100644
index 0000000000..a0568b2049
--- /dev/null
+++ b/arrow-array/benches/primitive_array.rs
@@ -0,0 +1,62 @@
+// 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 std::hint;
+
+use arrow_array::Int32Array;
+use arrow_array::types::Int32Type;
+use criterion::{Criterion, criterion_group, criterion_main};
+
+const BATCH_SIZE: usize = 64 * 1024;
+
+fn primitive_array_unary(c: &mut Criterion) {
+    let arrays = [
+        (
+            "no_input_nulls",
+            Int32Array::from_iter_values(0..BATCH_SIZE as i32),
+        ),
+        (
+            "20pct_input_nulls",
+            Int32Array::from_iter(
+                (0..BATCH_SIZE as i32).map(|value| (value % 5 != 
0).then_some(value)),
+            ),
+        ),
+    ];
+
+    let mut group = c.benchmark_group("primitive_array_unary");
+    for (name, array) in arrays {
+        group.bench_function(format!("try_unary/{name}"), |b| {
+            b.iter(|| {
+                hint::black_box(
+                    array
+                        .try_unary::<_, Int32Type, ()>(|value| Ok(value + 1))
+                        .unwrap(),
+                )
+            })
+        });
+        group.bench_function(format!("unary_opt/{name}"), |b| {
+            b.iter(|| {
+                hint::black_box(
+                    array.unary_opt::<_, Int32Type>(|value| (value % 7 != 
0).then_some(value + 1)),
+                )
+            })
+        });
+    }
+}
+
+criterion_group!(benches, primitive_array_unary);
+criterion_main!(benches);
diff --git a/arrow-array/src/array/primitive_array.rs 
b/arrow-array/src/array/primitive_array.rs
index 68b404e32b..dc17cdfffd 100644
--- a/arrow-array/src/array/primitive_array.rs
+++ b/arrow-array/src/array/primitive_array.rs
@@ -16,7 +16,7 @@
 // under the License.
 
 use crate::array::print_long_array;
-use crate::builder::{BooleanBufferBuilder, BufferBuilder, PrimitiveBuilder};
+use crate::builder::{BooleanBufferBuilder, PrimitiveBuilder};
 use crate::iterator::PrimitiveIter;
 use crate::temporal_conversions::{
     as_date, as_datetime, as_datetime_with_timezone, as_duration, as_time,
@@ -995,9 +995,8 @@ impl<T: ArrowPrimitiveType> PrimitiveArray<T> {
         let len = self.len();
 
         let nulls = self.nulls().cloned();
-        let mut buffer = BufferBuilder::<O::Native>::new(len);
-        buffer.append_n_zeroed(len);
-        let slice = buffer.as_slice_mut();
+        let mut values = vec![O::Native::default(); len];
+        let slice = values.as_mut_slice();
 
         let f = |idx| {
             unsafe { *slice.get_unchecked_mut(idx) = 
op(self.value_unchecked(idx))? };
@@ -1009,7 +1008,7 @@ impl<T: ArrowPrimitiveType> PrimitiveArray<T> {
             None => (0..len).try_for_each(f)?,
         }
 
-        let values = buffer.finish().into();
+        let values = values.into();
         Ok(PrimitiveArray::new(values, nulls))
     }
 
@@ -1079,9 +1078,8 @@ impl<T: ArrowPrimitiveType> PrimitiveArray<T> {
             None => null_builder.append_n(len, true),
         }
 
-        let mut buffer = BufferBuilder::<O::Native>::new(len);
-        buffer.append_n_zeroed(len);
-        let slice = buffer.as_slice_mut();
+        let mut values = vec![O::Native::default(); len];
+        let slice = values.as_mut_slice();
 
         let mut out_null_count = null_count;
 
@@ -1097,7 +1095,7 @@ impl<T: ArrowPrimitiveType> PrimitiveArray<T> {
         });
 
         let nulls = null_builder.finish();
-        let values = buffer.finish().into();
+        let values = values.into();
         let nulls = unsafe { NullBuffer::new_unchecked(nulls, out_null_count) 
};
         PrimitiveArray::new(values, Some(nulls))
     }

Reply via email to