1use std::sync::{Arc, LazyLock};
9
10pub static SYSTEM: LazyLock<Arc<SystemProfile>> =
12 LazyLock::new(|| Arc::new(SystemProfile::detect()));
13
14#[derive(Debug, Clone)]
16pub struct SystemProfile {
17 pub cpu_count: usize,
19
20 pub physical_cpu_count: usize,
22
23 pub total_memory: u64,
25
26 pub available_memory: u64,
28
29 pub os_name: String,
31
32 pub os_version: String,
34
35 pub hostname: String,
37
38 pub is_macos: bool,
40
41 pub is_windows: bool,
43
44 pub is_linux: bool,
46
47 pub recommended_io_workers: usize,
49
50 pub recommended_cpu_workers: usize,
52}
53
54impl SystemProfile {
55 fn detect() -> Self {
57 use sysinfo::System;
58
59 let cpu_count = num_cpus::get();
60 let physical_cpu_count = num_cpus::get_physical();
61
62 let mut sys = System::new_with_specifics(
64 sysinfo::RefreshKind::new().with_memory(sysinfo::MemoryRefreshKind::everything()),
65 );
66 sys.refresh_memory();
67
68 let total_memory = sys.total_memory();
69 let available_memory = sys.available_memory();
70
71 let os_name = System::name().unwrap_or_else(|| "Unknown".to_string());
73 let os_version = System::os_version().unwrap_or_else(|| "Unknown".to_string());
74 let hostname = System::host_name().unwrap_or_else(|| "Unknown".to_string());
75
76 let is_macos = cfg!(target_os = "macos");
78 let is_windows = cfg!(target_os = "windows");
79 let is_linux = cfg!(target_os = "linux");
80
81 let recommended_io_workers = cpu_count * 2;
84
85 let recommended_cpu_workers = physical_cpu_count;
87
88 Self {
89 cpu_count,
90 physical_cpu_count,
91 total_memory,
92 available_memory,
93 os_name,
94 os_version,
95 hostname,
96 is_macos,
97 is_windows,
98 is_linux,
99 recommended_io_workers,
100 recommended_cpu_workers,
101 }
102 }
103
104 pub fn get() -> Arc<SystemProfile> {
106 SYSTEM.clone()
107 }
108
109 pub fn calculate_workers(&self, percentage: usize) -> usize {
111 let percentage = percentage.min(100) as f32 / 100.0;
112 ((self.cpu_count as f32 * percentage).ceil() as usize).max(1)
113 }
114
115 pub fn calculate_workers_with_limit(&self, percentage: usize, max_threads: usize) -> usize {
117 if max_threads > 0 {
118 self.calculate_workers(percentage).min(max_threads)
119 } else {
120 self.calculate_workers(percentage)
121 }
122 }
123
124 pub fn adapt_workers_for_workload(&self, item_count: usize, max_workers: usize) -> usize {
126 match item_count {
127 0..=10 => 1.min(max_workers), 11..=50 => (max_workers / 2).max(1), 51..=100 => (max_workers * 3 / 4).max(1), _ => max_workers, }
132 }
133
134 pub fn should_use_parallel(&self, min_memory_mb: u64) -> bool {
136 self.cpu_count > 1 && self.available_memory > (min_memory_mb * 1024 * 1024)
137 }
138
139 pub fn summary(&self) -> String {
141 format!(
142 "System: {} {} on {}\nCPUs: {} ({} physical)\nMemory: {:.2} GB ({:.2} GB \
143 available)\nHost: {}",
144 self.os_name,
145 self.os_version,
146 if self.is_macos {
147 "macOS"
148 } else if self.is_windows {
149 "Windows"
150 } else if self.is_linux {
151 "Linux"
152 } else {
153 "Other"
154 },
155 self.cpu_count,
156 self.physical_cpu_count,
157 self.total_memory as f64 / (1024.0 * 1024.0 * 1024.0),
158 self.available_memory as f64 / (1024.0 * 1024.0 * 1024.0),
159 self.hostname
160 )
161 }
162
163 pub fn total_memory_gb(&self) -> f64 {
165 self.total_memory as f64 / (1024.0 * 1024.0 * 1024.0)
166 }
167
168 pub fn available_memory_gb(&self) -> f64 {
170 self.available_memory as f64 / (1024.0 * 1024.0 * 1024.0)
171 }
172}
173
174impl SystemProfile {
176 pub fn cpu_count() -> usize {
178 SYSTEM.cpu_count
179 }
180
181 pub fn physical_cpu_count() -> usize {
183 SYSTEM.physical_cpu_count
184 }
185
186 pub fn is_multicore() -> bool {
188 SYSTEM.cpu_count > 1
189 }
190
191 pub fn is_macos() -> bool {
193 SYSTEM.is_macos
194 }
195
196 pub fn is_windows() -> bool {
197 SYSTEM.is_windows
198 }
199
200 pub fn is_linux() -> bool {
201 SYSTEM.is_linux
202 }
203}
204
205#[cfg(feature = "gpu")]
206pub mod gpu {
207 #[derive(Debug, Clone)]
209 pub struct GpuInfo {
210 pub name: String,
211 pub memory_mb: u64,
212 pub cuda_cores: Option<u32>,
213 }
214
215 pub fn detect_nvidia_gpus() -> Vec<GpuInfo> {
217 vec![]
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn test_system_profile_initialization() {
228 let profile = SystemProfile::get();
229 assert!(profile.cpu_count > 0);
230 assert!(profile.physical_cpu_count > 0);
231 assert!(profile.total_memory > 0);
232
233 assert!(profile.is_macos || profile.is_windows || profile.is_linux);
235 }
236
237 #[test]
238 fn test_worker_calculation() {
239 let profile = SystemProfile::get();
240
241 let half_workers = profile.calculate_workers(50);
243 assert!(half_workers >= 1);
244 assert!(half_workers <= profile.cpu_count);
245
246 let full_workers = profile.calculate_workers(100);
248 assert_eq!(full_workers, profile.cpu_count);
249
250 let limited = profile.calculate_workers_with_limit(100, 4);
252 assert!(limited <= 4);
253 }
254
255 #[test]
256 fn test_workload_adaptation() {
257 let profile = SystemProfile::get();
258 let max_workers = 8;
259
260 assert_eq!(profile.adapt_workers_for_workload(5, max_workers), 1);
262
263 let medium = profile.adapt_workers_for_workload(30, max_workers);
265 assert!(medium <= max_workers / 2);
266
267 assert_eq!(
269 profile.adapt_workers_for_workload(200, max_workers),
270 max_workers
271 );
272 }
273
274 #[test]
275 fn test_static_access() {
276 let profile1 = SystemProfile::get();
278 let profile2 = SystemProfile::get();
279 assert_eq!(profile1.cpu_count, profile2.cpu_count);
280
281 assert_eq!(SystemProfile::cpu_count(), profile1.cpu_count);
283 assert_eq!(
284 SystemProfile::physical_cpu_count(),
285 profile1.physical_cpu_count
286 );
287 }
288
289 #[test]
290 fn test_summary() {
291 let profile = SystemProfile::get();
292 let summary = profile.summary();
293
294 assert!(summary.contains("CPUs:"));
296 assert!(summary.contains("Memory:"));
297 assert!(summary.contains("System:"));
298 }
299}