entrenar/efficiency/device/
cpu.rs1use serde::{Deserialize, Serialize};
4
5use super::simd::SimdCapability;
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct CpuInfo {
10 pub cores: u32,
12 pub threads: u32,
14 pub simd: SimdCapability,
16 pub model: String,
18 pub cache_bytes: u64,
20}
21
22impl CpuInfo {
23 pub fn new(cores: u32, threads: u32, simd: SimdCapability, model: impl Into<String>) -> Self {
25 Self { cores, threads, simd, model: model.into(), cache_bytes: 0 }
26 }
27
28 pub fn with_cache(mut self, cache_bytes: u64) -> Self {
30 self.cache_bytes = cache_bytes;
31 self
32 }
33
34 pub(super) fn usable_cores(detected_physical: Option<u32>, threads: u32) -> u32 {
47 let threads = threads.max(1);
48 detected_physical.map_or(threads, |physical| physical.clamp(1, threads))
49 }
50
51 pub fn detect() -> Self {
53 let threads = std::thread::available_parallelism().map(|n| n.get() as u32).unwrap_or(1);
55
56 let cores = Self::usable_cores(Self::detect_physical_cores(), threads);
59 let simd = SimdCapability::detect();
60
61 let model = Self::detect_model();
63
64 Self {
65 cores,
66 threads,
67 simd,
68 model,
69 cache_bytes: 0, }
71 }
72
73 #[cfg(target_os = "linux")]
75 fn detect_physical_cores() -> Option<u32> {
76 std::fs::read_to_string("/proc/cpuinfo").ok().map(|info| {
77 let mut core_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
79 let mut current_physical_id = String::new();
80
81 for line in info.lines() {
82 if line.starts_with("physical id") {
83 current_physical_id =
84 line.split(':').nth(1).map(|s| s.trim().to_string()).unwrap_or_default();
85 } else if line.starts_with("core id") {
86 let core_id =
87 line.split(':').nth(1).map(|s| s.trim().to_string()).unwrap_or_default();
88 core_ids.insert(format!("{current_physical_id}-{core_id}"));
89 }
90 }
91
92 if core_ids.is_empty() {
93 info.lines().filter(|line| line.starts_with("processor")).count() as u32
95 } else {
96 core_ids.len() as u32
97 }
98 })
99 }
100
101 #[cfg(target_os = "macos")]
103 fn detect_physical_cores() -> Option<u32> {
104 std::process::Command::new("sysctl")
105 .args(["-n", "hw.physicalcpu"])
106 .output()
107 .ok()
108 .and_then(|output| String::from_utf8(output.stdout).ok())
109 .and_then(|s| s.trim().parse().ok())
110 }
111
112 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
114 fn detect_physical_cores() -> Option<u32> {
115 None
116 }
117
118 #[cfg(target_os = "linux")]
120 fn detect_model() -> String {
121 std::fs::read_to_string("/proc/cpuinfo")
122 .ok()
123 .and_then(|info| {
124 info.lines()
125 .find(|line| line.starts_with("model name"))
126 .and_then(|line| line.split(':').nth(1))
127 .map(|s| s.trim().to_string())
128 })
129 .unwrap_or_else(|| "Unknown CPU".to_string())
130 }
131
132 #[cfg(target_os = "macos")]
133 fn detect_model() -> String {
134 std::process::Command::new("sysctl")
135 .args(["-n", "machdep.cpu.brand_string"])
136 .output()
137 .ok()
138 .and_then(|output| String::from_utf8(output.stdout).ok())
139 .map(|s| s.trim().to_string())
140 .unwrap_or_else(|| "Unknown CPU".to_string())
141 }
142
143 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
144 fn detect_model() -> String {
145 "Unknown CPU".to_string()
146 }
147
148 pub fn estimated_memory_bandwidth_gbps(&self) -> f64 {
150 40.0 * (f64::from(self.cores) / 8.0).min(2.0)
152 }
153}