cubecl-cpu 0.11.0

CPU runtime for CubeCL
use std::sync::{Mutex, PoisonError};

use cubecl_server::runtime::{DeviceUtilization, UtilizationUnavailable};
use sysinfo::System;

/// The time every core of the machine spent running, which the CPU runtime's device is. The
/// machine has one set of cores, so the process keeps one reading of them.
pub struct ProcessorTimes;

static PREVIOUS_READING: Mutex<Option<System>> = Mutex::new(None);

impl ProcessorTimes {
    /// The share of the time since the previous reading the cores spent running, averaged over
    /// every core.
    pub fn read_machine_wide() -> Result<DeviceUtilization, UtilizationUnavailable> {
        let mut previous_reading = PREVIOUS_READING
            .lock()
            .unwrap_or_else(PoisonError::into_inner);
        let Some(system) = previous_reading.as_mut() else {
            let mut system = System::new();
            system.refresh_cpu_usage();
            *previous_reading = Some(system);
            return Err(UtilizationUnavailable::NoPreviousReading);
        };
        system.refresh_cpu_usage();
        Ok(DeviceUtilization::new(system.global_cpu_usage()))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn the_first_reading_measures_nothing_and_the_next_is_a_percent() {
        assert_eq!(
            ProcessorTimes::read_machine_wide(),
            Err(UtilizationUnavailable::NoPreviousReading)
        );

        let next = ProcessorTimes::read_machine_wide().map(|utilization| utilization.busy_percent);

        assert!(
            matches!(next, Ok(busy_percent) if (0.0..=100.0).contains(&busy_percent)),
            "{next:?}"
        );
    }
}