Skip to content

Commit 77fab70

Browse files
committed
Minimize alignment noise in bitbuffer benchmarks
Signed-off-by: Adam Gutglick <adam@spiraldb.com>
1 parent 4f52cbc commit 77fab70

1 file changed

Lines changed: 53 additions & 17 deletions

File tree

vortex-buffer/benches/vortex_bitbuffer.rs

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ use std::iter::Iterator;
66
use arrow_buffer::BooleanBuffer;
77
use arrow_buffer::BooleanBufferBuilder;
88
use divan::Bencher;
9+
use vortex_buffer::Alignment;
910
use vortex_buffer::BitBuffer;
1011
use vortex_buffer::BitBufferMut;
12+
use vortex_buffer::BufferMut;
13+
use vortex_buffer::collect_bool_words;
1114

1215
fn main() {
1316
// Pre-warm CPUID feature detection so the one-time probe cost is never
@@ -32,12 +35,45 @@ impl FromIterator<bool> for Arrow<BooleanBuffer> {
3235
}
3336

3437
const INPUT_SIZE: &[usize] = &[128, 1024, 2048, 16_384, 65_536];
38+
// CodSpeed's Divan adapter measures one generated input per benchmark URI, so keep
39+
// allocator alignment out of run-to-run comparisons.
40+
const BENCH_ALIGNMENT: Alignment = Alignment::new(128);
3541

3642
#[inline]
3743
fn true_count_pattern(i: usize) -> bool {
3844
(i.is_multiple_of(3)) ^ (i.is_multiple_of(11))
3945
}
4046

47+
fn bit_buffer_with_capacity(capacity: usize) -> BitBufferMut {
48+
BitBufferMut::from_buffer(
49+
BufferMut::with_capacity_aligned(capacity.div_ceil(8), BENCH_ALIGNMENT),
50+
0,
51+
0,
52+
)
53+
}
54+
55+
fn bit_buffer_mut_from_fn<F>(len: usize, f: F) -> BitBufferMut
56+
where
57+
F: FnMut(usize) -> bool,
58+
{
59+
let num_words = len.div_ceil(64);
60+
let mut buffer = BufferMut::<u64>::with_capacity_aligned(num_words, BENCH_ALIGNMENT);
61+
// SAFETY: `collect_bool_words` writes every word before any read.
62+
unsafe { buffer.set_len(num_words) };
63+
collect_bool_words(buffer.as_mut_slice(), len, f);
64+
65+
let mut bytes = buffer.into_byte_buffer();
66+
bytes.truncate(len.div_ceil(8));
67+
BitBufferMut::from_buffer(bytes, 0, len)
68+
}
69+
70+
fn bit_buffer_from_fn<F>(len: usize, f: F) -> BitBuffer
71+
where
72+
F: FnMut(usize) -> bool,
73+
{
74+
bit_buffer_mut_from_fn(len, f).freeze()
75+
}
76+
4177
#[cfg(not(codspeed))]
4278
#[divan::bench(args = INPUT_SIZE)]
4379
fn from_iter_arrow(n: usize) {
@@ -52,7 +88,7 @@ fn from_iter_bit_buffer(n: usize) {
5288
#[divan::bench(args = INPUT_SIZE)]
5389
fn append_vortex_buffer(bencher: Bencher, length: usize) {
5490
bencher
55-
.with_inputs(|| (BitBufferMut::with_capacity(length), length))
91+
.with_inputs(|| (bit_buffer_with_capacity(length), length))
5692
.bench_refs(|(buffer, length)| {
5793
for idx in 0..*length {
5894
buffer.append(idx % 2 == 0);
@@ -74,7 +110,7 @@ fn append_arrow_buffer(bencher: Bencher, length: usize) {
74110
#[divan::bench(args = INPUT_SIZE)]
75111
fn append_n_vortex_buffer(bencher: Bencher, length: usize) {
76112
bencher
77-
.with_inputs(|| (BitBufferMut::with_capacity(length), length, true))
113+
.with_inputs(|| (bit_buffer_with_capacity(length), length, true))
78114
.bench_refs(|(buffer, length, boolean)| {
79115
for _ in 0..100 {
80116
buffer.append_n(*boolean, *length / 100);
@@ -97,8 +133,8 @@ fn append_n_arrow_buffer(bencher: Bencher, length: usize) {
97133
fn append_buffer_vortex_buffer(bencher: Bencher, length: usize) {
98134
bencher
99135
.with_inputs(|| {
100-
let source = BitBuffer::from_iter((0..length / 100).map(|i| i % 2 == 0));
101-
let dest = BitBufferMut::with_capacity(length);
136+
let source = bit_buffer_from_fn(length / 100, |i| i % 2 == 0);
137+
let dest = bit_buffer_with_capacity(length);
102138
(source, dest)
103139
})
104140
.bench_refs(|(source, dest)| {
@@ -129,7 +165,7 @@ fn append_buffer_arrow_buffer(bencher: Bencher, length: usize) {
129165

130166
#[divan::bench(args = INPUT_SIZE)]
131167
fn value_vortex_buffer(bencher: Bencher, length: usize) {
132-
let buffer = BitBuffer::from_iter((0..length).map(|i| i % 2 == 0));
168+
let buffer = bit_buffer_from_fn(length, |i| i % 2 == 0);
133169
bencher.with_inputs(|| &buffer).bench_refs(|buffer| {
134170
for idx in 0..length {
135171
divan::black_box(buffer.value(idx));
@@ -149,7 +185,7 @@ fn value_arrow_buffer(bencher: Bencher, length: usize) {
149185

150186
#[divan::bench(args = INPUT_SIZE)]
151187
fn slice_vortex_buffer(bencher: Bencher, length: usize) {
152-
let buffer = BitBuffer::from_iter((0..length).map(|i| i % 2 == 0));
188+
let buffer = bit_buffer_from_fn(length, |i| i % 2 == 0);
153189
bencher
154190
.with_inputs(|| (&buffer, length / 2))
155191
.bench_refs(|(buffer, mid)| {
@@ -172,7 +208,7 @@ fn slice_arrow_buffer(bencher: Bencher, length: usize) {
172208

173209
#[divan::bench(args = INPUT_SIZE)]
174210
fn true_count_vortex_buffer(bencher: Bencher, length: usize) {
175-
let buffer = BitBuffer::from_iter((0..length).map(true_count_pattern));
211+
let buffer = bit_buffer_from_fn(length, true_count_pattern);
176212

177213
bencher
178214
.with_inputs(|| &buffer)
@@ -194,8 +230,8 @@ fn true_count_arrow_buffer(bencher: Bencher, length: usize) {
194230

195231
#[divan::bench(args = INPUT_SIZE)]
196232
fn bitwise_and_vortex_buffer(bencher: Bencher, length: usize) {
197-
let a = BitBuffer::from_iter((0..length).map(|i| i % 2 == 0));
198-
let b = BitBuffer::from_iter((0..length).map(|i| i % 3 == 0));
233+
let a = bit_buffer_from_fn(length, |i| i % 2 == 0);
234+
let b = bit_buffer_from_fn(length, |i| i % 3 == 0);
199235
bencher
200236
.with_inputs(|| (&a, &b))
201237
.bench_values(|(a, b)| a & b);
@@ -215,16 +251,16 @@ fn bitwise_and_arrow_buffer(bencher: Bencher, length: usize) {
215251
/// `bitwise_and_vortex_buffer` (reference-LHS, which always allocates a result buffer).
216252
#[divan::bench(args = INPUT_SIZE)]
217253
fn bitand_owned_lhs_vortex_buffer(bencher: Bencher, length: usize) {
218-
let b = BitBuffer::from_iter((0..length).map(|i| i % 3 == 0));
254+
let b = bit_buffer_from_fn(length, |i| i % 3 == 0);
219255
bencher
220-
.with_inputs(|| BitBuffer::from_iter((0..length).map(|i| i % 2 == 0)))
256+
.with_inputs(|| bit_buffer_from_fn(length, |i| i % 2 == 0))
221257
.bench_values(|a| a & &b);
222258
}
223259

224260
#[divan::bench(args = INPUT_SIZE)]
225261
fn bitwise_or_vortex_buffer(bencher: Bencher, length: usize) {
226-
let a = BitBuffer::from_iter((0..length).map(|i| i % 2 == 0));
227-
let b = BitBuffer::from_iter((0..length).map(|i| i % 3 == 0));
262+
let a = bit_buffer_from_fn(length, |i| i % 2 == 0);
263+
let b = bit_buffer_from_fn(length, |i| i % 3 == 0);
228264
bencher
229265
.with_inputs(|| (&a, &b))
230266
.bench_values(|(a, b)| a | b);
@@ -242,14 +278,14 @@ fn bitwise_or_arrow_buffer(bencher: Bencher, length: usize) {
242278
#[divan::bench(args = INPUT_SIZE)]
243279
fn bitwise_not_vortex_buffer(bencher: Bencher, length: usize) {
244280
bencher
245-
.with_inputs(|| BitBuffer::from_iter((0..length).map(|i| i % 2 == 0)))
281+
.with_inputs(|| bit_buffer_from_fn(length, |i| i % 2 == 0))
246282
.bench_values(|buffer| !&buffer);
247283
}
248284

249285
#[divan::bench(args = INPUT_SIZE)]
250286
fn bitwise_not_vortex_buffer_mut(bencher: Bencher, length: usize) {
251287
bencher
252-
.with_inputs(|| BitBufferMut::from_iter((0..length).map(|i| i % 2 == 0)))
288+
.with_inputs(|| bit_buffer_mut_from_fn(length, |i| i % 2 == 0))
253289
.bench_values(|buffer| !buffer);
254290
}
255291

@@ -262,7 +298,7 @@ fn bitwise_not_arrow_buffer(bencher: Bencher, length: usize) {
262298

263299
#[divan::bench(args = INPUT_SIZE)]
264300
fn iter_vortex_buffer(bencher: Bencher, length: usize) {
265-
let buffer = BitBuffer::from_iter((0..length).map(|i| i % 2 == 0));
301+
let buffer = bit_buffer_from_fn(length, |i| i % 2 == 0);
266302
bencher.with_inputs(|| &buffer).bench_refs(|buffer| {
267303
for value in buffer.iter() {
268304
divan::black_box(value);
@@ -282,7 +318,7 @@ fn iter_arrow_buffer(bencher: Bencher, length: usize) {
282318

283319
#[divan::bench(args = INPUT_SIZE)]
284320
fn set_indices_vortex_buffer(bencher: Bencher, length: usize) {
285-
let buffer = BitBuffer::from_iter((0..length).map(|i| i % 2 == 0));
321+
let buffer = bit_buffer_from_fn(length, |i| i % 2 == 0);
286322
bencher.with_inputs(|| &buffer).bench_refs(|buffer| {
287323
for idx in buffer.set_indices() {
288324
divan::black_box(idx);

0 commit comments

Comments
 (0)