use crate::hist::History;
use std::collections::HashMap;
use std::sync::mpsc::{Receiver, RecvTimeoutError, SyncSender, channel, sync_channel};
use std::time::Duration;
#[cfg(target_os = "macos")]
use crate::apple as source;
#[cfg(not(target_os = "macos"))]
use crate::nvidia as source;
#[derive(Default, Clone)]
pub struct Gpu {
pub index: u32,
pub name: String,
pub util: Option<u32>,
pub mem_used: u64,
pub mem_total: u64,
pub temp: Option<u32>,
pub power: Option<u32>,
pub power_limit: Option<u32>,
pub fan: Option<u32>,
pub clock_gr: Option<u32>,
pub clock_gr_max: Option<u32>,
pub clock_mem: Option<u32>,
pub pcie_tx: Option<u64>,
pub pcie_rx: Option<u64>,
pub enc: Option<u32>,
pub dec: Option<u32>,
pub pstate: Option<String>,
pub cores: Option<u32>,
pub hist_util: History,
pub hist_mem: History,
}
impl Gpu {
pub fn mem_frac(&self) -> f64 {
crate::sys::frac(self.mem_used, self.mem_total)
}
fn fresh(&self) -> Gpu {
Gpu {
index: self.index,
name: self.name.clone(),
mem_total: self.mem_total,
power_limit: self.power_limit,
clock_gr_max: self.clock_gr_max,
cores: self.cores,
..Default::default()
}
}
fn take(&mut self, mut reading: Gpu) {
reading.hist_util = std::mem::take(&mut self.hist_util);
reading.hist_mem = std::mem::take(&mut self.hist_mem);
*self = reading;
}
fn record(&mut self) {
self.hist_util.push(self.util.unwrap_or(0) as f64);
self.hist_mem.push(100.0 * self.mem_frac());
}
}
#[derive(Default, Clone)]
pub struct GpuUse {
pub gpus: Vec<u32>,
pub mem: Option<u64>,
pub sm: Option<u32>,
}
type Sample = (Vec<Gpu>, HashMap<u32, GpuUse>);
fn read(source: &mut source::Source, gpus: &[Gpu]) -> Sample {
let mut sample: Sample = (gpus.iter().map(Gpu::fresh).collect(), HashMap::new());
source.sample(&mut sample.0, &mut sample.1);
sample
}
struct Reader {
ask: SyncSender<()>,
samples: Receiver<Sample>,
}
const WAIT: Duration = Duration::from_millis(40);
impl Reader {
fn spawn(mut source: source::Source, gpus: Vec<Gpu>) -> Self {
let (ask, asked) = sync_channel(1);
let (tx, samples) = channel();
std::thread::spawn(move || {
while asked.recv().is_ok() {
if tx.send(read(&mut source, &gpus)).is_err() {
break;
}
}
});
Reader { ask, samples }
}
}
pub struct Gpus {
reader: Option<Reader>,
pub info: String,
pub gpus: Vec<Gpu>,
pub procs: HashMap<u32, GpuUse>,
}
impl Gpus {
pub fn new() -> Self {
match source::Source::open() {
Ok((mut source, info, gpus)) => {
let first = read(&mut source, &gpus);
let mut g = Gpus {
reader: Some(Reader::spawn(source, gpus.clone())),
info,
gpus,
procs: HashMap::new(),
};
g.take(first);
g
}
Err(info) => Gpus {
reader: None,
info,
gpus: Vec::new(),
procs: HashMap::new(),
},
}
}
pub fn per_process(&self) -> bool {
source::PER_PROCESS && !self.gpus.is_empty()
}
fn take(&mut self, (gpus, procs): Sample) {
for (g, r) in self.gpus.iter_mut().zip(gpus) {
g.take(r);
g.record();
}
self.procs = procs;
}
pub fn sample(&mut self) {
let Some(r) = &self.reader else { return };
let mut newest = r.samples.try_iter().last();
let mut stopped = false;
if r.ask.try_send(()).is_ok() {
match r.samples.recv_timeout(WAIT) {
Ok(s) => newest = Some(s),
Err(RecvTimeoutError::Timeout) => {}
Err(RecvTimeoutError::Disconnected) => stopped = true,
}
}
if stopped {
self.stop();
} else if let Some(s) = newest {
self.take(s);
} else {
self.gpus.iter_mut().for_each(Gpu::record);
}
}
fn stop(&mut self) {
self.reader = None;
self.info += " (GPUs no longer read)";
let unknown = self.gpus.iter().map(Gpu::fresh).collect();
self.take((unknown, HashMap::new()));
}
}