#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Load {
pub avg1_milli: u32,
pub cores: u32,
}
impl Load {
pub fn factor_milli(self, cap: u64) -> u32 {
let cores = u64::from(self.cores.max(1));
let raw = u64::from(self.avg1_milli) / cores;
let capped = raw.clamp(1000, cap.max(1).saturating_mul(1000));
u32::try_from(capped).unwrap_or(u32::MAX)
}
pub fn avg1_text(self) -> String {
format!(
"{}.{}",
self.avg1_milli / 1000,
(self.avg1_milli % 1000) / 100
)
}
}
pub fn factor_text(milli: u32) -> String {
format!("×{}.{}", milli / 1000, (milli % 1000) / 100)
}
pub fn scaled_budget(idle: u64, load: Load, cap: u64, ceiling: Option<u64>) -> u64 {
let factor = u64::from(load.factor_milli(cap));
let scaled = (idle.saturating_mul(factor) + 500) / 1000;
match ceiling {
Some(c) if c > 0 => scaled.min(c),
_ => scaled,
}
}
pub fn read() -> Option<Load> {
let avg1 = platform::avg1()?;
let cores = std::thread::available_parallelism()
.map(|n| u32::try_from(n.get()).unwrap_or(u32::MAX))
.unwrap_or(1);
Some(Load {
avg1_milli: u32::try_from((avg1 * 1000.0).round() as i64).unwrap_or(u32::MAX),
cores,
})
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod platform {
extern "C" {
#[link_name = "getloadavg"]
fn getloadavg_raw(loadavg: *mut f64, nelem: i32) -> i32;
}
pub fn avg1() -> Option<f64> {
let mut avg = [0f64; 3];
let n = unsafe { getloadavg_raw(avg.as_mut_ptr(), 3) };
(n >= 1 && avg[0].is_finite() && avg[0] >= 0.0).then_some(avg[0])
}
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
mod platform {
pub fn avg1() -> Option<f64> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
fn load(avg1: f64, cores: u32) -> Load {
Load {
avg1_milli: (avg1 * 1000.0) as u32,
cores,
}
}
#[test]
fn the_budget_scales_with_oversubscription_up_to_the_cap() {
assert_eq!(scaled_budget(120, load(1.0, 8), 4, Some(3600)), 120);
assert_eq!(scaled_budget(120, load(3.9, 8), 4, Some(3600)), 120);
assert_eq!(scaled_budget(120, load(16.0, 8), 4, Some(3600)), 240);
assert_eq!(scaled_budget(120, load(31.2, 8), 4, Some(3600)), 468);
assert_eq!(scaled_budget(120, load(40.0, 8), 4, Some(3600)), 480);
assert_eq!(scaled_budget(120, load(31.2, 8), 1, Some(3600)), 120);
assert_eq!(scaled_budget(120, load(80.0, 8), 16, Some(3600)), 1200);
assert_eq!(scaled_budget(120, load(31.2, 8), 4, Some(300)), 300);
assert_eq!(scaled_budget(120, load(40.0, 8), 4, None), 480);
assert_eq!(scaled_budget(120, load(2.0, 0), 4, None), 240);
}
#[test]
fn factors_and_averages_read_as_one_decimal() {
assert_eq!(load(31.2, 8).factor_milli(4), 3900);
assert_eq!(factor_text(3900), "×3.9");
assert_eq!(factor_text(1000), "×1.0");
assert_eq!(load(31.25, 8).avg1_text(), "31.2");
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn the_platform_reads_a_sane_load() {
let l = read().expect("linux and macos read the load");
assert!(l.cores >= 1);
assert!(l.avg1_milli < 100_000_000);
}
}