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))
}