From e5a7072c3895dad2c9327f3397cdc34c3340b84c Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Fri, 12 Jun 2026 15:58:41 -0400 Subject: [PATCH] Add binary operator benchmarks Signed-off-by: Nicholas Gates --- vortex-array/Cargo.toml | 4 + vortex-array/benches/binary_ops.rs | 154 +++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 vortex-array/benches/binary_ops.rs diff --git a/vortex-array/Cargo.toml b/vortex-array/Cargo.toml index 4ec8a83575e..de3bb416985 100644 --- a/vortex-array/Cargo.toml +++ b/vortex-array/Cargo.toml @@ -125,6 +125,10 @@ harness = false name = "compare" harness = false +[[bench]] +name = "binary_ops" +harness = false + [[bench]] name = "interleave" harness = false diff --git a/vortex-array/benches/binary_ops.rs b/vortex-array/benches/binary_ops.rs new file mode 100644 index 00000000000..a5e3f9a5594 --- /dev/null +++ b/vortex-array/benches/binary_ops.rs @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +#![expect(clippy::unwrap_used)] +#![expect( + clippy::cast_possible_truncation, + reason = "benchmark fixtures use indices that fit in the chosen widths" +)] + +use std::sync::LazyLock; + +use divan::Bencher; +use divan::counter::ItemsCount; +use vortex_array::ArrayRef; +use vortex_array::Executable; +use vortex_array::IntoArray; +use vortex_array::VortexSessionExecute; +use vortex_array::arrays::BoolArray; +use vortex_array::arrays::ConstantArray; +use vortex_array::arrays::PrimitiveArray; +use vortex_array::builtins::ArrayBuiltins; +use vortex_array::scalar_fn::fns::operators::Operator; +use vortex_array::session::ArraySession; +use vortex_session::VortexSession; + +fn main() { + divan::main(); +} + +static SESSION: LazyLock = + LazyLock::new(|| VortexSession::empty().with::()); + +const LEN: usize = 65_536; + +#[divan::bench] +fn add_i64_nonnull(bencher: Bencher) { + let lhs = primitive_nonnull(0).into_array(); + let rhs = primitive_nonnull(1_000_000).into_array(); + + bench_primitive(bencher, lhs, rhs, Operator::Add); +} + +#[divan::bench] +fn add_i64_nullable(bencher: Bencher) { + let lhs = primitive_nullable(0, 7).into_array(); + let rhs = primitive_nullable(1_000_000, 5).into_array(); + + bench_primitive(bencher, lhs, rhs, Operator::Add); +} + +#[divan::bench] +fn mul_i64_nonnull(bencher: Bencher) { + let lhs = primitive_small_nonnull(1).into_array(); + let rhs = primitive_small_nonnull(17).into_array(); + + bench_primitive(bencher, lhs, rhs, Operator::Mul); +} + +#[divan::bench] +fn div_i64_nonnull(bencher: Bencher) { + let lhs = primitive_nonnull(1_000_000).into_array(); + let rhs = primitive_nonzero().into_array(); + + bench_primitive(bencher, lhs, rhs, Operator::Div); +} + +#[divan::bench] +fn sub_i64_constant(bencher: Bencher) { + let lhs = primitive_nonnull(0).into_array(); + let rhs = ConstantArray::new(37i64, LEN).into_array(); + + bench_primitive(bencher, lhs, rhs, Operator::Sub); +} + +#[divan::bench] +fn eq_i64_constant(bencher: Bencher) { + let lhs = primitive_nonnull(0).into_array(); + let rhs = ConstantArray::new(1024i64, LEN).into_array(); + + bench_bool(bencher, lhs, rhs, Operator::Eq); +} + +#[divan::bench] +fn lt_i64_nullable(bencher: Bencher) { + let lhs = primitive_nullable(0, 7).into_array(); + let rhs = primitive_nullable(1_000_000, 5).into_array(); + + bench_bool(bencher, lhs, rhs, Operator::Lt); +} + +#[divan::bench] +fn and_bool_nullable(bencher: Bencher) { + let lhs = bool_nullable(2, 7).into_array(); + let rhs = bool_nullable(3, 5).into_array(); + + bench_bool(bencher, lhs, rhs, Operator::And); +} + +#[divan::bench] +fn or_bool_constant(bencher: Bencher) { + let lhs = bool_nullable(2, 7).into_array(); + let rhs = ConstantArray::new(true, LEN).into_array(); + + bench_bool(bencher, lhs, rhs, Operator::Or); +} + +fn bench_primitive(bencher: Bencher, lhs: ArrayRef, rhs: ArrayRef, operator: Operator) { + bench_binary::(bencher, lhs, rhs, operator); +} + +fn bench_bool(bencher: Bencher, lhs: ArrayRef, rhs: ArrayRef, operator: Operator) { + bench_binary::(bencher, lhs, rhs, operator); +} + +fn bench_binary( + bencher: Bencher, + lhs: ArrayRef, + rhs: ArrayRef, + operator: Operator, +) { + let mut ctx = SESSION.create_execution_ctx(); + + bencher.counter(ItemsCount::new(LEN)).bench_local(|| { + lhs.clone() + .binary(rhs.clone(), operator) + .unwrap() + .execute::(&mut ctx) + .unwrap() + }); +} + +fn primitive_nonnull(base: i64) -> PrimitiveArray { + PrimitiveArray::from_iter((0..LEN as i64).map(|i| base + i)) +} + +fn primitive_small_nonnull(offset: i64) -> PrimitiveArray { + PrimitiveArray::from_iter((0..LEN as i64).map(|i| ((i + offset) % 1024) + 1)) +} + +fn primitive_nonzero() -> PrimitiveArray { + PrimitiveArray::from_iter((0..LEN as i64).map(|i| (i % 255) + 1)) +} + +fn primitive_nullable(base: i64, null_every: usize) -> PrimitiveArray { + PrimitiveArray::from_option_iter( + (0..LEN as i64).map(|i| (!(i as usize).is_multiple_of(null_every)).then_some(base + i)), + ) +} + +fn bool_nullable(true_every: usize, null_every: usize) -> BoolArray { + BoolArray::from_iter( + (0..LEN).map(|i| (!i.is_multiple_of(null_every)).then_some(i.is_multiple_of(true_every))), + ) +}