diff --git a/crates/bench/Cargo.toml b/crates/bench/Cargo.toml index 05f22c4..875e689 100644 --- a/crates/bench/Cargo.toml +++ b/crates/bench/Cargo.toml @@ -3,6 +3,10 @@ name = "log-viewer-bench" version = "0.1.0" edition = "2024" +[lib] +name = "log_viewer_bench" +path = "src/lib.rs" + [[bin]] name = "log-viewer-bench" path = "src/main.rs" diff --git a/crates/bench/src/report.rs b/crates/bench/src/report.rs index bc870aa..4b68642 100644 --- a/crates/bench/src/report.rs +++ b/crates/bench/src/report.rs @@ -43,14 +43,14 @@ pub fn format_report(results: &[BenchmarkResult]) -> String { let mut variants: Vec<(String, String)> = Vec::new(); for r in category_results { - let key = format!("{} ({})", r.backend, r.variant); if !variants .iter() - .any(|(b, v)| format!("{} ({})", b, v) == key) + .any(|(b, v)| b == &r.backend && v == &r.variant) { variants.push((r.backend.clone(), r.variant.clone())); } } + variants.sort(); report.push_str("### Latency\n\n"); report.push_str("| Test |"); @@ -143,7 +143,13 @@ pub fn format_report(results: &[BenchmarkResult]) -> String { report.push_str("| Test | Variant | RSS | Peak RSS | Page Faults |\n"); report.push_str("|------|---------|-----|----------|-------------|\n"); - for r in category_results { + let mut mem_rows: Vec<&BenchmarkResult> = + category_results.iter().copied().collect(); + mem_rows.sort_by(|a, b| { + (&a.test_name, &a.backend, &a.variant) + .cmp(&(&b.test_name, &b.backend, &b.variant)) + }); + for r in mem_rows { let variant_label = format!("{} ({})", r.backend, r.variant); report.push_str(&format!( "| {} | {} | {} | {} | {} |\n", @@ -157,7 +163,7 @@ pub fn format_report(results: &[BenchmarkResult]) -> String { report.push('\n'); } - let extras: Vec<(String, String, Vec<(String, f64)>)> = category_results + let mut extras: Vec<(String, String, Vec<(String, f64)>)> = category_results .iter() .filter(|r| !r.extra.is_empty()) .map(|r| { @@ -171,6 +177,7 @@ pub fn format_report(results: &[BenchmarkResult]) -> String { ) }) .collect(); + extras.sort_by(|a, b| (&a.0, &a.1).cmp(&(&b.0, &b.1))); if !extras.is_empty() { report.push_str("### Extra Metrics\n\n"); @@ -205,3 +212,50 @@ fn capitalize(s: &str) -> String { Some(f) => f.to_uppercase().chain(c).collect(), } } + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashMap; + + fn make_result( + category: &str, + test_name: &str, + backend: &str, + variant: &str, + latency_us: Vec, + ) -> BenchmarkResult { + BenchmarkResult { + category: category.to_string(), + test_name: test_name.to_string(), + backend: backend.to_string(), + variant: variant.to_string(), + latency_us, + rss_kb: 0, + rss_peak_kb: 0, + page_faults: 0, + extra: HashMap::new(), + } + } + + #[test] + fn report_ordering_independent_of_input_order() { + let set_a = vec![ + make_result("sequential", "read_1mb", "pread", "default", vec![100, 110, 105]), + make_result("sequential", "read_1mb", "mmap", "default", vec![80, 85, 90]), + make_result("sequential", "read_4kb", "pread", "default", vec![10, 12, 11]), + make_result("sequential", "read_4kb", "mmap", "default", vec![8, 9, 7]), + ]; + + let set_b = vec![ + make_result("sequential", "read_4kb", "mmap", "default", vec![8, 9, 7]), + make_result("sequential", "read_1mb", "mmap", "default", vec![80, 85, 90]), + make_result("sequential", "read_4kb", "pread", "default", vec![10, 12, 11]), + make_result("sequential", "read_1mb", "pread", "default", vec![100, 110, 105]), + ]; + + let report_a = format_report(&set_a); + let report_b = format_report(&set_b); + assert_eq!(report_a, report_b, "Reports must be identical regardless of input order"); + } +}