mod amdgpu;
mod apple;
mod nvidia_smi;
mod nvml;
use anyhow::{Result, anyhow, bail};
use crate::config::BackendChoice;
use crate::model::{GpuInfo, GpuSample};
pub trait GpuBackend: Send {
fn label(&self) -> &str;
fn devices(&self) -> &[GpuInfo];
fn sample(&mut self) -> Result<Vec<GpuSample>>;
}
pub fn create_backend(kind: BackendChoice) -> Result<Box<dyn GpuBackend>> {
match kind {
BackendChoice::Auto => create_auto_backend(),
BackendChoice::Nvml => Ok(Box::new(nvml::NvmlBackend::new()?)),
BackendChoice::NvidiaSmi => Ok(Box::new(nvidia_smi::NvidiaSmiBackend::new()?)),
BackendChoice::Amdgpu => Ok(Box::new(amdgpu::AmdGpuBackend::new()?)),
BackendChoice::Apple => Ok(Box::new(apple::AppleBackend::new()?)),
}
}
fn create_auto_backend() -> Result<Box<dyn GpuBackend>> {
if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
return apple::AppleBackend::new()
.map(|backend| Box::new(backend) as Box<dyn GpuBackend>)
.map_err(|error| {
anyhow!("no real GPU metrics backend is available on Apple Silicon: {error:#}")
});
}
let mut errors = Vec::new();
match nvml::NvmlBackend::new() {
Ok(backend) => return Ok(Box::new(backend)),
Err(error) => errors.push(format!("NVML: {error:#}")),
}
match nvidia_smi::NvidiaSmiBackend::new() {
Ok(backend) => return Ok(Box::new(backend)),
Err(error) => errors.push(format!("nvidia-smi: {error:#}")),
}
match amdgpu::AmdGpuBackend::new() {
Ok(backend) => return Ok(Box::new(backend)),
Err(error) => errors.push(format!("AMDGPU sysfs: {error:#}")),
}
match apple::AppleBackend::new() {
Ok(backend) => return Ok(Box::new(backend)),
Err(error) => errors.push(format!("Apple Silicon: {error:#}")),
}
Err(anyhow!(
"no real GPU metrics backend is available; tried {}",
errors.join("; ")
))
}
fn require_devices(devices: &[GpuInfo], backend: &str) -> Result<()> {
if devices.is_empty() {
bail!("{backend} reported no GPUs");
}
Ok(())
}