fast-hex/benches/bench.rs

248 lines
7.6 KiB
Rust
Raw Normal View History

2022-10-23 11:28:06 -04:00
#![feature(generic_const_exprs)]
2022-10-23 11:28:22 -04:00
#![feature(new_uninit)]
use std::mem::MaybeUninit;
2022-10-23 11:28:06 -04:00
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use fast_hex::*;
const ASCII_BYTES: &[u8; 16] = b"Donald J. Trump!";
const HEX_BYTES: &[u8; ASCII_BYTES.len() * 2] = b"446F6E616C64204A2E205472756D7021";
const ASCII_BYTES_LONG: &[u8; 256] = b"Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!Donald J. Trump!";
const HEX_BYTES_LONG: &[u8; ASCII_BYTES_LONG.len() * 2] = b"446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021446F6E616C64204A2E205472756D7021";
2022-10-23 11:28:22 -04:00
macro_rules! name {
($group:ident, $f:literal) => {
std::boxed::Box::leak(format!(concat!("{} - ", $f), $group).into_boxed_str())
};
}
//#[track_caller]
fn test_sized<const N: usize, const HEAP_ONLY: bool>(hex_bytes: &[u8; N * 2], bytes: &[u8; N])
2022-10-23 11:28:06 -04:00
where
2022-10-23 11:28:22 -04:00
[(); N * 2]:,
2022-10-23 11:28:06 -04:00
{
2022-10-23 11:28:22 -04:00
test(hex_bytes, bytes);
if !HEAP_ONLY {
assert_eq!(
Some(bytes),
hex_bytes_sized_const::<N>(hex_bytes).as_ref(),
stringify!(hex_bytes_sized_const)
);
assert_eq!(
Some(bytes),
hex_bytes_sized::<N>(hex_bytes).as_ref(),
stringify!(hex_bytes_sized)
);
}
assert_eq!(
Some(bytes),
hex_bytes_sized_heap::<N>(hex_bytes)
.as_ref()
.map(Box::as_ref),
stringify!(hex_bytes_sized_heap)
);
}
//#[track_caller]
#[inline]
fn test(hex_bytes: &[u8], bytes: &[u8]) {
assert_eq!(hex_bytes.len(), bytes.len() * 2);
assert_eq!(
Some(bytes),
hex_bytes_dyn(hex_bytes).as_ref().map(Box::as_ref),
stringify!(hex_bytes_dyn)
);
assert_eq!(
Some(bytes),
hex_bytes_dyn_niched(hex_bytes).as_ref().map(Box::as_ref),
stringify!(hex_bytes_dyn_niched)
);
assert_eq!(
Some(bytes),
hex_bytes_dyn_unsafe_iter(hex_bytes)
.as_ref()
.map(Box::as_ref),
stringify!(hex_bytes_dyn_unsafe_iter)
);
assert_eq!(
Some(bytes),
hex_bytes_dyn_unsafe(hex_bytes).as_ref().map(Box::as_ref),
stringify!(hex_bytes_dyn_unsafe)
);
}
fn benchmark_sized<const N: usize, const HEAP_ONLY: bool>(
name: &str,
bytes: &[u8; N * 2],
c: &mut Criterion,
) where
[(); N * 2]:,
{
if !HEAP_ONLY {
c.bench_function(name!(name, "sized"), |b| {
b.iter(|| hex_bytes_sized::<N>(black_box(bytes)))
});
c.bench_function(name!(name, "sized const"), |b| {
b.iter(|| hex_bytes_sized_const::<N>(black_box(bytes)))
});
}
c.bench_function(name!(name, "sized heap"), |b| {
b.iter(|| hex_bytes_sized_heap::<N>(black_box(bytes)))
});
benchmark(name, bytes, c);
}
const BENCH_UNSAFE: bool = true;
const BENCH_UNSAFE_ITER: bool = true;
const BENCH_NON_NICHED: bool = false;
const BENCH_NICHED: bool = false;
fn benchmark(name: &str, bytes: &[u8], c: &mut Criterion) {
if BENCH_UNSAFE {
c.bench_function(name!(name, "dyn unsafe"), |b| {
b.iter(|| hex_bytes_dyn_unsafe(black_box(bytes)))
});
}
//c.bench_function(format!("{name} - dyn unsafe for"), |b| b.iter(|| hex_bytes_dyn_unsafe_for(black_box(bytes))));
if BENCH_UNSAFE_ITER {
c.bench_function(name!(name, "dyn unsafe iter"), |b| {
b.iter(|| hex_bytes_dyn_unsafe_iter(black_box(bytes)))
});
}
if BENCH_NON_NICHED {
c.bench_function(name!(name, "dyn non-niched"), |b| {
b.iter(|| hex_bytes_dyn(black_box(bytes)))
});
}
if BENCH_NICHED {
c.bench_function(name!(name, "dyn niched"), |b| {
b.iter(|| hex_bytes_dyn_niched(black_box(bytes)))
});
2022-10-23 11:28:06 -04:00
}
}
pub fn bench_16(c: &mut Criterion) {
2022-10-23 11:28:22 -04:00
test_sized::<{ ASCII_BYTES.len() }, false>(HEX_BYTES, ASCII_BYTES);
benchmark_sized::<{ ASCII_BYTES.len() }, false>("[16]", HEX_BYTES, c);
2022-10-23 11:28:06 -04:00
}
pub fn bench_256(c: &mut Criterion) {
2022-10-23 11:28:22 -04:00
test_sized::<{ ASCII_BYTES_LONG.len() }, false>(HEX_BYTES_LONG, ASCII_BYTES_LONG);
benchmark_sized::<{ ASCII_BYTES_LONG.len() }, false>("[256]", HEX_BYTES_LONG, c);
}
const fn __make_hex_chars() -> [u8; 16] {
let mut chars = [0u8; 16];
let mut i = 0u8;
while (i as usize) < chars.len() {
chars[i as usize] = if i < 10 {
'0' as u8 + i
} else {
'a' as u8 + i - 10
};
i += 1;
}
chars
}
const HEX_CHARS: [u8; 16] = __make_hex_chars();
trait SliceRandom {
type Item;
fn choose<R>(&self, rng: &mut R) -> Option<&Self::Item>
where
R: rand::Rng + ?Sized;
}
#[inline]
fn gen_index<R: rand::Rng + ?Sized, const UBOUND: usize>(rng: &mut R, ubound: usize) -> usize {
if UBOUND <= (core::u32::MAX as usize) {
rng.gen_range(0..ubound as u32) as usize
} else {
rng.gen_range(0..ubound)
}
}
impl<T> SliceRandom for [T] {
type Item = T;
#[inline]
fn choose<R>(&self, rng: &mut R) -> Option<&Self::Item>
where
R: rand::Rng + ?Sized,
{
if self.is_empty() {
None
} else {
Some(&self[gen_index::<_, { usize::MAX }>(rng, self.len())])
}
}
}
impl<const N: usize, T> SliceRandom for [T; N] {
type Item = T;
#[inline]
fn choose<R>(&self, rng: &mut R) -> Option<&Self::Item>
where
R: rand::Rng + ?Sized,
{
if self.is_empty() {
None
} else {
Some(&self[gen_index::<_, N>(rng, N)])
}
}
}
struct DisplayAsHexDigits<'a>(&'a [u8]);
impl<'a> std::fmt::Display for DisplayAsHexDigits<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
if self.0.is_empty() {
return f.write_str("[]");
}
f.write_str("[ ")?;
for b in self.0 {
match hex_digit(*b) {
d @ 0..=9 => f.write_char(('0' as u8 + d) as char),
d @ 10..=15 => f.write_char(('a' as u8 + d - 10) as char),
_ => write!(f, "0x{:x}", b),
}?;
f.write_char(' ')?;
}
f.write_char(']')?;
Ok(())
}
}
pub fn bench_1_6m(c: &mut Criterion) {
const LEN: usize = 1_600_000;
const LEN2: usize = LEN * 2;
let mut hex_bytes: Box<[MaybeUninit<u8>; LEN2]> =
unsafe { std::mem::transmute(Box::<[u8; LEN2]>::new_uninit()) };
let mut rng = rand::thread_rng();
for b in hex_bytes.iter_mut() {
*b = MaybeUninit::new(*HEX_CHARS.choose(&mut rng).unwrap());
}
let hex_bytes: Box<[u8; LEN2]> = unsafe { std::mem::transmute(hex_bytes) };
let bytes = match hex_bytes_dyn(hex_bytes.as_ref()) {
Some(b) => b,
None => {
panic!(
"Generated hex bytes were invalid: {}",
DisplayAsHexDigits(hex_bytes.as_ref())
);
}
};
test_sized::<LEN, true>(&hex_bytes, bytes.as_ref().try_into().unwrap());
benchmark_sized::<LEN, true>("[1.6m]", &hex_bytes, c);
2022-10-23 11:28:06 -04:00
}
2022-10-23 11:28:22 -04:00
criterion_group!(benches, bench_16, bench_256, bench_1_6m);
2022-10-23 11:28:06 -04:00
criterion_main!(benches);