use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput};
use pklib::{implode_bytes, CompressionMode, DictionarySize};
use std::hint::black_box;
use std::time::Duration;
fn generate_test_data(size: usize, pattern: &str) -> Vec<u8> {
match pattern {
"text" => {
let base = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. ";
let mut data = Vec::with_capacity(size);
while data.len() < size {
data.extend_from_slice(base);
}
data.truncate(size);
data
}
"binary" => {
(0..size).map(|i| ((i * 17 + 11) % 256) as u8).collect()
}
"repetitive" => {
let pattern = b"ABCDEFGHIJ";
let mut data = Vec::with_capacity(size);
while data.len() < size {
data.extend_from_slice(pattern);
}
data.truncate(size);
data
}
"random" => {
(0..size)
.map(|i| {
let x = i as u32;
((x.wrapping_mul(1664525).wrapping_add(1013904223)) % 256) as u8
})
.collect()
}
_ => panic!("Unknown pattern: {pattern}"),
}
}
fn compression_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("compression_throughput");
group.measurement_time(Duration::from_secs(10));
group.sample_size(100);
for size in [1024, 10240, 102400, 1048576].iter() {
let size_label = match *size {
1024 => "1KB",
10240 => "10KB",
102400 => "100KB",
1048576 => "1MB",
_ => "unknown",
};
for pattern in ["text", "binary", "repetitive", "random"].iter() {
let data = generate_test_data(*size, pattern);
for mode in [CompressionMode::Binary, CompressionMode::ASCII].iter() {
for dict_size in [
DictionarySize::Size1K,
DictionarySize::Size2K,
DictionarySize::Size4K,
]
.iter()
{
let mode_str = match mode {
CompressionMode::Binary => "binary",
CompressionMode::ASCII => "ascii",
};
let dict_str = match dict_size {
DictionarySize::Size1K => "1KB",
DictionarySize::Size2K => "2KB",
DictionarySize::Size4K => "4KB",
};
let benchmark_id = BenchmarkId::from_parameter(format!(
"{size_label}/{pattern}/{mode_str}/{dict_str}"
));
group.throughput(Throughput::Bytes(*size as u64));
group.bench_with_input(benchmark_id, &data, |b, data| {
b.iter(|| {
implode_bytes(black_box(data), black_box(*mode), black_box(*dict_size))
.expect("Compression failed")
});
});
}
}
}
}
group.finish();
}
fn compression_ratio(c: &mut Criterion) {
let mut group = c.benchmark_group("compression_ratio");
group.measurement_time(Duration::from_secs(5));
let test_sizes = vec![10240, 102400];
for size in test_sizes {
for pattern in ["text", "binary", "repetitive", "random"].iter() {
let data = generate_test_data(size, pattern);
for mode in [CompressionMode::Binary, CompressionMode::ASCII].iter() {
let dict_size = DictionarySize::Size4K;
let mode_str = match mode {
CompressionMode::Binary => "binary",
CompressionMode::ASCII => "ascii",
};
let benchmark_id =
BenchmarkId::from_parameter(format!("{size}/{pattern}/{mode_str}"));
group.bench_with_input(benchmark_id, &data, |b, data| {
b.iter_batched(
|| data.clone(),
|data| {
let compressed = implode_bytes(
black_box(&data),
black_box(*mode),
black_box(dict_size),
)
.expect("Compression failed");
let ratio = compressed.len() as f64 / data.len() as f64;
black_box(ratio)
},
BatchSize::SmallInput,
);
});
}
}
}
group.finish();
}
fn large_file_compression(c: &mut Criterion) {
let mut group = c.benchmark_group("large_file_compression");
group.measurement_time(Duration::from_secs(20));
group.sample_size(10);
for size in [10485760, 104857600].iter() {
let size_label = match *size {
10485760 => "10MB",
104857600 => "100MB",
_ => "unknown",
};
let data = generate_test_data(*size, "text");
let mode = CompressionMode::Binary;
let dict_size = DictionarySize::Size4K;
let benchmark_id = BenchmarkId::from_parameter(size_label);
group.throughput(Throughput::Bytes(*size as u64));
group.bench_with_input(benchmark_id, &data, |b, data| {
b.iter(|| {
implode_bytes(black_box(data), black_box(mode), black_box(dict_size))
.expect("Compression failed")
});
});
}
group.finish();
}
criterion_group!(
benches,
compression_throughput,
compression_ratio,
large_file_compression
);
criterion_main!(benches);