use criterion::{Criterion, criterion_group, criterion_main};
use std::hint::black_box;
use vlen::{
bulk_decode, bulk_decode_i64, bulk_decode_u32, bulk_decode_u64,
bulk_encode, bulk_encode_i64, bulk_encode_u32, bulk_encode_u64,
};
const N: usize = 1024;
fn values(kind: &str) -> Vec<u32> {
(0..N as u32)
.map(|i| match kind {
"small" => i % 0x80,
"two_byte" => 0x80 + (i * 37) % 0x3F80,
"three_byte" => 0x4000 + (i * 97) % 0x1F_C000,
"four_byte" => 0x20_0000 + (i * 997) % 0xFE0_0000,
"mixed" => match i % 4 {
0 => i,
1 => 1000 + i,
2 => 1_000_000 + i,
_ => 1_000_000_000 + i,
},
"random" => {
let mut x = i.wrapping_mul(0x9E37_79B9) ^ 0xDEAD_BEEF;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
match x % 4 {
0 => x % 0x80,
1 => 0x80 + x % 0x3F80,
2 => 0x4000 + x % 0x1F_C000,
_ => 0x20_0000 + x % 0xFE0_0000,
}
},
_ => 0x1000_0000 + i,
})
.collect()
}
fn bench_bulk_i64_deltas(c: &mut Criterion) {
for (kind, spike_every) in [("smooth", 64), ("choppy", 4)] {
let values: Vec<i64> = (0..N as i64)
.map(|i| {
let mut x = (i as u32).wrapping_mul(0x9E37_79B9) ^ 0xBEEF;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
if x % spike_every == 0 {
(x % 0x40_0000) as i64 - 0x20_0000
} else {
(x % 63) as i64 - 31
}
})
.collect();
let mut buf = vec![0u8; N * 9];
c.bench_function(&format!("bulk_encode_i64/{kind}"), |b| {
b.iter(|| {
bulk_encode_i64(black_box(&mut buf), black_box(&values))
.unwrap()
})
});
c.bench_function(&format!("bulk_encode_generic_i64/{kind}"), |b| {
b.iter(|| {
bulk_encode(black_box(&mut buf), black_box(&values)).unwrap()
})
});
let encoded_len = bulk_encode(&mut buf, &values).unwrap();
let encoded = &buf[..encoded_len];
let mut decoded = vec![0i64; N];
c.bench_function(&format!("bulk_decode_i64/{kind}"), |b| {
b.iter(|| {
bulk_decode_i64(black_box(encoded), black_box(&mut decoded))
.unwrap()
})
});
c.bench_function(&format!("bulk_decode_generic_i64/{kind}"), |b| {
b.iter(|| {
bulk_decode(black_box(encoded), black_box(&mut decoded))
.unwrap()
})
});
}
}
fn bench_bulk_u64(c: &mut Criterion) {
for kind in ["small", "two_byte", "random"] {
let values: Vec<u64> =
values(kind).into_iter().map(|v| v as u64).collect();
let mut buf = vec![0u8; N * 9];
c.bench_function(&format!("bulk_encode_u64/{kind}"), |b| {
b.iter(|| {
bulk_encode_u64(black_box(&mut buf), black_box(&values))
.unwrap()
})
});
let encoded_len = bulk_encode(&mut buf, &values).unwrap();
let encoded = &buf[..encoded_len];
let mut decoded = vec![0u64; N];
c.bench_function(&format!("bulk_decode_u64/{kind}"), |b| {
b.iter(|| {
bulk_decode_u64(black_box(encoded), black_box(&mut decoded))
.unwrap()
})
});
}
}
fn bench_bulk(c: &mut Criterion) {
for kind in [
"small",
"two_byte",
"three_byte",
"four_byte",
"mixed",
"random",
"large",
] {
let values = values(kind);
let mut buf = vec![0u8; N * 5];
c.bench_function(&format!("bulk_encode_u32/{kind}"), |b| {
b.iter(|| {
bulk_encode_u32(black_box(&mut buf), black_box(&values))
.unwrap()
})
});
c.bench_function(&format!("bulk_encode_generic/{kind}"), |b| {
b.iter(|| {
bulk_encode(black_box(&mut buf), black_box(&values)).unwrap()
})
});
let encoded_len = bulk_encode(&mut buf, &values).unwrap();
let encoded = &buf[..encoded_len];
let mut decoded = vec![0u32; N];
c.bench_function(&format!("bulk_decode_u32/{kind}"), |b| {
b.iter(|| {
bulk_decode_u32(black_box(encoded), black_box(&mut decoded))
.unwrap()
})
});
c.bench_function(&format!("bulk_decode_generic/{kind}"), |b| {
b.iter(|| {
bulk_decode(black_box(encoded), black_box(&mut decoded))
.unwrap()
})
});
}
}
criterion_group!(
benches,
bench_bulk,
bench_bulk_u64,
bench_bulk_i64_deltas,
bench_iter
);
criterion_main!(benches);
fn bench_iter(c: &mut Criterion) {
for kind in ["small", "two_byte", "random"] {
let values = values(kind);
let mut buf = vec![0u8; N * 5];
let len = bulk_encode(&mut buf, &values).unwrap();
let encoded = &buf[..len];
let mut out = vec![0u32; N];
c.bench_function(&format!("iter_generic/{kind}"), |b| {
b.iter(|| {
vlen::decode_iter::<u32>(black_box(encoded))
.map(Result::unwrap)
.fold(0u64, |acc, v| acc.wrapping_add(v as u64))
})
});
c.bench_function(&format!("iter_run/{kind}"), |b| {
b.iter(|| {
vlen::decode_iter_u32(black_box(encoded))
.map(Result::unwrap)
.fold(0u64, |acc, v| acc.wrapping_add(v as u64))
})
});
c.bench_function(&format!("iter_bulk_slice/{kind}"), |b| {
b.iter(|| {
bulk_decode_u32(black_box(encoded), black_box(&mut out))
.unwrap();
out.iter().fold(0u64, |acc, &v| acc.wrapping_add(v as u64))
})
});
}
}