use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ExecutionMetrics {
pub total_time_ms: f64,
pub operation_times: HashMap<String, f64>,
pub operation_count: usize,
pub peak_memory_mb: f64,
}
impl ExecutionMetrics {
pub fn new() -> Self {
Self {
total_time_ms: 0.0,
operation_times: HashMap::new(),
operation_count: 0,
peak_memory_mb: 0.0,
}
}
pub fn add_operation_time(&mut self, op_name: &str, time_ms: f64) {
*self
.operation_times
.entry(op_name.to_string())
.or_insert(0.0) += time_ms;
self.operation_count += 1;
}
pub fn set_total_time(&mut self, time_ms: f64) {
self.total_time_ms = time_ms;
}
pub fn set_peak_memory(&mut self, memory_mb: f64) {
self.peak_memory_mb = memory_mb;
}
pub fn average_time_per_op(&self) -> f64 {
if self.operation_count > 0 {
self.total_time_ms / self.operation_count as f64
} else {
0.0
}
}
pub fn slowest_operation(&self) -> Option<(&String, &f64)> {
self.operation_times
.iter()
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
}
pub fn fastest_operation(&self) -> Option<(&String, &f64)> {
self.operation_times
.iter()
.min_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
}
pub fn get_operation_time(&self, op_name: &str) -> f64 {
self.operation_times.get(op_name).copied().unwrap_or(0.0)
}
pub fn get_operation_percentage(&self, op_name: &str) -> f64 {
if self.total_time_ms > 0.0 {
(self.get_operation_time(op_name) / self.total_time_ms) * 100.0
} else {
0.0
}
}
pub fn operation_type_count(&self) -> usize {
self.operation_times.len()
}
pub fn is_empty(&self) -> bool {
self.operation_count == 0 && self.total_time_ms == 0.0
}
pub fn merge(&mut self, other: &ExecutionMetrics) {
self.total_time_ms += other.total_time_ms;
self.operation_count += other.operation_count;
self.peak_memory_mb = self.peak_memory_mb.max(other.peak_memory_mb);
for (op_name, time) in &other.operation_times {
*self.operation_times.entry(op_name.clone()).or_insert(0.0) += time;
}
}
pub fn clear(&mut self) {
self.total_time_ms = 0.0;
self.operation_times.clear();
self.operation_count = 0;
self.peak_memory_mb = 0.0;
}
pub fn generate_report(&self) -> String {
let mut report = format!(
"Execution Performance Report:\n\
Total Time: {:.2} ms\n\
Operations Executed: {}\n\
Average Time/Op: {:.2} ms\n\
Peak Memory: {:.2} MB\n\
Operation Types: {}\n\n\
Operation Breakdown:",
self.total_time_ms,
self.operation_count,
self.average_time_per_op(),
self.peak_memory_mb,
self.operation_type_count()
);
let mut sorted_ops: Vec<_> = self.operation_times.iter().collect();
sorted_ops.sort_by(|a, b| b.1.partial_cmp(a.1).unwrap_or(std::cmp::Ordering::Equal));
for (op_name, time) in sorted_ops {
let percentage = self.get_operation_percentage(op_name);
report.push_str(&format!(
"\n {}: {:.2} ms ({:.1}%)",
op_name, time, percentage
));
}
if let Some((slowest_op, slowest_time)) = self.slowest_operation() {
report.push_str(&format!(
"\n\nPerformance Insights:\n\
Slowest Operation: {} ({:.2} ms)\n",
slowest_op, slowest_time
));
}
if self.peak_memory_mb > 1000.0 {
report.push_str(&format!(
" Memory Usage: High ({:.2} MB)\n",
self.peak_memory_mb
));
}
if self.average_time_per_op() > 100.0 {
report.push_str(" Average Operation Time: High (>100ms per operation)\n");
}
report
}
pub fn generate_summary(&self) -> String {
format!(
"Performance Summary: {:.2}ms total, {} ops, {:.2}ms/op avg, {:.2}MB peak",
self.total_time_ms,
self.operation_count,
self.average_time_per_op(),
self.peak_memory_mb
)
}
pub fn to_json(&self) -> String {
let mut json = format!(
r#"{{
"total_time_ms": {},
"operation_count": {},
"peak_memory_mb": {},
"average_time_per_op": {},
"operation_times": {{"#,
self.total_time_ms,
self.operation_count,
self.peak_memory_mb,
self.average_time_per_op()
);
let op_entries: Vec<String> = self
.operation_times
.iter()
.map(|(name, time)| format!(r#" "{}": {}"#, name, time))
.collect();
json.push_str(&op_entries.join(",\n"));
json.push_str("\n }\n}");
json
}
}
impl Default for ExecutionMetrics {
fn default() -> Self {
Self::new()
}
}
pub struct ExecutionTimer {
start_time: std::time::Instant,
}
impl ExecutionTimer {
pub fn start() -> Self {
Self {
start_time: std::time::Instant::now(),
}
}
pub fn elapsed_ms(&self) -> f64 {
self.start_time.elapsed().as_secs_f64() * 1000.0
}
pub fn reset(&mut self) {
self.start_time = std::time::Instant::now();
}
}
pub struct MetricsCollector {
runs: Vec<ExecutionMetrics>,
total_metrics: ExecutionMetrics,
}
impl MetricsCollector {
pub fn new() -> Self {
Self {
runs: Vec::new(),
total_metrics: ExecutionMetrics::new(),
}
}
pub fn add_run(&mut self, metrics: ExecutionMetrics) {
self.total_metrics.merge(&metrics);
self.runs.push(metrics);
}
pub fn total_metrics(&self) -> &ExecutionMetrics {
&self.total_metrics
}
pub fn get_run(&self, run_index: usize) -> Option<&ExecutionMetrics> {
self.runs.get(run_index)
}
pub fn run_count(&self) -> usize {
self.runs.len()
}
pub fn average_execution_time(&self) -> f64 {
if self.runs.is_empty() {
0.0
} else {
self.runs.iter().map(|r| r.total_time_ms).sum::<f64>() / self.runs.len() as f64
}
}
pub fn execution_time_variance(&self) -> f64 {
if self.runs.len() <= 1 {
return 0.0;
}
let avg = self.average_execution_time();
let variance = self
.runs
.iter()
.map(|r| (r.total_time_ms - avg).powi(2))
.sum::<f64>()
/ (self.runs.len() - 1) as f64;
variance
}
pub fn fastest_execution(&self) -> Option<f64> {
self.runs
.iter()
.map(|r| r.total_time_ms)
.fold(None, |acc, time| match acc {
None => Some(time),
Some(min_time) => Some(min_time.min(time)),
})
}
pub fn slowest_execution(&self) -> Option<f64> {
self.runs
.iter()
.map(|r| r.total_time_ms)
.fold(None, |acc, time| match acc {
None => Some(time),
Some(max_time) => Some(max_time.max(time)),
})
}
pub fn clear(&mut self) {
self.runs.clear();
self.total_metrics.clear();
}
pub fn generate_statistical_report(&self) -> String {
if self.runs.is_empty() {
return "No execution runs recorded".to_string();
}
format!(
"Statistical Performance Report:\n\
Total Runs: {}\n\
Average Execution Time: {:.2} ms\n\
Fastest Execution: {:.2} ms\n\
Slowest Execution: {:.2} ms\n\
Execution Time Variance: {:.2}\n\
Standard Deviation: {:.2} ms\n\n\
{}",
self.run_count(),
self.average_execution_time(),
self.fastest_execution().unwrap_or(0.0),
self.slowest_execution().unwrap_or(0.0),
self.execution_time_variance(),
self.execution_time_variance().sqrt(),
self.total_metrics.generate_report()
)
}
}
impl Default for MetricsCollector {
fn default() -> Self {
Self::new()
}
}