Skip to main content

oxirs_core/ai/
gpu_monitor.rs

1//! GPU Monitoring for AI Engine
2//!
3//! This module provides GPU utilization monitoring across different platforms:
4//! - NVIDIA GPUs via NVML (NVIDIA Management Library)
5//! - Future support for Apple Metal, AMD ROCm, etc.
6//!
7//! NOTE (COOLJAPAN Pure Rust Policy v2): the real NVML (`nvml-wrapper`) backend
8//! has been quarantined into the `oxirs-gpu-monitor` crate (`publish = false`) so
9//! that `oxirs-core`'s published `--all-features` surface stays free of the
10//! `nvml-wrapper-sys` C FFI. `GpuMonitor` below is a Pure-Rust stub with an
11//! unchanged public API (it reports "no GPU" / zeros). Binaries that need live
12//! NVIDIA telemetry should use `oxirs_gpu_monitor::NvmlGpuMonitor`, which returns
13//! the same `GpuStats` type defined here.
14
15use anyhow::Result;
16use std::sync::{Arc, Mutex, OnceLock};
17
18/// GPU monitoring statistics
19#[derive(Debug, Clone, Default)]
20pub struct GpuStats {
21    /// GPU utilization percentage (0.0-100.0)
22    pub utilization: f32,
23
24    /// Memory utilization percentage (0.0-100.0)
25    pub memory_utilization: f32,
26
27    /// Temperature in Celsius
28    pub temperature: f32,
29
30    /// Power usage in watts
31    pub power_usage: f32,
32
33    /// Available memory in MB
34    pub memory_free_mb: u64,
35
36    /// Total memory in MB
37    pub memory_total_mb: u64,
38}
39
40/// GPU monitor that provides cross-platform GPU statistics
41pub struct GpuMonitor {}
42
43static GPU_MONITOR: OnceLock<Arc<Mutex<GpuMonitor>>> = OnceLock::new();
44
45impl GpuMonitor {
46    /// Create a new GPU monitor
47    pub fn new() -> Self {
48        Self::with_device(0)
49    }
50
51    /// Create a new GPU monitor with specific device index
52    pub fn with_device(device_index: u32) -> Self {
53        let _ = device_index; // Suppress unused variable warning
54        Self {}
55    }
56
57    /// Get the global GPU monitor instance
58    pub fn global() -> Arc<Mutex<GpuMonitor>> {
59        GPU_MONITOR
60            .get_or_init(|| Arc::new(Mutex::new(GpuMonitor::new())))
61            .clone()
62    }
63
64    /// Get current GPU statistics
65    pub fn get_stats(&self) -> Result<GpuStats> {
66        // GPU monitoring not enabled (Pure-Rust stub; see module docs / oxirs-gpu-monitor)
67        Ok(GpuStats::default())
68    }
69
70    /// Get GPU utilization percentage (0.0-100.0)
71    pub fn get_utilization(&self) -> f32 {
72        self.get_stats()
73            .map(|stats| stats.utilization)
74            .unwrap_or(0.0)
75    }
76
77    /// Check if GPU is available
78    pub fn is_available(&self) -> bool {
79        false
80    }
81
82    /// Get number of available GPUs
83    pub fn device_count() -> u32 {
84        0
85    }
86}
87
88impl Default for GpuMonitor {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_gpu_monitor_creation() {
100        let monitor = GpuMonitor::new();
101        // Should not panic even if GPU is not available
102        let _ = monitor.is_available();
103    }
104
105    #[test]
106    fn test_gpu_stats() {
107        let monitor = GpuMonitor::new();
108        let stats = monitor.get_stats();
109        assert!(stats.is_ok());
110
111        if monitor.is_available() {
112            let stats = stats.expect("stats should be available");
113            assert!(stats.utilization >= 0.0 && stats.utilization <= 100.0);
114        }
115    }
116
117    #[test]
118    fn test_device_count() {
119        let _count = GpuMonitor::device_count();
120        // Device count is u32, always >= 0 (no assertion needed)
121        // Just verify the method doesn't panic
122    }
123
124    #[test]
125    fn test_global_monitor() {
126        let monitor1 = GpuMonitor::global();
127        let monitor2 = GpuMonitor::global();
128
129        // Should return the same instance
130        assert!(Arc::ptr_eq(&monitor1, &monitor2));
131    }
132}