use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::time::Duration;
#[derive(Debug)]
pub struct ComputeMetrics {
tasks_total: AtomicU64,
tasks_active: AtomicUsize,
total_compute_time_us: AtomicU64,
max_task_duration_us: AtomicU64,
slow_tasks: AtomicU64,
}
impl ComputeMetrics {
pub fn new() -> Self {
Self {
tasks_total: AtomicU64::new(0),
tasks_active: AtomicUsize::new(0),
total_compute_time_us: AtomicU64::new(0),
max_task_duration_us: AtomicU64::new(0),
slow_tasks: AtomicU64::new(0),
}
}
pub fn record_task_start(&self) {
self.tasks_active.fetch_add(1, Ordering::Relaxed);
}
pub fn record_task_completion(&self, duration: Duration) {
self.tasks_active.fetch_sub(1, Ordering::Relaxed);
self.tasks_total.fetch_add(1, Ordering::Relaxed);
let duration_us = duration.as_micros().min(u64::MAX as u128) as u64;
self.total_compute_time_us
.fetch_add(duration_us, Ordering::Relaxed);
let mut current_max = self.max_task_duration_us.load(Ordering::Relaxed);
while duration_us > current_max {
match self.max_task_duration_us.compare_exchange_weak(
current_max,
duration_us,
Ordering::SeqCst,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(x) => current_max = x,
}
}
if duration.as_millis() > 100 {
self.slow_tasks.fetch_add(1, Ordering::Relaxed);
}
}
pub fn tasks_total(&self) -> u64 {
self.tasks_total.load(Ordering::Relaxed)
}
pub fn tasks_active(&self) -> usize {
self.tasks_active.load(Ordering::Relaxed)
}
pub fn avg_task_duration_us(&self) -> f64 {
let total = self.tasks_total.load(Ordering::Relaxed);
if total == 0 {
return 0.0;
}
let total_time = self.total_compute_time_us.load(Ordering::Relaxed);
total_time as f64 / total as f64
}
pub fn max_task_duration_us(&self) -> u64 {
self.max_task_duration_us.load(Ordering::Relaxed)
}
pub fn slow_tasks(&self) -> u64 {
self.slow_tasks.load(Ordering::Relaxed)
}
pub fn reset(&self) {
self.tasks_total.store(0, Ordering::Relaxed);
self.tasks_active.store(0, Ordering::Relaxed);
self.total_compute_time_us.store(0, Ordering::Relaxed);
self.max_task_duration_us.store(0, Ordering::Relaxed);
self.slow_tasks.store(0, Ordering::Relaxed);
}
}
impl Default for ComputeMetrics {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for ComputeMetrics {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ComputeMetrics {{ tasks_total: {}, tasks_active: {}, avg_duration_ms: {:.2}, max_duration_ms: {:.2}, slow_tasks: {} }}",
self.tasks_total(),
self.tasks_active(),
self.avg_task_duration_us() / 1000.0,
self.max_task_duration_us() as f64 / 1000.0,
self.slow_tasks(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_recording() {
let metrics = ComputeMetrics::new();
assert_eq!(metrics.tasks_total(), 0);
assert_eq!(metrics.tasks_active(), 0);
metrics.record_task_start();
assert_eq!(metrics.tasks_active(), 1);
metrics.record_task_completion(Duration::from_millis(50));
assert_eq!(metrics.tasks_active(), 0);
assert_eq!(metrics.tasks_total(), 1);
assert_eq!(metrics.slow_tasks(), 0);
metrics.record_task_start();
metrics.record_task_completion(Duration::from_millis(150));
assert_eq!(metrics.tasks_total(), 2);
assert_eq!(metrics.slow_tasks(), 1);
}
#[test]
fn test_metrics_reset() {
let metrics = ComputeMetrics::new();
metrics.record_task_start();
metrics.record_task_completion(Duration::from_millis(50));
assert_eq!(metrics.tasks_total(), 1);
metrics.reset();
assert_eq!(metrics.tasks_total(), 0);
assert_eq!(metrics.tasks_active(), 0);
}
}