forge-guard 0.3.6

Pre-deployment smart contract auditing framework for Foundry
Documentation
//! Benchmark runner — measures performance of audit components.

use crate::core::ForgeGuardError;
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};

/// Results of a single benchmark run.
#[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,
}

/// Runner for performance benchmarks.
pub struct BenchmarkRunner {
    iterations: u32,
    warmup: u32,
}

impl BenchmarkRunner {
    /// Create a new benchmark runner.
    pub fn new(iterations: u32, warmup: u32) -> Self {
        Self { iterations, warmup }
    }

    /// Run all available benchmarks.
    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)
    }

    /// Benchmark a specific module.
    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
            ))),
        }
    }

    /// Benchmark a closure, collecting timing statistics.
    fn benchmark_fn(&self, name: &str, mut f: impl FnMut()) -> BenchmarkResult {
        // Warmup
        for _ in 0..self.warmup {
            f();
        }

        // Measured runs
        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,
        }
    }
}