use std::path::PathBuf;
use super::governor::CpuGovernor;
use super::info::CpuFrequencyInfo;
use super::reading::FrequencyReading;
use super::variance::FrequencyVariance;
#[derive(Debug)]
pub struct FrequencyController {
pub(crate) sysfs_path: PathBuf,
cpu_count: usize,
pub(crate) mock_mode: bool,
mock_frequency: u64,
mock_governor: CpuGovernor,
}
impl Default for FrequencyController {
fn default() -> Self {
Self::new()
}
}
impl FrequencyController {
pub fn new() -> Self {
let cpu_count = super::num_cpus();
Self {
sysfs_path: PathBuf::from("/sys/devices/system/cpu"),
cpu_count,
mock_mode: false,
mock_frequency: 3_000_000, mock_governor: CpuGovernor::Performance,
}
}
pub fn with_mock(mut self, frequency_khz: u64, governor: CpuGovernor) -> Self {
self.mock_mode = true;
self.mock_frequency = frequency_khz;
self.mock_governor = governor;
self
}
pub fn cpu_count(&self) -> usize {
self.cpu_count
}
pub fn read_cpu_frequency(&self, cpu_id: usize) -> Option<CpuFrequencyInfo> {
Some(if self.mock_mode {
CpuFrequencyInfo::mock(cpu_id, self.mock_frequency, self.mock_governor)
} else {
CpuFrequencyInfo::from_sysfs(cpu_id, &self.sysfs_path)
})
}
pub fn read_all_frequencies(&self) -> FrequencyReading {
let timestamp_ns = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0);
let cpus: Vec<CpuFrequencyInfo> = (0..self.cpu_count)
.filter_map(|id| self.read_cpu_frequency(id))
.collect();
FrequencyReading { cpus, timestamp_ns }
}
pub fn detect_governor(&self) -> CpuGovernor {
self.read_all_frequencies().common_governor()
}
pub fn can_control(&self) -> bool {
if self.mock_mode {
return true;
}
let reading = self.read_all_frequencies();
if reading.cpus.is_empty() {
return false;
}
let govs = &reading.cpus[0].available_governors;
govs.contains(&CpuGovernor::Userspace) || govs.contains(&CpuGovernor::Performance)
}
pub fn measure_variance(&self, samples: usize, interval_ms: u64) -> FrequencyVariance {
let mut readings = Vec::with_capacity(samples);
for _ in 0..samples {
readings.push(self.read_all_frequencies().average_mhz());
std::thread::sleep(std::time::Duration::from_millis(interval_ms));
}
FrequencyVariance::from_samples(&readings)
}
}