fix(bench): stabilize report column/row ordering across input permutations (#39)
- variants: use direct tuple comparison instead of format! string, add sort() after dedup - Memory section: sort rows by (test_name, backend, variant) before output - Extra Metrics section: sort rows by (test_name, variant_label) before output - add [lib] target to Cargo.toml to enable unit tests - add regression test: same data in different input order produces identical report
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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<u64>,
|
||||
) -> 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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user