use crate::core::ForgeGuardError;
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchmarkResult {
pub name: String,
pub avg_ms: f64,
pub min_ms: f64,
pub max_ms: f64,
pub median_ms: f64,
pub p99_ms: f64,
pub samples: u32,
}
pub struct BenchmarkRunner {
iterations: u32,
warmup: u32,
}
impl BenchmarkRunner {
pub fn new(iterations: u32, warmup: u32) -> Self {
Self { iterations, warmup }
}
pub fn benchmark_all(&mut self) -> Result<Vec<BenchmarkResult>, ForgeGuardError> {
let mut results = Vec::new();
results.push(self.benchmark_fn("source_discovery", || {
let _ = std::fs::read_dir(".");
std::thread::sleep(Duration::from_micros(100));
}));
results.push(self.benchmark_fn("pattern_matching", || {
let _ = regex::Regex::new(r"function\s+\w+\s*\(");
std::thread::sleep(Duration::from_micros(50));
}));
results.push(self.benchmark_fn("json_serialization", || {
let data = serde_json::json!({"test": "value", "count": 100});
let _ = serde_json::to_string(&data);
}));
Ok(results)
}
pub fn benchmark_module(&mut self, module: &str) -> Result<BenchmarkResult, ForgeGuardError> {
match module {
"source_discovery" => Ok(self.benchmark_fn("source_discovery", || {
let _ = std::fs::read_dir(".");
std::thread::sleep(Duration::from_micros(100));
})),
"pattern_matching" => Ok(self.benchmark_fn("pattern_matching", || {
let _ = regex::Regex::new(r"function\s+\w+\s*\(");
std::thread::sleep(Duration::from_micros(50));
})),
"json_serialization" => Ok(self.benchmark_fn("json_serialization", || {
let data = serde_json::json!({"test": "value"});
let _ = serde_json::to_string(&data);
})),
_ => Err(ForgeGuardError::Config(format!(
"Unknown benchmark module: {}",
module
))),
}
}
fn benchmark_fn(&self, name: &str, mut f: impl FnMut()) -> BenchmarkResult {
for _ in 0..self.warmup {
f();
}
let mut times = Vec::with_capacity(self.iterations as usize);
for _ in 0..self.iterations {
let start = Instant::now();
f();
times.push(start.elapsed());
}
let mut ms_times: Vec<f64> = times.iter().map(|t| t.as_secs_f64() * 1000.0).collect();
ms_times.sort_by(|a, b| a.partial_cmp(b).unwrap());
let avg = ms_times.iter().sum::<f64>() / ms_times.len() as f64;
let min = ms_times.first().copied().unwrap_or(0.0);
let max = ms_times.last().copied().unwrap_or(0.0);
let median = ms_times.get(ms_times.len() / 2).copied().unwrap_or(0.0);
let p99_idx = (ms_times.len() as f64 * 0.99) as usize;
let p99 = ms_times
.get(p99_idx.min(ms_times.len().saturating_sub(1)))
.copied()
.unwrap_or(0.0);
BenchmarkResult {
name: name.to_string(),
avg_ms: (avg * 100.0).round() / 100.0,
min_ms: (min * 100.0).round() / 100.0,
max_ms: (max * 100.0).round() / 100.0,
median_ms: (median * 100.0).round() / 100.0,
p99_ms: (p99 * 100.0).round() / 100.0,
samples: self.iterations,
}
}
}