pub fn physical_core_count() -> usize {
#[cfg(target_os = "linux")]
if let Some(n) = linux_physical_cores() {
return n;
}
let ret = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
if ret < 1 { 1 } else { ret as usize }
}
pub(crate) fn physical_core_first_cpus() -> Option<Vec<usize>> {
#[cfg(target_os = "linux")]
{
linux_first_siblings()
}
#[cfg(not(target_os = "linux"))]
{
None
}
}
#[cfg(target_os = "linux")]
fn linux_first_siblings() -> Option<Vec<usize>> {
use std::collections::HashSet;
let mut seen: HashSet<(String, String)> = HashSet::new();
let mut firsts: Vec<usize> = Vec::new();
let mut cpu = 0usize;
while let Ok(pkg_raw) = std::fs::read_to_string(format!(
"/sys/devices/system/cpu/cpu{cpu}/topology/physical_package_id"
)) {
let pkg = pkg_raw.trim().to_owned();
let core = match std::fs::read_to_string(format!(
"/sys/devices/system/cpu/cpu{cpu}/topology/core_id"
)) {
Ok(s) => s.trim().to_owned(),
Err(_) => break,
};
if seen.insert((pkg, core)) {
firsts.push(cpu);
}
cpu += 1;
}
if firsts.is_empty() {
None
} else {
Some(firsts)
}
}
#[cfg(target_os = "linux")]
fn linux_physical_cores() -> Option<usize> {
linux_first_siblings().map(|v| v.len())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn physical_core_count_is_positive() {
assert!(physical_core_count() >= 1);
}
#[test]
fn physical_core_count_does_not_exceed_logical() {
let logical = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } as usize;
assert!(physical_core_count() <= logical);
}
#[test]
fn first_siblings_are_distinct_ascending_and_agree_with_count() {
if let Some(cpus) = physical_core_first_cpus() {
assert_eq!(cpus.len(), physical_core_count());
assert!(cpus.windows(2).all(|w| w[0] < w[1]));
let logical = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } as usize;
assert!(cpus.iter().all(|&c| c < logical));
}
}
}