bencher: print html table with full results too, use num_format

This commit is contained in:
Joakim Frostegård 2024-01-01 16:42:27 +01:00
parent 29e243ac81
commit 4db1fe75f2
5 changed files with 164 additions and 19 deletions

1
Cargo.lock generated
View file

@ -139,6 +139,7 @@ dependencies = [
"indoc", "indoc",
"itertools 0.12.0", "itertools 0.12.0",
"nonblock", "nonblock",
"num-format",
"once_cell", "once_cell",
"regex", "regex",
"serde", "serde",

View file

@ -27,6 +27,7 @@ clap = { version = "4", features = ["derive"] }
indexmap = "2" indexmap = "2"
indoc = "2" indoc = "2"
itertools = "0.12" itertools = "0.12"
num-format = "0.4"
nonblock = "0.2" nonblock = "0.2"
once_cell = "1" once_cell = "1"
regex = "1" regex = "1"

View file

@ -1,7 +1,9 @@
use indexmap::{IndexMap, IndexSet}; use indexmap::{IndexMap, IndexSet};
use indoc::formatdoc;
use itertools::Itertools; use itertools::Itertools;
use num_format::{Locale, ToFormattedString};
use crate::set::TrackerCoreCountResults; use crate::set::{LoadTestRunResults, TrackerCoreCountResults};
pub fn html_best_results(results: &[TrackerCoreCountResults]) -> String { pub fn html_best_results(results: &[TrackerCoreCountResults]) -> String {
let mut all_implementation_names = IndexSet::new(); let mut all_implementation_names = IndexSet::new();
@ -45,7 +47,7 @@ pub fn html_best_results(results: &[TrackerCoreCountResults]) -> String {
r#"<td><span title="{}, avg cpu utilization: {}%">{}</span></td>"#, r#"<td><span title="{}, avg cpu utilization: {}%">{}</span></td>"#,
r.tracker_info, r.tracker_info,
r.tracker_process_stats.avg_cpu_utilization, r.tracker_process_stats.avg_cpu_utilization,
r.average_responses, r.average_responses.to_formatted_string(&Locale::en),
) )
} else { } else {
"<td>-</td>".to_string() "<td>-</td>".to_string()
@ -59,6 +61,7 @@ pub fn html_best_results(results: &[TrackerCoreCountResults]) -> String {
format!( format!(
" "
<h2>Best results</h2>
<table> <table>
<thead> <thead>
<tr> <tr>
@ -78,3 +81,129 @@ pub fn html_best_results(results: &[TrackerCoreCountResults]) -> String {
data_rows.join("\n") data_rows.join("\n")
) )
} }
pub fn html_all_runs(all_results: &[TrackerCoreCountResults]) -> String {
let mut all_implementation_names = IndexSet::new();
for core_count_results in all_results {
all_implementation_names.extend(
core_count_results
.implementations
.iter()
.map(|r| r.name.clone()),
);
}
struct R {
core_count: usize,
avg_responses: Option<u64>,
tracker_keys: IndexMap<String, String>,
tracker_vcpus: String,
load_test_keys: IndexMap<String, String>,
load_test_vcpus: String,
}
let mut output = String::new();
let mut results_by_implementation: IndexMap<String, Vec<R>> = Default::default();
for implementation_name in all_implementation_names {
let results = results_by_implementation
.entry(implementation_name.clone())
.or_default();
let mut tracker_key_names: IndexSet<String> = Default::default();
let mut load_test_key_names: IndexSet<String> = Default::default();
for r in all_results {
for i in r
.implementations
.iter()
.filter(|i| i.name == implementation_name)
{
for c in i.configurations.iter() {
for l in c.load_tests.iter() {
match l {
LoadTestRunResults::Success(l) => {
tracker_key_names.extend(l.tracker_keys.keys().cloned());
load_test_key_names.extend(l.load_test_keys.keys().cloned());
results.push(R {
core_count: r.core_count,
avg_responses: Some(l.average_responses),
tracker_keys: l.tracker_keys.clone(),
tracker_vcpus: l.tracker_vcpus.as_cpu_list(),
load_test_keys: l.load_test_keys.clone(),
load_test_vcpus: l.load_test_vcpus.as_cpu_list(),
})
}
LoadTestRunResults::Failure(l) => {
tracker_key_names.extend(l.tracker_keys.keys().cloned());
load_test_key_names.extend(l.load_test_keys.keys().cloned());
results.push(R {
core_count: r.core_count,
avg_responses: None,
tracker_keys: l.tracker_keys.clone(),
tracker_vcpus: l.tracker_vcpus.as_cpu_list(),
load_test_keys: l.load_test_keys.clone(),
load_test_vcpus: l.load_test_vcpus.as_cpu_list(),
})
}
}
}
}
}
}
output.push_str(&formatdoc! {
"
<h2>Results for {}</h2>
<table>
<thead>
<tr>
<th>Cores</th>
<th>Responses</th>
{}
{}
<th>Tracker vCPUs</th>
<th>Load test vCPUs</th>
</tr>
</thead>
<tbody>
{}
</tbody>
</table>
",
implementation_name,
tracker_key_names.iter().map(|name| format!("<th>Tracker {}</th>", name)).join("\n"),
load_test_key_names.iter().map(|name| format!("<th>Load test {}</th>", name)).join("\n"),
results.into_iter().map(|r| {
formatdoc! {
"
<tr>
<td>{}</td>
<td>{}</td>
{}
{}
<td>{}</td>
<td>{}</td>
</tr>
",
r.core_count,
r.avg_responses.map(|v| v.to_formatted_string(&Locale::en)).unwrap_or_else(|| "-".to_string()),
tracker_key_names.iter().map(|name| {
format!("<th>{}</th>", r.tracker_keys.get(name).cloned().unwrap_or_else(|| "-".to_string()))
}).join("\n"),
load_test_key_names.iter().map(|name| {
format!("<th>{}</th>", r.load_test_keys.get(name).cloned().unwrap_or_else(|| "-".to_string()))
}).join("\n"),
r.tracker_vcpus,
r.load_test_vcpus,
}
}).join("\n")
});
}
output
}

View file

@ -163,9 +163,8 @@ impl<C> RunConfig<C> {
}; };
let avg_responses = { let avg_responses = {
static RE: Lazy<Regex> = Lazy::new(|| { static RE: Lazy<Regex> =
Regex::new(r"Average responses per second: ([0-9]+\.?[0-9]+)").unwrap() Lazy::new(|| Regex::new(r"Average responses per second: ([0-9]+\.?)").unwrap());
});
let opt_avg_responses = RE let opt_avg_responses = RE
.captures_iter(&load_test_stdout) .captures_iter(&load_test_stdout)
@ -175,7 +174,7 @@ impl<C> RunConfig<C> {
avg_responses.to_string() avg_responses.to_string()
}) })
.and_then(|v| v.parse::<f32>().ok()); .and_then(|v| v.parse::<u64>().ok());
if let Some(avg_responses) = opt_avg_responses { if let Some(avg_responses) = opt_avg_responses {
avg_responses avg_responses
@ -199,7 +198,7 @@ impl<C> RunConfig<C> {
pub struct RunSuccessResults { pub struct RunSuccessResults {
pub tracker_process_stats: ProcessStats, pub tracker_process_stats: ProcessStats,
pub avg_responses: f32, pub avg_responses: u64,
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,10 +1,11 @@
use std::rc::Rc; use std::rc::Rc;
use indexmap::IndexMap; use indexmap::IndexMap;
use num_format::{Locale, ToFormattedString};
use crate::{ use crate::{
common::{CpuDirection, CpuMode, TaskSetCpuList}, common::{CpuDirection, CpuMode, TaskSetCpuList},
html::html_best_results, html::{html_all_runs, html_best_results},
run::{ProcessRunner, ProcessStats, RunConfig}, run::{ProcessRunner, ProcessStats, RunConfig},
}; };
@ -106,7 +107,8 @@ pub fn run_sets<C, F, I>(
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
html_best_results(&results); println!("{}", html_all_runs(&results));
println!("{}", html_best_results(&results));
} }
pub struct TrackerCoreCountResults { pub struct TrackerCoreCountResults {
@ -185,18 +187,21 @@ impl LoadTestRunResults {
); );
let load_test_runner = load_test_gen(workers); let load_test_runner = load_test_gen(workers);
// let load_test_keys = load_test_runner.keys(); let load_test_keys = load_test_runner.keys();
let run_config = RunConfig { let run_config = RunConfig {
tracker_runner: tracker_process.clone(), tracker_runner: tracker_process.clone(),
tracker_vcpus: tracker_vcpus.clone(), tracker_vcpus: tracker_vcpus.clone(),
load_test_runner, load_test_runner,
load_test_vcpus, load_test_vcpus: load_test_vcpus.clone(),
}; };
match run_config.run(command) { match run_config.run(command) {
Ok(r) => { Ok(r) => {
println!("- Average responses per second: {}", r.avg_responses); println!(
"- Average responses per second: {}",
r.avg_responses.to_formatted_string(&Locale::en)
);
println!( println!(
"- Average tracker CPU utilization: {}%", "- Average tracker CPU utilization: {}%",
r.tracker_process_stats.avg_cpu_utilization, r.tracker_process_stats.avg_cpu_utilization,
@ -208,17 +213,22 @@ impl LoadTestRunResults {
LoadTestRunResults::Success(LoadTestRunResultsSuccess { LoadTestRunResults::Success(LoadTestRunResultsSuccess {
average_responses: r.avg_responses, average_responses: r.avg_responses,
// tracker_keys: tracker_process.keys(), tracker_keys: tracker_process.keys(),
tracker_info: tracker_process.info(), tracker_info: tracker_process.info(),
tracker_process_stats: r.tracker_process_stats, tracker_process_stats: r.tracker_process_stats,
// load_test_keys, tracker_vcpus,
load_test_keys,
load_test_vcpus,
}) })
} }
Err(results) => { Err(results) => {
println!("\nRun failed:\n{:#}\n", results); println!("\nRun failed:\n{:#}\n", results);
LoadTestRunResults::Failure(LoadTestRunResultsFailure { LoadTestRunResults::Failure(LoadTestRunResultsFailure {
// load_test_keys tracker_keys: tracker_process.keys(),
tracker_vcpus,
load_test_keys,
load_test_vcpus,
}) })
} }
} }
@ -227,13 +237,18 @@ impl LoadTestRunResults {
#[derive(Clone)] #[derive(Clone)]
pub struct LoadTestRunResultsSuccess { pub struct LoadTestRunResultsSuccess {
pub average_responses: f32, pub average_responses: u64,
// tracker_keys: IndexMap<String, String>, pub tracker_keys: IndexMap<String, String>,
pub tracker_info: String, pub tracker_info: String,
pub tracker_process_stats: ProcessStats, pub tracker_process_stats: ProcessStats,
// load_test_keys: IndexMap<String, String>, pub tracker_vcpus: TaskSetCpuList,
pub load_test_keys: IndexMap<String, String>,
pub load_test_vcpus: TaskSetCpuList,
} }
pub struct LoadTestRunResultsFailure { pub struct LoadTestRunResultsFailure {
// load_test_keys: IndexMap<String, String>, pub tracker_keys: IndexMap<String, String>,
pub tracker_vcpus: TaskSetCpuList,
pub load_test_keys: IndexMap<String, String>,
pub load_test_vcpus: TaskSetCpuList,
} }