Skip to main content

volta/gpu/
system_monitor.rs

1//! Real-time system monitoring for GPU performance profiling
2//!
3//! Provides detailed performance tracking and profiling capabilities for
4//! debugging GPU operations and understanding performance characteristics.
5
6use std::sync::Mutex;
7use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
8use std::time::Instant;
9
10/// Real-time system monitor for GPU performance profiling
11///
12/// Tracks performance metrics over time with checkpoint-based profiling.
13/// Useful for debugging GPU operations and identifying performance bottlenecks.
14///
15/// # Example
16///
17/// ```ignore
18/// use volta::gpu::system_monitor::SystemMonitor;
19///
20/// let monitor = SystemMonitor::new();
21///
22/// monitor.checkpoint("setup_start");
23/// // ... GPU operations ...
24/// monitor.checkpoint("setup_complete");
25///
26/// // ... more operations ...
27///
28/// let stats = monitor.stats();
29/// println!("Ops/sec: {:.2}", stats.ops_per_sec);
30/// ```
31pub struct SystemMonitor {
32    start_time: Instant,
33    peak_memory_mb: AtomicUsize,
34    operation_count: AtomicU64,
35    sync_count: AtomicU64,
36    last_checkpoint_ops: AtomicU64,
37    last_checkpoint_time: Mutex<Instant>,
38}
39
40impl SystemMonitor {
41    /// Create a new system monitor
42    #[must_use]
43    pub fn new() -> Self {
44        Self {
45            start_time: Instant::now(),
46            peak_memory_mb: AtomicUsize::new(0),
47            operation_count: AtomicU64::new(0),
48            sync_count: AtomicU64::new(0),
49            last_checkpoint_ops: AtomicU64::new(0),
50            last_checkpoint_time: Mutex::new(Instant::now()),
51        }
52    }
53
54    /// Record a checkpoint with a descriptive label
55    ///
56    /// Prints performance metrics including memory usage, operation counts,
57    /// operations per second, sync count, and pending GPU operations.
58    ///
59    /// # Arguments
60    /// * `label` - Descriptive name for this checkpoint
61    pub fn checkpoint(&self, label: &str) {
62        let current_memory_mb = crate::gpu::monitor::get_process_memory_mb();
63        let _peak = self
64            .peak_memory_mb
65            .fetch_max(current_memory_mb, Ordering::Relaxed);
66        let ops = self.operation_count.load(Ordering::Relaxed);
67        let syncs = self.sync_count.load(Ordering::Relaxed);
68        let elapsed = self.start_time.elapsed().as_secs_f64();
69        let pending = crate::gpu::gpu_pending_count();
70
71        // Calculate delta since last checkpoint
72        let last_ops = self.last_checkpoint_ops.swap(ops, Ordering::Relaxed);
73        let ops_delta = ops - last_ops;
74
75        let mut last_time = self.last_checkpoint_time.lock().unwrap();
76        let time_delta = last_time.elapsed().as_secs_f64();
77        *last_time = Instant::now();
78
79        let ops_per_sec = if time_delta > 0.0 {
80            ops_delta as f64 / time_delta
81        } else {
82            0.0
83        };
84
85        println!(
86            "[{label}] Mem: {current_memory_mb}MB, Ops: {ops} (+{ops_delta}, {ops_per_sec:.0}/s), Syncs: {syncs}, Pending: {pending}, Time: {elapsed:.2}s"
87        );
88    }
89
90    /// Record a GPU operation
91    ///
92    /// Increments the operation counter. Call this after each GPU operation
93    /// to track total workload.
94    pub fn record_operation(&self) {
95        self.operation_count.fetch_add(1, Ordering::Relaxed);
96    }
97
98    /// Record a GPU sync
99    ///
100    /// Increments the sync counter. Call this after each `gpu_sync()` call
101    /// to track synchronization frequency.
102    pub fn record_sync(&self) {
103        self.sync_count.fetch_add(1, Ordering::Relaxed);
104    }
105
106    /// Get current performance statistics
107    ///
108    /// Returns a snapshot of performance metrics including elapsed time,
109    /// peak memory usage, operation counts, and throughput.
110    pub fn stats(&self) -> MonitorStats {
111        let elapsed = self.start_time.elapsed().as_secs_f64();
112        let ops = self.operation_count.load(Ordering::Relaxed);
113
114        MonitorStats {
115            elapsed_secs: elapsed,
116            peak_memory_mb: self.peak_memory_mb.load(Ordering::Relaxed),
117            operation_count: ops,
118            sync_count: self.sync_count.load(Ordering::Relaxed),
119            ops_per_sec: if elapsed > 0.0 {
120                ops as f64 / elapsed
121            } else {
122                0.0
123            },
124        }
125    }
126
127    /// Reset all counters and timers
128    ///
129    /// Useful for starting fresh profiling sessions without creating
130    /// a new monitor instance.
131    pub fn reset(&self) {
132        self.peak_memory_mb.store(0, Ordering::Relaxed);
133        self.operation_count.store(0, Ordering::Relaxed);
134        self.sync_count.store(0, Ordering::Relaxed);
135        self.last_checkpoint_ops.store(0, Ordering::Relaxed);
136        *self.last_checkpoint_time.lock().unwrap() = Instant::now();
137    }
138}
139
140impl Default for SystemMonitor {
141    fn default() -> Self {
142        Self::new()
143    }
144}
145
146/// Snapshot of system monitor performance statistics
147#[derive(Debug, Clone)]
148pub struct MonitorStats {
149    /// Total elapsed time in seconds
150    pub elapsed_secs: f64,
151    /// Peak memory usage in megabytes
152    pub peak_memory_mb: usize,
153    /// Total GPU operations recorded
154    pub operation_count: u64,
155    /// Total GPU sync operations
156    pub sync_count: u64,
157    /// Average operations per second
158    pub ops_per_sec: f64,
159}
160
161impl std::fmt::Display for MonitorStats {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        write!(
164            f,
165            "MonitorStats {{ elapsed: {:.2}s, peak_mem: {}MB, ops: {}, syncs: {}, throughput: {:.0} ops/s }}",
166            self.elapsed_secs,
167            self.peak_memory_mb,
168            self.operation_count,
169            self.sync_count,
170            self.ops_per_sec
171        )
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178
179    #[test]
180    fn test_monitor_creation() {
181        let monitor = SystemMonitor::new();
182        let stats = monitor.stats();
183        assert_eq!(stats.operation_count, 0);
184        assert_eq!(stats.sync_count, 0);
185    }
186
187    #[test]
188    fn test_record_operations() {
189        let monitor = SystemMonitor::new();
190
191        monitor.record_operation();
192        monitor.record_operation();
193        monitor.record_operation();
194
195        let stats = monitor.stats();
196        assert_eq!(stats.operation_count, 3);
197    }
198
199    #[test]
200    fn test_record_syncs() {
201        let monitor = SystemMonitor::new();
202
203        monitor.record_sync();
204        monitor.record_sync();
205
206        let stats = monitor.stats();
207        assert_eq!(stats.sync_count, 2);
208    }
209
210    #[test]
211    fn test_reset() {
212        let monitor = SystemMonitor::new();
213
214        monitor.record_operation();
215        monitor.record_sync();
216        monitor.reset();
217
218        let stats = monitor.stats();
219        assert_eq!(stats.operation_count, 0);
220        assert_eq!(stats.sync_count, 0);
221    }
222
223    #[test]
224    fn test_checkpoint_does_not_panic() {
225        let monitor = SystemMonitor::new();
226        monitor.checkpoint("test");
227        // Should complete without panic
228    }
229
230    #[test]
231    fn test_stats_display() {
232        let monitor = SystemMonitor::new();
233        monitor.record_operation();
234        let stats = monitor.stats();
235        let display = format!("{}", stats);
236        assert!(display.contains("ops: 1"));
237    }
238}