use core::fmt;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use eolify::{helpers::vec_to_uninit_mut, NormalizeChunk, NormalizeChunkResult, CRLF, LF};
use std::{mem::MaybeUninit, time::Duration};
fn make_buffer(size: usize, pattern: &str) -> Vec<u8> {
let mut v = vec![0u8; size];
match pattern {
"random" => {
let mut state: u64 = 0x12345678;
for byte in &mut v {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1);
*byte = (state & 0xFF) as u8;
}
}
"all_lf" => {
for b in &mut v[..] {
*b = b'\n';
}
}
"all_cr" => {
for b in &mut v[..] {
*b = b'\r';
}
}
"crlf" => {
for (i, byte) in v.iter_mut().enumerate() {
*byte = if i % 2 == 0 { b'\r' } else { b'\n' };
}
}
"mixed" => {
for (i, byte) in v.iter_mut().enumerate() {
*byte = match i % 7 {
0 => b'\r',
1 => b'\n',
2 => b'a',
_ => b'b',
}
}
}
_ => {}
}
v
}
enum Format {
Crlf,
LF,
}
impl fmt::Display for Format {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Format::Crlf => write!(f, "crlf"),
Format::LF => write!(f, "lf"),
}
}
}
impl Format {
fn normalize_chunk(
&self,
input: &[u8],
output: &mut [MaybeUninit<u8>],
state: Option<&bool>,
is_last_chunk: bool,
) -> eolify::Result<NormalizeChunkResult<bool>> {
match self {
Format::Crlf => CRLF::normalize_chunk(input, output, state, is_last_chunk),
Format::LF => LF::normalize_chunk(input, output, state, is_last_chunk),
}
}
}
fn bench_throughput(c: &mut Criterion) {
let formats = [Format::Crlf, Format::LF];
for format in formats {
let mut group1 = c.benchmark_group(format!("{format}_throughput"));
group1.measurement_time(Duration::from_secs(3));
group1.sample_size(10);
let sizes = [8 << 10, 1 << 20]; let patterns = ["random", "all_lf", "all_cr", "crlf", "mixed"];
for &size in &sizes {
for &pattern in &patterns {
let buf = make_buffer(size, pattern);
let id = BenchmarkId::new(pattern, size);
group1.throughput(Throughput::Bytes(size as u64));
group1.bench_with_input(id, &buf, |b, data| {
let mut out = Vec::with_capacity(data.len() * 3 + 8);
b.iter(|| {
let status = format
.normalize_chunk(data, vec_to_uninit_mut(&mut out), None, false)
.unwrap();
std::hint::black_box(status.output_len());
std::hint::black_box(status.state());
})
});
}
}
group1.finish();
let mut group2 = c.benchmark_group(format!("{format}_chunked"));
group2.measurement_time(Duration::from_secs(3));
group2.sample_size(10);
let max_size = 64 << 20; let chunk_sizes = [1 << 10, 8 << 10, 16 << 10]; for &pattern in &patterns {
let data = make_buffer(max_size, pattern);
let max_chunk = *chunk_sizes.iter().max().unwrap();
let mut out = Vec::with_capacity(max_chunk * 3 + 8);
for &chunk in &chunk_sizes {
let id = BenchmarkId::new(format!("{pattern}/chunk-{chunk}"), max_size);
group2.throughput(Throughput::Bytes(max_size as u64));
group2.bench_with_input(id, &data, |b, input| {
b.iter(|| {
let mut state = None;
for ch in input.chunks(chunk) {
let status = format
.normalize_chunk(
ch,
vec_to_uninit_mut(&mut out),
state.as_ref(),
false,
)
.unwrap();
std::hint::black_box(status.output_len());
state = status.state().copied();
}
std::hint::black_box(state);
})
});
}
}
group2.finish();
}
}
criterion_group!(benches, bench_throughput);
criterion_main!(benches);