Files
Oxicloud/benches/thumbnails.rs
T

109 lines
3.9 KiB
Rust
Raw Normal View History

//! Phase 0 — Task 0.2: thumbnail render latency + output-size baseline.
//!
//! Measures the CPU-bound render path (decode → EXIF orientation → resize →
//! JPEG encode) per size and for the all-sizes upload path, across the size/
//! format corpus. `Throughput::Elements(1)` makes criterion report images/sec
//! alongside ms/image. Output byte sizes (bandwidth/disk proxy) are printed once
//! as a table before the timed runs.
//!
//! Run: `cargo bench --features bench`
//! HTML report: `target/criterion/report/index.html`
use std::time::Duration;
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
use oxicloud::bench_support::{self, CorpusCase};
use oxicloud::infrastructure::services::thumbnail_service::{ThumbnailService, ThumbnailSize};
const SIZES: [ThumbnailSize; 3] = [
ThumbnailSize::Icon,
ThumbnailSize::Preview,
ThumbnailSize::Large,
];
/// Print the output-size table (per-size encoded JPEG bytes) once. This is the
/// bandwidth/disk half of the Task 0.2 deliverable.
fn print_output_sizes(corpus: &[CorpusCase]) {
println!("\n=== Output size baseline (encoded JPEG bytes per thumbnail) ===");
println!(
"| {:<17} | {:<5} | {:>11} | {:>8} | {:>7} | {:>8} | {:>8} |",
"case", "fmt", "source", "input KB", "icon B", "preview B", "large B"
);
println!(
"|{:-<19}|{:-<7}|{:-<13}|{:-<10}|{:-<9}|{:-<10}|{:-<10}|",
"", "", "", "", "", "", ""
);
for case in corpus {
let sizes = ThumbnailService::bench_render_all(&case.bytes).unwrap_or_default();
let get = |want: ThumbnailSize| {
sizes
.iter()
.find(|(s, _)| *s == want)
.map(|(_, n)| *n)
.unwrap_or(0)
};
println!(
"| {:<17} | {:<5} | {:>5}×{:<5} | {:>8} | {:>6} | {:>8} | {:>8} |",
case.name,
case.format,
case.width,
case.height,
case.bytes.len() / 1024,
get(ThumbnailSize::Icon),
get(ThumbnailSize::Preview),
get(ThumbnailSize::Large),
);
}
println!();
}
fn configure(group: &mut criterion::BenchmarkGroup<'_, criterion::measurement::WallTime>) {
// Big images (48 MP) are slow per-iter; keep the suite bounded but stable.
group
.sample_size(10)
.warm_up_time(Duration::from_secs(1))
.measurement_time(Duration::from_secs(6));
}
fn bench_thumbnails(c: &mut Criterion) {
let corpus = bench_support::load_or_generate();
assert!(!corpus.is_empty(), "corpus is empty — generation failed");
print_output_sizes(&corpus);
// Per-size single-thumbnail latency (the lazy request path).
for size in SIZES {
let mut group = c.benchmark_group(format!("render_thumbnail/{size:?}"));
configure(&mut group);
for case in &corpus {
group.throughput(Throughput::Elements(1));
group.bench_with_input(BenchmarkId::from_parameter(case.name), case, |b, case| {
b.iter(|| {
let out =
ThumbnailService::bench_render_thumbnail(black_box(&case.bytes), size)
.expect("render_thumbnail");
black_box(out.len())
});
});
}
group.finish();
}
// All-sizes-in-one-decode latency (the eager upload path).
let mut group = c.benchmark_group("render_all");
configure(&mut group);
for case in &corpus {
group.throughput(Throughput::Elements(1));
group.bench_with_input(BenchmarkId::from_parameter(case.name), case, |b, case| {
b.iter(|| {
let out =
ThumbnailService::bench_render_all(black_box(&case.bytes)).expect("render_all");
black_box(out.len())
});
});
}
group.finish();
}
criterion_group!(benches, bench_thumbnails);
criterion_main!(benches);