use cli_testing_specialist::utils::io_optimized::{
read_json_naive, read_json_optimized, write_json_naive, write_json_optimized,
};
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use serde::{Deserialize, Serialize};
use std::fs;
use std::hint::black_box;
use tempfile::NamedTempFile;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct SmallData {
name: String,
value: i32,
items: Vec<String>,
}
fn create_small_data() -> SmallData {
SmallData {
name: "test-cli".to_string(),
value: 42,
items: (0..10).map(|i| format!("item-{}", i)).collect(),
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MediumData {
items: Vec<SmallData>,
}
fn create_medium_data() -> MediumData {
MediumData {
items: (0..100)
.map(|i| SmallData {
name: format!("item-{}", i),
value: i,
items: (0..10).map(|j| format!("sub-{}-{}", i, j)).collect(),
})
.collect(),
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct LargeData {
items: Vec<MediumData>,
}
fn create_large_data() -> LargeData {
LargeData {
items: (0..10).map(|_| create_medium_data()).collect(),
}
}
fn bench_write_json(c: &mut Criterion) {
let mut group = c.benchmark_group("json_write");
let small_data = create_small_data();
let small_json = serde_json::to_string_pretty(&small_data).unwrap();
group.throughput(Throughput::Bytes(small_json.len() as u64));
group.bench_function(BenchmarkId::new("naive", "small_1kb"), |b| {
b.iter(|| {
let temp_file = NamedTempFile::new().unwrap();
write_json_naive(black_box(&small_data), temp_file.path()).unwrap();
});
});
group.bench_function(BenchmarkId::new("optimized", "small_1kb"), |b| {
b.iter(|| {
let temp_file = NamedTempFile::new().unwrap();
write_json_optimized(black_box(&small_data), temp_file.path()).unwrap();
});
});
let medium_data = create_medium_data();
let medium_json = serde_json::to_string_pretty(&medium_data).unwrap();
group.throughput(Throughput::Bytes(medium_json.len() as u64));
group.bench_function(BenchmarkId::new("naive", "medium_50kb"), |b| {
b.iter(|| {
let temp_file = NamedTempFile::new().unwrap();
write_json_naive(black_box(&medium_data), temp_file.path()).unwrap();
});
});
group.bench_function(BenchmarkId::new("optimized", "medium_50kb"), |b| {
b.iter(|| {
let temp_file = NamedTempFile::new().unwrap();
write_json_optimized(black_box(&medium_data), temp_file.path()).unwrap();
});
});
let large_data = create_large_data();
let large_json = serde_json::to_string_pretty(&large_data).unwrap();
group.throughput(Throughput::Bytes(large_json.len() as u64));
group.bench_function(BenchmarkId::new("naive", "large_500kb"), |b| {
b.iter(|| {
let temp_file = NamedTempFile::new().unwrap();
write_json_naive(black_box(&large_data), temp_file.path()).unwrap();
});
});
group.bench_function(BenchmarkId::new("optimized", "large_500kb"), |b| {
b.iter(|| {
let temp_file = NamedTempFile::new().unwrap();
write_json_optimized(black_box(&large_data), temp_file.path()).unwrap();
});
});
group.finish();
}
fn bench_read_json(c: &mut Criterion) {
let mut group = c.benchmark_group("json_read");
let small_data = create_small_data();
let small_temp = NamedTempFile::new().unwrap();
write_json_optimized(&small_data, small_temp.path()).unwrap();
let small_size = fs::metadata(small_temp.path()).unwrap().len();
group.throughput(Throughput::Bytes(small_size));
group.bench_function(BenchmarkId::new("naive", "small_1kb"), |b| {
b.iter(|| {
let _data: SmallData = read_json_naive(black_box(small_temp.path())).unwrap();
});
});
group.bench_function(BenchmarkId::new("optimized", "small_1kb"), |b| {
b.iter(|| {
let _data: SmallData = read_json_optimized(black_box(small_temp.path())).unwrap();
});
});
let medium_data = create_medium_data();
let medium_temp = NamedTempFile::new().unwrap();
write_json_optimized(&medium_data, medium_temp.path()).unwrap();
let medium_size = fs::metadata(medium_temp.path()).unwrap().len();
group.throughput(Throughput::Bytes(medium_size));
group.bench_function(BenchmarkId::new("naive", "medium_50kb"), |b| {
b.iter(|| {
let _data: MediumData = read_json_naive(black_box(medium_temp.path())).unwrap();
});
});
group.bench_function(BenchmarkId::new("optimized", "medium_50kb"), |b| {
b.iter(|| {
let _data: MediumData = read_json_optimized(black_box(medium_temp.path())).unwrap();
});
});
let large_data = create_large_data();
let large_temp = NamedTempFile::new().unwrap();
write_json_optimized(&large_data, large_temp.path()).unwrap();
let large_size = fs::metadata(large_temp.path()).unwrap().len();
group.throughput(Throughput::Bytes(large_size));
group.bench_function(BenchmarkId::new("naive", "large_500kb"), |b| {
b.iter(|| {
let _data: LargeData = read_json_naive(black_box(large_temp.path())).unwrap();
});
});
group.bench_function(BenchmarkId::new("optimized", "large_500kb"), |b| {
b.iter(|| {
let _data: LargeData = read_json_optimized(black_box(large_temp.path())).unwrap();
});
});
group.finish();
}
fn bench_roundtrip_json(c: &mut Criterion) {
let mut group = c.benchmark_group("json_roundtrip");
let medium_data = create_medium_data();
let medium_json = serde_json::to_string_pretty(&medium_data).unwrap();
group.throughput(Throughput::Bytes(medium_json.len() as u64 * 2));
group.bench_function("naive", |b| {
b.iter(|| {
let temp_file = NamedTempFile::new().unwrap();
write_json_naive(black_box(&medium_data), temp_file.path()).unwrap();
let _data: MediumData = read_json_naive(black_box(temp_file.path())).unwrap();
});
});
group.bench_function("optimized", |b| {
b.iter(|| {
let temp_file = NamedTempFile::new().unwrap();
write_json_optimized(black_box(&medium_data), temp_file.path()).unwrap();
let _data: MediumData = read_json_optimized(black_box(temp_file.path())).unwrap();
});
});
group.finish();
}
criterion_group!(
benches,
bench_write_json,
bench_read_json,
bench_roundtrip_json
);
criterion_main!(benches);