///|
/// Deterministic benchmark configuration shared by local and CI runs.
pub(all) struct BenchmarkConfig {
recording_length : Int
repetitions : Int
baseline_rr : Double
sample_rate_hz : Double
} derive(FromJson, ToJson, Debug, Eq)
///|
/// Aggregate benchmark output that can be copied into a report.
pub(all) struct BenchmarkSummary {
samples : Int
repetitions : Int
feature_count : Int
mean_rr : Double
rmssd : Double
total_power : Double
quality_ratio : Double
} derive(FromJson, ToJson, Debug, Eq)
///|
/// The default workload is large enough to exercise every major layer.
pub fn BenchmarkConfig::default() -> BenchmarkConfig {
{
recording_length: 256,
repetitions: 10,
baseline_rr: 800.0,
sample_rate_hz: 4.0,
}
}
///|
/// Generate the deterministic workload for a benchmark run.
pub fn benchmark_input(config : BenchmarkConfig) -> Array[Double] {
let base = generate_resting_fixture(
config.recording_length,
config.baseline_rr,
)
repeat_fixture(base, config.repetitions)
}
///|
/// Run the full analysis workload once.
pub fn run_benchmark_once(config : BenchmarkConfig) -> BenchmarkSummary {
let input = benchmark_input(config)
let defaults = AnalysisOptions::default()
let options : AnalysisOptions = {
cleaning_method: defaults.cleaning_method,
sample_rate_hz: config.sample_rate_hz,
remove_trend: defaults.remove_trend,
window_function: defaults.window_function,
segment_size: defaults.segment_size,
segment_hop: defaults.segment_hop,
nonlinear_enabled: defaults.nonlinear_enabled,
}
let report = analyze_rr(input, HrvConfig::default(), options)
{
samples: input.length(),
repetitions: config.repetitions,
feature_count: report.feature_vector.length(),
mean_rr: report.metrics.mean_rr,
rmssd: report.metrics.rmssd,
total_power: report.frequency.total_power,
quality_ratio: report.quality.clean_ratio,
}
}
///|
/// Run repeated analyses and average stable numeric fields.
pub fn run_benchmark(config : BenchmarkConfig) -> BenchmarkSummary {
let repetitions = if config.repetitions < 1 { 1 } else { config.repetitions }
let one_config : BenchmarkConfig = {
recording_length: config.recording_length,
repetitions: 1,
baseline_rr: config.baseline_rr,
sample_rate_hz: config.sample_rate_hz,
}
let first = run_benchmark_once(one_config)
let mut mean_rr = 0.0
let mut rmssd = 0.0
let mut power = 0.0
for _ in 0.. String {
to_csv([
[
"samples", "repetitions", "feature_count", "mean_rr", "rmssd", "total_power",
"quality_ratio",
],
[
summary.samples.to_string(),
summary.repetitions.to_string(),
summary.feature_count.to_string(),
summary.mean_rr.to_string(),
summary.rmssd.to_string(),
summary.total_power.to_string(),
summary.quality_ratio.to_string(),
],
])
}
///|
/// Return whether a benchmark summary has usable output.
pub fn benchmark_is_valid(summary : BenchmarkSummary) -> Bool {
summary.samples > 0 &&
summary.feature_count > 0 &&
summary.quality_ratio >= 0.0 &&
summary.quality_ratio <= 1.0
}