mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 15:41:02 +00:00
[subsystem-benchmarks] Save results to json (#3829)
Here we add the ability to save subsystem benchmark results in JSON format to display them as graphs To draw graphs, CI team will use [github-action-benchmark](https://github.com/benchmark-action/github-action-benchmark). Since we are using custom benchmarks, we need to prepare [a specific data type](https://github.com/benchmark-action/github-action-benchmark?tab=readme-ov-file#examples): ``` [ { "name": "CPU Load", "unit": "Percent", "value": 50 } ] ``` Then we'll get graphs like this:  [A live page with graphs](https://benchmark-action.github.io/github-action-benchmark/dev/bench/) --------- Co-authored-by: ordian <write@reusable.software>
This commit is contained in:
Generated
+1
@@ -13644,6 +13644,7 @@ dependencies = [
|
|||||||
"sc-service",
|
"sc-service",
|
||||||
"schnorrkel 0.11.4",
|
"schnorrkel 0.11.4",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sp-application-crypto",
|
"sp-application-crypto",
|
||||||
|
|||||||
+7
@@ -27,6 +27,7 @@ use polkadot_subsystem_bench::{
|
|||||||
availability::{benchmark_availability_write, prepare_test, TestState},
|
availability::{benchmark_availability_write, prepare_test, TestState},
|
||||||
configuration::TestConfiguration,
|
configuration::TestConfiguration,
|
||||||
usage::BenchmarkUsage,
|
usage::BenchmarkUsage,
|
||||||
|
utils::save_to_file,
|
||||||
};
|
};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
@@ -60,7 +61,13 @@ fn main() -> Result<(), String> {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
println!("\rDone!{}", " ".repeat(BENCH_COUNT));
|
println!("\rDone!{}", " ".repeat(BENCH_COUNT));
|
||||||
|
|
||||||
let average_usage = BenchmarkUsage::average(&usages);
|
let average_usage = BenchmarkUsage::average(&usages);
|
||||||
|
save_to_file(
|
||||||
|
"charts/availability-distribution-regression-bench.json",
|
||||||
|
average_usage.to_chart_json().map_err(|e| e.to_string())?,
|
||||||
|
)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
println!("{}", average_usage);
|
println!("{}", average_usage);
|
||||||
|
|
||||||
// We expect no variance for received and sent
|
// We expect no variance for received and sent
|
||||||
|
|||||||
+7
@@ -28,6 +28,7 @@ use polkadot_subsystem_bench::{
|
|||||||
},
|
},
|
||||||
configuration::TestConfiguration,
|
configuration::TestConfiguration,
|
||||||
usage::BenchmarkUsage,
|
usage::BenchmarkUsage,
|
||||||
|
utils::save_to_file,
|
||||||
};
|
};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
@@ -58,7 +59,13 @@ fn main() -> Result<(), String> {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
println!("\rDone!{}", " ".repeat(BENCH_COUNT));
|
println!("\rDone!{}", " ".repeat(BENCH_COUNT));
|
||||||
|
|
||||||
let average_usage = BenchmarkUsage::average(&usages);
|
let average_usage = BenchmarkUsage::average(&usages);
|
||||||
|
save_to_file(
|
||||||
|
"charts/availability-recovery-regression-bench.json",
|
||||||
|
average_usage.to_chart_json().map_err(|e| e.to_string())?,
|
||||||
|
)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
println!("{}", average_usage);
|
println!("{}", average_usage);
|
||||||
|
|
||||||
// We expect no variance for received and sent
|
// We expect no variance for received and sent
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ prometheus_endpoint = { package = "substrate-prometheus-endpoint", path = "../..
|
|||||||
prometheus = { version = "0.13.0", default-features = false }
|
prometheus = { version = "0.13.0", default-features = false }
|
||||||
serde = { workspace = true, default-features = true }
|
serde = { workspace = true, default-features = true }
|
||||||
serde_yaml = { workspace = true }
|
serde_yaml = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
polkadot-node-core-approval-voting = { path = "../core/approval-voting" }
|
polkadot-node-core-approval-voting = { path = "../core/approval-voting" }
|
||||||
polkadot-approval-distribution = { path = "../network/approval-distribution" }
|
polkadot-approval-distribution = { path = "../network/approval-distribution" }
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ impl TestEnvironment {
|
|||||||
let total_cpu = test_env_cpu_metrics.sum_by("substrate_tasks_polling_duration_sum");
|
let total_cpu = test_env_cpu_metrics.sum_by("substrate_tasks_polling_duration_sum");
|
||||||
|
|
||||||
usage.push(ResourceUsage {
|
usage.push(ResourceUsage {
|
||||||
resource_name: "Test environment".to_string(),
|
resource_name: "test-environment".to_string(),
|
||||||
total: total_cpu,
|
total: total_cpu,
|
||||||
per_block: total_cpu / num_blocks,
|
per_block: total_cpu / num_blocks,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,3 +26,4 @@ pub(crate) mod keyring;
|
|||||||
pub(crate) mod mock;
|
pub(crate) mod mock;
|
||||||
pub(crate) mod network;
|
pub(crate) mod network;
|
||||||
pub mod usage;
|
pub mod usage;
|
||||||
|
pub mod utils;
|
||||||
|
|||||||
@@ -82,6 +82,27 @@ impl BenchmarkUsage {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepares a json string for a graph representation
|
||||||
|
// See: https://github.com/benchmark-action/github-action-benchmark?tab=readme-ov-file#examples
|
||||||
|
pub fn to_chart_json(&self) -> color_eyre::eyre::Result<String> {
|
||||||
|
let chart = self
|
||||||
|
.network_usage
|
||||||
|
.iter()
|
||||||
|
.map(|v| ChartItem {
|
||||||
|
name: v.resource_name.clone(),
|
||||||
|
unit: "KiB".to_string(),
|
||||||
|
value: v.per_block,
|
||||||
|
})
|
||||||
|
.chain(self.cpu_usage.iter().map(|v| ChartItem {
|
||||||
|
name: v.resource_name.clone(),
|
||||||
|
unit: "seconds".to_string(),
|
||||||
|
value: v.per_block,
|
||||||
|
}))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Ok(serde_json::to_string(&chart)?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_usage(
|
fn check_usage(
|
||||||
@@ -151,3 +172,10 @@ impl ResourceUsage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ResourceUsageCheck<'a> = (&'a str, f64, f64);
|
type ResourceUsageCheck<'a> = (&'a str, f64, f64);
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ChartItem {
|
||||||
|
pub name: String,
|
||||||
|
pub unit: String,
|
||||||
|
pub value: f64,
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,61 +16,26 @@
|
|||||||
|
|
||||||
//! Test utils
|
//! Test utils
|
||||||
|
|
||||||
use crate::usage::BenchmarkUsage;
|
use std::{fs::File, io::Write};
|
||||||
use std::io::{stdout, Write};
|
|
||||||
|
|
||||||
pub struct WarmUpOptions<'a> {
|
// Saves a given string to a file
|
||||||
/// The maximum number of runs considered for warming up.
|
pub fn save_to_file(path: &str, value: String) -> color_eyre::eyre::Result<()> {
|
||||||
pub warm_up: usize,
|
let output = std::process::Command::new(env!("CARGO"))
|
||||||
/// The number of runs considered for benchmarking.
|
.arg("locate-project")
|
||||||
pub bench: usize,
|
.arg("--workspace")
|
||||||
/// The difference in CPU usage between runs considered as normal
|
.arg("--message-format=plain")
|
||||||
pub precision: f64,
|
.output()
|
||||||
/// The subsystems whose CPU usage is checked during warm-up cycles
|
.unwrap()
|
||||||
pub subsystems: &'a [&'a str],
|
.stdout;
|
||||||
|
let workspace_dir = std::path::Path::new(std::str::from_utf8(&output).unwrap().trim())
|
||||||
|
.parent()
|
||||||
|
.unwrap();
|
||||||
|
let path = workspace_dir.join(path);
|
||||||
|
if let Some(dir) = path.parent() {
|
||||||
|
std::fs::create_dir_all(dir)?;
|
||||||
}
|
}
|
||||||
|
let mut file = File::create(path)?;
|
||||||
|
file.write_all(value.as_bytes())?;
|
||||||
|
|
||||||
impl<'a> WarmUpOptions<'a> {
|
Ok(())
|
||||||
pub fn new(subsystems: &'a [&'a str]) -> Self {
|
|
||||||
Self { warm_up: 100, bench: 3, precision: 0.02, subsystems }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn warm_up_and_benchmark(
|
|
||||||
options: WarmUpOptions,
|
|
||||||
run: impl Fn() -> BenchmarkUsage,
|
|
||||||
) -> Result<BenchmarkUsage, String> {
|
|
||||||
println!("Warming up...");
|
|
||||||
let mut usages = Vec::with_capacity(options.bench);
|
|
||||||
|
|
||||||
for n in 1..=options.warm_up {
|
|
||||||
let curr = run();
|
|
||||||
if let Some(prev) = usages.last() {
|
|
||||||
let diffs = options
|
|
||||||
.subsystems
|
|
||||||
.iter()
|
|
||||||
.map(|&v| {
|
|
||||||
curr.cpu_usage_diff(prev, v)
|
|
||||||
.ok_or(format!("{} not found in benchmark {:?}", v, prev))
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<f64>, String>>()?;
|
|
||||||
if !diffs.iter().all(|&v| v < options.precision) {
|
|
||||||
usages.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
usages.push(curr);
|
|
||||||
print!("\r{}%", n * 100 / options.warm_up);
|
|
||||||
if usages.len() == options.bench {
|
|
||||||
println!("\rTook {} runs to warm up", n.saturating_sub(options.bench));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stdout().flush().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if usages.len() != options.bench {
|
|
||||||
println!("Didn't warm up after {} runs", options.warm_up);
|
|
||||||
return Err("Can't warm up".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(BenchmarkUsage::average(&usages))
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user