mod common;
use common::N;
use common::data::gen_data;
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
use dsi_bitstream::prelude::*;
use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};
use std::hint::black_box;
#[cfg(feature = "bench-u16")]
type ReadWord = u16;
#[cfg(all(feature = "bench-u64", not(feature = "bench-u16")))]
type ReadWord = u64;
#[cfg(not(any(feature = "bench-u16", feature = "bench-u64")))]
type ReadWord = u32;
#[cfg(feature = "bench-u16")]
const GROUP: &str = "bufbitreader_u16";
#[cfg(all(feature = "bench-u64", not(feature = "bench-u16")))]
const GROUP: &str = "bufbitreader_u64";
#[cfg(not(any(feature = "bench-u16", feature = "bench-u64")))]
const GROUP: &str = "bufbitreader";
macro_rules! bench_endian {
($group:expr, $E:ty, $ename:literal, $unary_data:expr, $widths:expr, $values:expr) => {{
{
let data: &[u64] = $unary_data;
let encoded = {
let mut buffer: Box<[u64]> = vec![0u64; 10 * N].into_boxed_slice();
{
let mut wr =
BufBitWriter::<$E, _>::new(MemWordWriterSlice::<u64, _>::new(&mut *buffer));
for &value in data {
wr.write_unary(value).unwrap();
}
}
buffer
};
let slice: &[ReadWord] = unsafe { encoded.align_to::<ReadWord>().1 };
let expected = data.iter().fold(0u64, |acc, &v| acc.wrapping_add(v));
let mut reader = BufBitReader::<$E, _>::new(MemWordReader::new(slice));
let mut sum = 0u64;
for _ in 0..data.len() {
sum = sum.wrapping_add(reader.read_unary().unwrap());
}
assert_eq!(sum, expected, "read_unary/{} checksum mismatch", $ename);
$group.bench_function(concat!("read_unary/", $ename), |b| {
b.iter(|| {
let mut r = BufBitReader::<$E, _>::new(MemWordReader::new(slice));
for _ in 0..N {
black_box(r.read_unary().unwrap());
}
});
});
}
{
let widths: &[usize] = $widths;
let values: &[u64] = $values;
let encoded = {
let mut buffer: Box<[u64]> = vec![0u64; 10 * N].into_boxed_slice();
{
let mut wr =
BufBitWriter::<$E, _>::new(MemWordWriterSlice::<u64, _>::new(&mut *buffer));
for (&value, &n_bits) in values.iter().zip(widths) {
wr.write_bits(value, n_bits).unwrap();
}
}
buffer
};
let slice: &[ReadWord] = unsafe { encoded.align_to::<ReadWord>().1 };
let expected = values.iter().fold(0u64, |acc, &v| acc.wrapping_add(v));
let mut reader = BufBitReader::<$E, _>::new(MemWordReader::new(slice));
let mut sum = 0u64;
for &n_bits in widths {
sum = sum.wrapping_add(reader.read_bits(n_bits).unwrap());
}
assert_eq!(sum, expected, "read_bits/{} checksum mismatch", $ename);
$group.bench_function(concat!("read_bits/", $ename), |b| {
b.iter(|| {
let mut r = BufBitReader::<$E, _>::new(MemWordReader::new(slice));
for &n_bits in widths {
black_box(r.read_bits(n_bits).unwrap());
}
});
});
}
}};
}
fn bench_bufbitreader(c: &mut Criterion) {
#[cfg(target_os = "linux")]
common::utils::pin_to_core(2);
let mut group = c.benchmark_group(GROUP);
group.throughput(Throughput::Elements(u64::try_from(N).unwrap()));
let unary_data = gen_data(
|x| usize::try_from(x.saturating_add(1)).expect("unary code length fits usize"),
false,
);
let mut rng = SmallRng::seed_from_u64(0x0DD5_EED5);
let widths: Vec<usize> = (0..N).map(|_| rng.random_range(1..=32usize)).collect();
let values: Vec<u64> = widths
.iter()
.map(|&w| rng.random::<u64>() & (u64::MAX >> (64 - w)))
.collect();
bench_endian!(group, BE, "BE", &unary_data, &widths, &values);
bench_endian!(group, LE, "LE", &unary_data, &widths, &values);
group.finish();
}
criterion_group! {
name = bufbitreader;
config = Criterion::default()
.sample_size(10)
.warm_up_time(std::time::Duration::from_millis(500))
.measurement_time(std::time::Duration::from_secs(2));
targets = bench_bufbitreader
}
criterion_main!(bufbitreader);