Skip to content

Commit d0013ff

Browse files
authored
Add binary operator benchmarks (vortex-data#8396)
Adds some benchmarks for binary operators as I have some plans to improve Signed-off-by: Nicholas Gates <nick@nickgates.com>
1 parent 5580295 commit d0013ff

2 files changed

Lines changed: 158 additions & 0 deletions

File tree

vortex-array/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ harness = false
125125
name = "compare"
126126
harness = false
127127

128+
[[bench]]
129+
name = "binary_ops"
130+
harness = false
131+
128132
[[bench]]
129133
name = "interleave"
130134
harness = false

vortex-array/benches/binary_ops.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
#![expect(clippy::unwrap_used)]
5+
#![expect(
6+
clippy::cast_possible_truncation,
7+
reason = "benchmark fixtures use indices that fit in the chosen widths"
8+
)]
9+
10+
use std::sync::LazyLock;
11+
12+
use divan::Bencher;
13+
use divan::counter::ItemsCount;
14+
use vortex_array::ArrayRef;
15+
use vortex_array::Executable;
16+
use vortex_array::IntoArray;
17+
use vortex_array::VortexSessionExecute;
18+
use vortex_array::arrays::BoolArray;
19+
use vortex_array::arrays::ConstantArray;
20+
use vortex_array::arrays::PrimitiveArray;
21+
use vortex_array::builtins::ArrayBuiltins;
22+
use vortex_array::scalar_fn::fns::operators::Operator;
23+
use vortex_array::session::ArraySession;
24+
use vortex_session::VortexSession;
25+
26+
fn main() {
27+
divan::main();
28+
}
29+
30+
static SESSION: LazyLock<VortexSession> =
31+
LazyLock::new(|| VortexSession::empty().with::<ArraySession>());
32+
33+
const LEN: usize = 65_536;
34+
35+
#[divan::bench]
36+
fn add_i64_nonnull(bencher: Bencher) {
37+
let lhs = primitive_nonnull(0).into_array();
38+
let rhs = primitive_nonnull(1_000_000).into_array();
39+
40+
bench_primitive(bencher, lhs, rhs, Operator::Add);
41+
}
42+
43+
#[divan::bench]
44+
fn add_i64_nullable(bencher: Bencher) {
45+
let lhs = primitive_nullable(0, 7).into_array();
46+
let rhs = primitive_nullable(1_000_000, 5).into_array();
47+
48+
bench_primitive(bencher, lhs, rhs, Operator::Add);
49+
}
50+
51+
#[divan::bench]
52+
fn mul_i64_nonnull(bencher: Bencher) {
53+
let lhs = primitive_small_nonnull(1).into_array();
54+
let rhs = primitive_small_nonnull(17).into_array();
55+
56+
bench_primitive(bencher, lhs, rhs, Operator::Mul);
57+
}
58+
59+
#[divan::bench]
60+
fn div_i64_nonnull(bencher: Bencher) {
61+
let lhs = primitive_nonnull(1_000_000).into_array();
62+
let rhs = primitive_nonzero().into_array();
63+
64+
bench_primitive(bencher, lhs, rhs, Operator::Div);
65+
}
66+
67+
#[divan::bench]
68+
fn sub_i64_constant(bencher: Bencher) {
69+
let lhs = primitive_nonnull(0).into_array();
70+
let rhs = ConstantArray::new(37i64, LEN).into_array();
71+
72+
bench_primitive(bencher, lhs, rhs, Operator::Sub);
73+
}
74+
75+
#[divan::bench]
76+
fn eq_i64_constant(bencher: Bencher) {
77+
let lhs = primitive_nonnull(0).into_array();
78+
let rhs = ConstantArray::new(1024i64, LEN).into_array();
79+
80+
bench_bool(bencher, lhs, rhs, Operator::Eq);
81+
}
82+
83+
#[divan::bench]
84+
fn lt_i64_nullable(bencher: Bencher) {
85+
let lhs = primitive_nullable(0, 7).into_array();
86+
let rhs = primitive_nullable(1_000_000, 5).into_array();
87+
88+
bench_bool(bencher, lhs, rhs, Operator::Lt);
89+
}
90+
91+
#[divan::bench]
92+
fn and_bool_nullable(bencher: Bencher) {
93+
let lhs = bool_nullable(2, 7).into_array();
94+
let rhs = bool_nullable(3, 5).into_array();
95+
96+
bench_bool(bencher, lhs, rhs, Operator::And);
97+
}
98+
99+
#[divan::bench]
100+
fn or_bool_constant(bencher: Bencher) {
101+
let lhs = bool_nullable(2, 7).into_array();
102+
let rhs = ConstantArray::new(true, LEN).into_array();
103+
104+
bench_bool(bencher, lhs, rhs, Operator::Or);
105+
}
106+
107+
fn bench_primitive(bencher: Bencher, lhs: ArrayRef, rhs: ArrayRef, operator: Operator) {
108+
bench_binary::<PrimitiveArray>(bencher, lhs, rhs, operator);
109+
}
110+
111+
fn bench_bool(bencher: Bencher, lhs: ArrayRef, rhs: ArrayRef, operator: Operator) {
112+
bench_binary::<BoolArray>(bencher, lhs, rhs, operator);
113+
}
114+
115+
fn bench_binary<T: Executable + 'static>(
116+
bencher: Bencher,
117+
lhs: ArrayRef,
118+
rhs: ArrayRef,
119+
operator: Operator,
120+
) {
121+
let mut ctx = SESSION.create_execution_ctx();
122+
123+
bencher.counter(ItemsCount::new(LEN)).bench_local(|| {
124+
lhs.clone()
125+
.binary(rhs.clone(), operator)
126+
.unwrap()
127+
.execute::<T>(&mut ctx)
128+
.unwrap()
129+
});
130+
}
131+
132+
fn primitive_nonnull(base: i64) -> PrimitiveArray {
133+
PrimitiveArray::from_iter((0..LEN as i64).map(|i| base + i))
134+
}
135+
136+
fn primitive_small_nonnull(offset: i64) -> PrimitiveArray {
137+
PrimitiveArray::from_iter((0..LEN as i64).map(|i| ((i + offset) % 1024) + 1))
138+
}
139+
140+
fn primitive_nonzero() -> PrimitiveArray {
141+
PrimitiveArray::from_iter((0..LEN as i64).map(|i| (i % 255) + 1))
142+
}
143+
144+
fn primitive_nullable(base: i64, null_every: usize) -> PrimitiveArray {
145+
PrimitiveArray::from_option_iter(
146+
(0..LEN as i64).map(|i| (!(i as usize).is_multiple_of(null_every)).then_some(base + i)),
147+
)
148+
}
149+
150+
fn bool_nullable(true_every: usize, null_every: usize) -> BoolArray {
151+
BoolArray::from_iter(
152+
(0..LEN).map(|i| (!i.is_multiple_of(null_every)).then_some(i.is_multiple_of(true_every))),
153+
)
154+
}

0 commit comments

Comments
 (0)