use serde::{Deserialize, Serialize};
use super::simd::SimdCapability;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CpuInfo {
pub cores: u32,
pub threads: u32,
pub simd: SimdCapability,
pub model: String,
pub cache_bytes: u64,
}
impl CpuInfo {
pub fn new(cores: u32, threads: u32, simd: SimdCapability, model: impl Into<String>) -> Self {
Self { cores, threads, simd, model: model.into(), cache_bytes: 0 }
}
pub fn with_cache(mut self, cache_bytes: u64) -> Self {
self.cache_bytes = cache_bytes;
self
}
pub fn detect() -> Self {
let threads = std::thread::available_parallelism().map(|n| n.get() as u32).unwrap_or(1);
let cores = Self::detect_physical_cores().unwrap_or_else(|| threads.max(1));
let simd = SimdCapability::detect();
let model = Self::detect_model();
Self {
cores,
threads,
simd,
model,
cache_bytes: 0, }
}
#[cfg(target_os = "linux")]
fn detect_physical_cores() -> Option<u32> {
std::fs::read_to_string("/proc/cpuinfo").ok().map(|info| {
let mut core_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
let mut current_physical_id = String::new();
for line in info.lines() {
if line.starts_with("physical id") {
current_physical_id =
line.split(':').nth(1).map(|s| s.trim().to_string()).unwrap_or_default();
} else if line.starts_with("core id") {
let core_id =
line.split(':').nth(1).map(|s| s.trim().to_string()).unwrap_or_default();
core_ids.insert(format!("{current_physical_id}-{core_id}"));
}
}
if core_ids.is_empty() {
info.lines().filter(|line| line.starts_with("processor")).count() as u32
} else {
core_ids.len() as u32
}
})
}
#[cfg(target_os = "macos")]
fn detect_physical_cores() -> Option<u32> {
std::process::Command::new("sysctl")
.args(["-n", "hw.physicalcpu"])
.output()
.ok()
.and_then(|output| String::from_utf8(output.stdout).ok())
.and_then(|s| s.trim().parse().ok())
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn detect_physical_cores() -> Option<u32> {
None
}
#[cfg(target_os = "linux")]
fn detect_model() -> String {
std::fs::read_to_string("/proc/cpuinfo")
.ok()
.and_then(|info| {
info.lines()
.find(|line| line.starts_with("model name"))
.and_then(|line| line.split(':').nth(1))
.map(|s| s.trim().to_string())
})
.unwrap_or_else(|| "Unknown CPU".to_string())
}
#[cfg(target_os = "macos")]
fn detect_model() -> String {
std::process::Command::new("sysctl")
.args(["-n", "machdep.cpu.brand_string"])
.output()
.ok()
.and_then(|output| String::from_utf8(output.stdout).ok())
.map(|s| s.trim().to_string())
.unwrap_or_else(|| "Unknown CPU".to_string())
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn detect_model() -> String {
"Unknown CPU".to_string()
}
pub fn estimated_memory_bandwidth_gbps(&self) -> f64 {
40.0 * (f64::from(self.cores) / 8.0).min(2.0)
}
}