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}