use std::num::NonZeroUsize;
use std::time::{Duration, Instant};
use serde_json::Value;
use crate::emission_plan::{CompletionPass, LoweredEmissionPlan};
use crate::{EmissionPolicy, EmissionReport, ValuesSchemaInput};
#[derive(Debug, Clone, Copy)]
pub struct BenchmarkPolicy {
pub name: &'static str,
pub policy: EmissionPolicy,
}
#[derive(Debug)]
pub struct PolicyProjectionBenchmark {
pub name: &'static str,
pub projection_times: Vec<Duration>,
pub completion_times: Vec<Duration>,
pub schema: Value,
pub emission_report: EmissionReport,
}
#[derive(Debug)]
pub struct MultiPolicyBenchmark {
pub plan_construction_times: Vec<Duration>,
pub retained_plan_bytes: Option<u64>,
pub retained_candidate_bytes: usize,
pub policies: Vec<PolicyProjectionBenchmark>,
}
#[must_use]
pub fn benchmark_policies(
input: &ValuesSchemaInput<'_>,
policies: &[BenchmarkPolicy],
runs: NonZeroUsize,
) -> MultiPolicyBenchmark {
let mut plan_construction_times = Vec::with_capacity(runs.get());
let mut retained_plan_bytes = None;
let mut retained_candidate_bytes = 0;
let mut outputs = policies
.iter()
.map(|policy| PolicyProjectionBenchmark {
name: policy.name,
projection_times: Vec::with_capacity(runs.get()),
completion_times: Vec::with_capacity(runs.get()),
schema: Value::Null,
emission_report: EmissionReport::default(),
})
.collect::<Vec<_>>();
for run in 0..runs.get() {
let resident_before = resident_memory_kib();
let started = Instant::now();
let plan = LoweredEmissionPlan::build(input);
plan_construction_times.push(started.elapsed());
if run == 0 {
retained_plan_bytes = resident_memory_kib()
.zip(resident_before)
.map(|(after, before)| after.saturating_sub(before) * 1024);
retained_candidate_bytes = plan.benchmark_retained_candidate_bytes();
}
for (policy, output) in policies.iter().zip(&mut outputs) {
let started = Instant::now();
let projected = plan.project(policy.policy);
output.projection_times.push(started.elapsed());
let started = Instant::now();
let completed = plan.complete(projected, CompletionPass::Descriptions);
output.completion_times.push(started.elapsed());
output.schema = completed.schema;
output.emission_report = completed.emission_report;
}
}
MultiPolicyBenchmark {
plan_construction_times,
retained_plan_bytes,
retained_candidate_bytes,
policies: outputs,
}
}
#[cfg(target_os = "linux")]
fn resident_memory_kib() -> Option<u64> {
let status = std::fs::read_to_string("/proc/self/status").ok()?;
let line = status.lines().find(|line| line.starts_with("VmRSS:"))?;
line.split_ascii_whitespace().nth(1)?.parse().ok()
}
#[cfg(not(target_os = "linux"))]
fn resident_memory_kib() -> Option<u64> {
None
}