fn parse_cpu_list(s: &str) -> Vec<usize> {
let mut cpus = Vec::new();
for part in s.trim().split(',') {
if let Some((a, b)) = part.split_once('-') {
if let (Ok(lo), Ok(hi)) = (a.trim().parse::<usize>(), b.trim().parse::<usize>()) {
cpus.extend(lo..=hi);
}
} else if let Ok(n) = part.trim().parse::<usize>() {
cpus.push(n);
}
}
cpus
}
pub fn physical_cores() -> Vec<usize> {
let mut physical = Vec::new();
for cpu_id in 0..4096 {
let path = format!(
"/sys/devices/system/cpu/cpu{cpu_id}/topology/thread_siblings_list"
);
match std::fs::read_to_string(&path) {
Ok(s) => {
let siblings = parse_cpu_list(&s);
let min = siblings.iter().copied().min().unwrap_or(cpu_id);
if min == cpu_id {
physical.push(cpu_id);
}
}
Err(_) => break,
}
}
if physical.is_empty() {
let n = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
(0..n).collect()
} else {
physical
}
}
pub fn pin_to_cpu(cpu_id: usize) {
#[cfg(target_os = "linux")]
unsafe {
let mut set = std::mem::zeroed::<libc::cpu_set_t>();
libc::CPU_SET(cpu_id, &mut set);
libc::sched_setaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &set);
}
#[cfg(not(target_os = "linux"))]
let _ = cpu_id;
}