forge_guard/benchmark/
mod.rs1use crate::core::ForgeGuardError;
4use serde::{Deserialize, Serialize};
5use std::time::{Duration, Instant};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct BenchmarkResult {
10 pub name: String,
11 pub avg_ms: f64,
12 pub min_ms: f64,
13 pub max_ms: f64,
14 pub median_ms: f64,
15 pub p99_ms: f64,
16 pub samples: u32,
17}
18
19pub struct BenchmarkRunner {
21 iterations: u32,
22 warmup: u32,
23}
24
25impl BenchmarkRunner {
26 pub fn new(iterations: u32, warmup: u32) -> Self {
28 Self { iterations, warmup }
29 }
30
31 pub fn benchmark_all(&mut self) -> Result<Vec<BenchmarkResult>, ForgeGuardError> {
33 let mut results = Vec::new();
34
35 results.push(self.benchmark_fn("source_discovery", || {
36 let _ = std::fs::read_dir(".");
37 std::thread::sleep(Duration::from_micros(100));
38 }));
39
40 results.push(self.benchmark_fn("pattern_matching", || {
41 let _ = regex::Regex::new(r"function\s+\w+\s*\(");
42 std::thread::sleep(Duration::from_micros(50));
43 }));
44
45 results.push(self.benchmark_fn("json_serialization", || {
46 let data = serde_json::json!({"test": "value", "count": 100});
47 let _ = serde_json::to_string(&data);
48 }));
49
50 Ok(results)
51 }
52
53 pub fn benchmark_module(&mut self, module: &str) -> Result<BenchmarkResult, ForgeGuardError> {
55 match module {
56 "source_discovery" => Ok(self.benchmark_fn("source_discovery", || {
57 let _ = std::fs::read_dir(".");
58 std::thread::sleep(Duration::from_micros(100));
59 })),
60 "pattern_matching" => Ok(self.benchmark_fn("pattern_matching", || {
61 let _ = regex::Regex::new(r"function\s+\w+\s*\(");
62 std::thread::sleep(Duration::from_micros(50));
63 })),
64 "json_serialization" => Ok(self.benchmark_fn("json_serialization", || {
65 let data = serde_json::json!({"test": "value"});
66 let _ = serde_json::to_string(&data);
67 })),
68 _ => Err(ForgeGuardError::Config(format!(
69 "Unknown benchmark module: {}",
70 module
71 ))),
72 }
73 }
74
75 fn benchmark_fn(&self, name: &str, mut f: impl FnMut()) -> BenchmarkResult {
77 for _ in 0..self.warmup {
79 f();
80 }
81
82 let mut times = Vec::with_capacity(self.iterations as usize);
84 for _ in 0..self.iterations {
85 let start = Instant::now();
86 f();
87 times.push(start.elapsed());
88 }
89
90 let mut ms_times: Vec<f64> = times.iter().map(|t| t.as_secs_f64() * 1000.0).collect();
91 ms_times.sort_by(|a, b| a.partial_cmp(b).unwrap());
92
93 let avg = ms_times.iter().sum::<f64>() / ms_times.len() as f64;
94 let min = ms_times.first().copied().unwrap_or(0.0);
95 let max = ms_times.last().copied().unwrap_or(0.0);
96 let median = ms_times.get(ms_times.len() / 2).copied().unwrap_or(0.0);
97 let p99_idx = (ms_times.len() as f64 * 0.99) as usize;
98 let p99 = ms_times
99 .get(p99_idx.min(ms_times.len().saturating_sub(1)))
100 .copied()
101 .unwrap_or(0.0);
102
103 BenchmarkResult {
104 name: name.to_string(),
105 avg_ms: (avg * 100.0).round() / 100.0,
106 min_ms: (min * 100.0).round() / 100.0,
107 max_ms: (max * 100.0).round() / 100.0,
108 median_ms: (median * 100.0).round() / 100.0,
109 p99_ms: (p99 * 100.0).round() / 100.0,
110 samples: self.iterations,
111 }
112 }
113}