#![expect(missing_docs, reason = "Benchmark code")]
use std::alloc::System;
use std::hint::black_box;
use std::iter;
use std::num::NonZero;
use alloc_tracker::{Allocator, Session};
use bytesbuf::mem::BlockSize;
use bytesbuf::mem::testing::{FixedBlockMemory, TransparentMemory};
use bytesbuf::{BytesBuf, BytesView};
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
use new_zealand::nz;
criterion_group!(benches, entrypoint);
criterion_main!(benches);
#[global_allocator]
static ALLOCATOR: Allocator<System> = Allocator::system();
const TEST_SPAN_SIZE: NonZero<BlockSize> = nz!(12345);
const TEST_DATA: &[u8] = &[88_u8; TEST_SPAN_SIZE.get() as usize];
const MAX_INLINE_SPANS: usize = bytesbuf::MAX_INLINE_SPANS;
const MANY_SPANS: usize = 32;
#[expect(clippy::too_many_lines, reason = "Is fine - lots of benchmarks to do!")]
fn entrypoint(c: &mut Criterion) {
let allocs = Session::new();
let memory = TransparentMemory::new();
let test_data_as_view = BytesView::copied_from_slice(TEST_DATA, &memory);
let max_inline = iter::repeat_n(test_data_as_view.clone(), MAX_INLINE_SPANS).collect::<Vec<_>>();
let max_inline_as_view = BytesView::from_views(max_inline.iter().cloned());
let many = iter::repeat_n(test_data_as_view.clone(), MANY_SPANS).collect::<Vec<_>>();
let many_as_view = BytesView::from_views(many.iter().cloned());
let mut group = c.benchmark_group("BytesBuf");
let new_allocs = allocs.operation("new");
group.bench_function("new", |b| {
b.iter(|| {
let _span = new_allocs.measure_thread();
BytesBuf::new()
});
});
group.bench_function("len_empty", |b| {
b.iter_batched_ref(BytesBuf::new, |buf| buf.len(), BatchSize::SmallInput);
});
group.bench_function("len_many", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.put_bytes(many_as_view.clone());
buf
},
|buf| buf.len(),
BatchSize::SmallInput,
);
});
group.bench_function("is_empty_empty", |b| {
b.iter_batched_ref(BytesBuf::new, |buf| buf.is_empty(), BatchSize::SmallInput);
});
group.bench_function("is_empty_many", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.put_bytes(many_as_view.clone());
buf
},
|buf| buf.is_empty(),
BatchSize::SmallInput,
);
});
group.bench_function("capacity_empty", |b| {
b.iter_batched_ref(BytesBuf::new, |buf| buf.capacity(), BatchSize::SmallInput);
});
group.bench_function("capacity_many", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.put_bytes(many_as_view.clone());
buf
},
|buf| buf.capacity(),
BatchSize::SmallInput,
);
});
group.bench_function("reserve", |b| {
b.iter_batched_ref(BytesBuf::new, |buf| buf.reserve(black_box(1), &memory), BatchSize::SmallInput);
});
let allocs_op = allocs.operation("put_view_clean");
group.bench_function("put_view_clean", |b| {
b.iter_batched_ref(
BytesBuf::new,
|buf| {
let _span = allocs_op.measure_thread();
buf.put_bytes(test_data_as_view.clone());
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("put_view_dirty");
group.bench_function("put_view_dirty", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.reserve(TEST_SPAN_SIZE.get() as usize, &memory);
buf.put_byte(123);
buf
},
|buf| {
let _span = allocs_op.measure_thread();
buf.put_bytes(test_data_as_view.clone());
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("consume_one_span");
group.bench_function("consume_one_span", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.put_bytes(many_as_view.clone());
buf
},
|buf| {
let _span = allocs_op.measure_thread();
buf.consume(TEST_SPAN_SIZE.get() as usize)
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("consume_max_inline_spans");
group.bench_function("consume_max_inline_spans", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.put_bytes(max_inline_as_view.clone());
buf
},
|buf| {
let _span = allocs_op.measure_thread();
buf.consume(TEST_SPAN_SIZE.get() as usize);
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("consume_many_spans");
group.bench_function("consume_many_spans", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.put_bytes(many_as_view.clone());
buf
},
|buf| {
let _span = allocs_op.measure_thread();
buf.consume_all()
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("extend_lifetime");
group.bench_function("extend_lifetime", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.put_bytes(test_data_as_view.clone());
buf
},
|buf| {
let _span = allocs_op.measure_thread();
buf.extend_lifetime()
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("vectored_write_one_span");
group.bench_function("vectored_write_one_span", |b| {
const BLOCK_SIZE: NonZero<BlockSize> = nz!(10);
let memory = FixedBlockMemory::new(BLOCK_SIZE);
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.reserve(BLOCK_SIZE.get() as usize, &memory);
buf
},
|buf| {
let _span = allocs_op.measure_thread();
let write = buf.begin_vectored_write(None);
unsafe {
write.commit(BLOCK_SIZE.get() as usize);
}
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("vectored_write_max_inline_spans");
group.bench_function("vectored_write_max_inline_spans", |b| {
const BLOCK_SIZE: NonZero<BlockSize> = nz!(10);
let memory = FixedBlockMemory::new(BLOCK_SIZE);
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.reserve(BLOCK_SIZE.get() as usize * MAX_INLINE_SPANS, &memory);
buf
},
|buf| {
let _span = allocs_op.measure_thread();
let write = buf.begin_vectored_write(None);
unsafe {
write.commit(BLOCK_SIZE.get() as usize * MAX_INLINE_SPANS);
}
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("vectored_write_many_spans");
group.bench_function("vectored_write_many_spans", |b| {
const BLOCK_SIZE: NonZero<BlockSize> = nz!(10);
let memory = FixedBlockMemory::new(BLOCK_SIZE);
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.reserve(BLOCK_SIZE.get() as usize * MANY_SPANS, &memory);
buf
},
|buf| {
let _span = allocs_op.measure_thread();
let write = buf.begin_vectored_write(None);
unsafe {
write.commit(BLOCK_SIZE.get() as usize * MANY_SPANS);
}
},
BatchSize::SmallInput,
);
});
group.bench_function("advance_mut_one_span", |b| {
const BLOCK_SIZE: NonZero<BlockSize> = nz!(10);
let memory = FixedBlockMemory::new(BLOCK_SIZE);
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.reserve(BLOCK_SIZE.get() as usize, &memory);
buf
},
|buf| {
unsafe {
buf.advance(BLOCK_SIZE.get() as usize);
}
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("peek_frozen_all");
group.bench_function("peek_frozen_all", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.put_bytes(many_as_view.clone());
buf
},
|buf| {
let _span = allocs_op.measure_thread();
let mut peeked = buf.peek();
while !peeked.is_empty() {
peeked.advance(peeked.first_slice().len());
}
},
BatchSize::SmallInput,
);
});
let allocs_op = allocs.operation("peek_unfrozen_all");
group.bench_function("peek_unfrozen_all", |b| {
b.iter_batched_ref(
|| {
let mut buf = BytesBuf::new();
buf.reserve(TEST_SPAN_SIZE.get() as usize, &memory);
buf.put_byte(123);
buf
},
|buf| {
let _span = allocs_op.measure_thread();
let mut peeked = buf.peek();
while !peeked.is_empty() {
peeked.advance(peeked.first_slice().len());
}
},
BatchSize::SmallInput,
);
});
group.finish();
allocs.print_to_stdout();
}