use std::collections::HashMap;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct PerformanceMeasurement {
pub operation: String,
pub duration: Duration,
pub memory_before: usize,
pub memory_after: usize,
pub peak_memory: usize,
pub metrics: HashMap<String, f64>,
}
impl PerformanceMeasurement {
pub fn new(operation: String) -> Self {
Self {
operation,
duration: Duration::default(),
memory_before: 0,
memory_after: 0,
peak_memory: 0,
metrics: HashMap::new(),
}
}
pub fn add_metric(&mut self, name: String, value: f64) {
self.metrics.insert(name, value);
}
pub fn memory_delta(&self) -> i64 {
self.memory_after as i64 - self.memory_before as i64
}
pub fn memory_efficiency(&self) -> f64 {
if self.peak_memory == 0 {
1.0
} else {
self.memory_after as f64 / self.peak_memory as f64
}
}
pub fn ops_per_second(&self) -> Option<f64> {
self.metrics.get("flops").map(|&flops| {
flops / self.duration.as_secs_f64()
})
}
}
#[derive(Debug, Clone)]
pub struct BenchmarkConfig {
pub warmup_iterations: usize,
pub measured_iterations: usize,
pub collect_memory: bool,
pub gc_between_iterations: bool,
pub max_iteration_time: Duration,
}
impl Default for BenchmarkConfig {
fn default() -> Self {
Self {
warmup_iterations: 3,
measured_iterations: 10,
collect_memory: true,
gc_between_iterations: true,
max_iteration_time: Duration::from_secs(30),
}
}
}
impl BenchmarkConfig {
pub fn fast() -> Self {
Self {
warmup_iterations: 1,
measured_iterations: 3,
collect_memory: false,
gc_between_iterations: false,
max_iteration_time: Duration::from_secs(5),
}
}
pub fn comprehensive() -> Self {
Self {
warmup_iterations: 10,
measured_iterations: 50,
collect_memory: true,
gc_between_iterations: true,
max_iteration_time: Duration::from_secs(120),
}
}
pub fn memory_focused() -> Self {
Self {
warmup_iterations: 2,
measured_iterations: 5,
collect_memory: true,
gc_between_iterations: true,
max_iteration_time: Duration::from_secs(60),
}
}
pub fn estimated_total_time(&self) -> Duration {
let total_iterations = self.warmup_iterations + self.measured_iterations;
self.max_iteration_time * total_iterations as u32
}
}
pub mod memory {
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, Default)]
pub struct MemoryTracker {
current_usage: Arc<Mutex<usize>>,
peak_usage: Arc<Mutex<usize>>,
}
impl MemoryTracker {
pub fn new() -> Self {
Self::default()
}
pub fn record_usage(&self, usage: usize) {
*self.current_usage.lock().expect("lock should not be poisoned") = usage;
let mut peak = self.peak_usage.lock().expect("lock should not be poisoned");
if usage > *peak {
*peak = usage;
}
}
pub fn current_usage(&self) -> usize {
*self.current_usage.lock().expect("lock should not be poisoned")
}
pub fn peak_usage(&self) -> usize {
*self.peak_usage.lock().expect("lock should not be poisoned")
}
pub fn reset(&self) {
*self.current_usage.lock().expect("lock should not be poisoned") = 0;
*self.peak_usage.lock().expect("lock should not be poisoned") = 0;
}
}
}
pub mod timing {
use std::time::{Duration, Instant};
pub struct Timer {
start: Instant,
}
impl Timer {
pub fn start() -> Self {
Self {
start: Instant::now(),
}
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn stop(self) -> Duration {
self.elapsed()
}
}
pub fn measure_time<F, R>(f: F) -> (R, Duration)
where
F: FnOnce() -> R,
{
let start = Instant::now();
let result = f();
let duration = start.elapsed();
(result, duration)
}
pub async fn measure_time_async<F, Fut, R>(f: F) -> (R, Duration)
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = R>,
{
let start = Instant::now();
let result = f().await;
let duration = start.elapsed();
(result, duration)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_performance_measurement_creation() {
let measurement = PerformanceMeasurement::new("test_op".to_string());
assert_eq!(measurement.operation, "test_op");
assert_eq!(measurement.duration, Duration::default());
assert!(measurement.metrics.is_empty());
}
#[test]
fn test_performance_measurement_metrics() {
let mut measurement = PerformanceMeasurement::new("test".to_string());
measurement.add_metric("flops".to_string(), 1000.0);
measurement.duration = Duration::from_secs(1);
assert_eq!(measurement.metrics.get("flops"), Some(&1000.0));
assert_eq!(measurement.ops_per_second(), Some(1000.0));
}
#[test]
fn test_performance_measurement_memory() {
let measurement = PerformanceMeasurement {
operation: "test".to_string(),
duration: Duration::default(),
memory_before: 1000,
memory_after: 1500,
peak_memory: 2000,
metrics: HashMap::new(),
};
assert_eq!(measurement.memory_delta(), 500);
assert_eq!(measurement.memory_efficiency(), 0.75);
}
#[test]
fn test_benchmark_config_defaults() {
let config = BenchmarkConfig::default();
assert_eq!(config.warmup_iterations, 3);
assert_eq!(config.measured_iterations, 10);
assert!(config.collect_memory);
assert!(config.gc_between_iterations);
}
#[test]
fn test_benchmark_config_presets() {
let fast = BenchmarkConfig::fast();
assert_eq!(fast.warmup_iterations, 1);
assert_eq!(fast.measured_iterations, 3);
assert!(!fast.collect_memory);
let comprehensive = BenchmarkConfig::comprehensive();
assert_eq!(comprehensive.warmup_iterations, 10);
assert_eq!(comprehensive.measured_iterations, 50);
assert!(comprehensive.collect_memory);
let memory_focused = BenchmarkConfig::memory_focused();
assert!(memory_focused.collect_memory);
assert!(memory_focused.gc_between_iterations);
}
#[test]
fn test_memory_tracker() {
let tracker = memory::MemoryTracker::new();
tracker.record_usage(1000);
assert_eq!(tracker.current_usage(), 1000);
assert_eq!(tracker.peak_usage(), 1000);
tracker.record_usage(1500);
assert_eq!(tracker.current_usage(), 1500);
assert_eq!(tracker.peak_usage(), 1500);
tracker.record_usage(1200);
assert_eq!(tracker.current_usage(), 1200);
assert_eq!(tracker.peak_usage(), 1500);
tracker.reset();
assert_eq!(tracker.current_usage(), 0);
assert_eq!(tracker.peak_usage(), 0);
}
#[test]
fn test_timer() {
use std::thread;
let timer = timing::Timer::start();
thread::sleep(Duration::from_millis(10));
let elapsed = timer.stop();
assert!(elapsed >= Duration::from_millis(10));
assert!(elapsed <= Duration::from_millis(50)); }
#[test]
fn test_measure_time() {
let (result, duration) = timing::measure_time(|| {
std::thread::sleep(Duration::from_millis(10));
42
});
assert_eq!(result, 42);
assert!(duration >= Duration::from_millis(10));
assert!(duration <= Duration::from_millis(50)); }
}