#[cfg(feature = "gpu")]
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
#[cfg(feature = "mac")]
mod mac;
#[cfg(all(feature = "cuda", not(feature = "mac")))]
mod nvml;
#[cfg(feature = "mac")]
use mac::Sampler;
#[cfg(all(feature = "cuda", not(feature = "mac")))]
use nvml::Sampler;
#[cfg(feature = "gpu")]
static GPU_UTIL_BITS: AtomicU32 = AtomicU32::new(0);
#[cfg(feature = "gpu")]
static GPU_SM_BITS: AtomicU32 = AtomicU32::new(0);
#[cfg(feature = "gpu")]
static PCIE_BPS: AtomicU64 = AtomicU64::new(0);
#[cfg(feature = "gpu")]
static GPU_POWER_BITS: AtomicU32 = AtomicU32::new(0);
#[cfg(feature = "gpu")]
static GPU_MEM_BYTES: AtomicU64 = AtomicU64::new(0);
#[cfg(feature = "gpu")]
static GPU_TEMP_BITS: AtomicU32 = AtomicU32::new(0);
#[cfg(feature = "gpu")]
static GPU_CLOCK_BITS: AtomicU32 = AtomicU32::new(0);
#[cfg(feature = "gpu")]
#[derive(Clone, Copy)]
struct GpuReading {
util: f32,
sm: f32,
pcie_bps: u64,
power_w: f32,
mem_bytes: u64,
temp_c: f32,
clock_mhz: f32,
}
#[cfg(feature = "gpu")]
pub(crate) fn ensure_collector() {
use std::sync::Once;
use std::time::Duration;
static START: Once = Once::new();
START.call_once(|| {
const POLL: Duration = Duration::from_millis(250);
std::thread::Builder::new()
.name("vor-gpu".into())
.spawn(|| {
let Some(mut sampler) = Sampler::new() else {
return;
};
loop {
std::thread::sleep(POLL);
store(sampler.poll());
}
})
.unwrap();
});
}
#[cfg(not(feature = "gpu"))]
pub(crate) const fn ensure_collector() {}
#[cfg(feature = "gpu")]
fn store(reading: GpuReading) {
let GpuReading {
util,
sm,
pcie_bps,
power_w,
mem_bytes,
temp_c,
clock_mhz,
} = reading;
GPU_UTIL_BITS.store(util.to_bits(), Ordering::Relaxed);
GPU_SM_BITS.store(sm.to_bits(), Ordering::Relaxed);
PCIE_BPS.store(pcie_bps, Ordering::Relaxed);
GPU_POWER_BITS.store(power_w.to_bits(), Ordering::Relaxed);
GPU_MEM_BYTES.store(mem_bytes, Ordering::Relaxed);
GPU_TEMP_BITS.store(temp_c.to_bits(), Ordering::Relaxed);
GPU_CLOCK_BITS.store(clock_mhz.to_bits(), Ordering::Relaxed);
}
#[cfg(feature = "gpu")]
pub(crate) fn read_gpu_util() -> f32 {
f32::from_bits(GPU_UTIL_BITS.load(Ordering::Relaxed))
}
#[cfg(feature = "mac")]
pub(crate) fn read_gpu_sm() -> f32 {
f32::from_bits(GPU_SM_BITS.load(Ordering::Relaxed))
}
#[cfg(feature = "cuda")]
pub(crate) fn read_pcie_bps() -> u64 {
PCIE_BPS.load(Ordering::Relaxed)
}
#[cfg(feature = "gpu")]
pub(crate) fn read_gpu_power_w() -> f32 {
f32::from_bits(GPU_POWER_BITS.load(Ordering::Relaxed))
}
#[cfg(feature = "gpu")]
pub(crate) fn read_gpu_mem_bytes() -> u64 {
GPU_MEM_BYTES.load(Ordering::Relaxed)
}
#[cfg(feature = "cuda")]
pub(crate) fn read_gpu_temp_c() -> f32 {
f32::from_bits(GPU_TEMP_BITS.load(Ordering::Relaxed))
}
#[cfg(feature = "cuda")]
pub(crate) fn read_gpu_clock_mhz() -> f32 {
f32::from_bits(GPU_CLOCK_BITS.load(Ordering::Relaxed))
}
#[cfg(all(test, feature = "mac"))]
mod tests {
#[test]
fn poll_yields_sane_readings() {
let mut sampler = super::mac::Sampler::new().unwrap();
std::thread::sleep(std::time::Duration::from_millis(300));
let r = sampler.poll();
assert!((0.0..=100.0).contains(&r.util));
assert!((0.0..=100.0).contains(&r.sm));
assert!(r.power_w.is_finite() && r.power_w >= 0.0);
}
}
#[cfg(all(test, feature = "cuda", not(feature = "mac")))]
mod tests {
#[test]
fn poll_yields_sane_readings() {
let mut sampler = super::nvml::Sampler::new().unwrap();
std::thread::sleep(std::time::Duration::from_millis(300));
let r = sampler.poll();
assert!((0.0..=100.0).contains(&r.util));
assert!(r.power_w.is_finite() && r.power_w >= 0.0);
}
}