1use std::sync::OnceLock;
21use std::time::Instant;
22
23#[derive(Debug, Clone)]
25pub struct HardwareDetector {
26 cpu_info: CpuInfo,
27 memory_info: MemoryInfo,
28 cache_info: CacheInfo,
29}
30
31#[derive(Debug, Clone)]
32pub struct CpuInfo {
33 pub cores: usize,
34 pub logical_cores: usize,
35 pub architecture: String,
36 pub vendor: String,
37 pub model_name: String,
38 pub base_frequency_mhz: Option<u32>,
39 pub max_frequency_mhz: Option<u32>,
40 pub features: Vec<String>,
41}
42
43#[derive(Debug, Clone)]
44pub struct MemoryInfo {
45 pub total_memory_bytes: u64,
46 pub available_memory_bytes: u64,
47 pub page_size_bytes: usize,
48}
49
50#[derive(Debug, Clone, Default)]
51pub struct CacheInfo {
52 pub l1_data_cache_kb: Option<u32>,
53 pub l1_instruction_cache_kb: Option<u32>,
54 pub l2_cache_kb: Option<u32>,
55 pub l3_cache_kb: Option<u32>,
56 pub cache_line_size_bytes: Option<u32>,
57}
58
59impl Default for CpuInfo {
60 fn default() -> Self {
61 Self {
62 cores: 1,
63 logical_cores: 1,
64 architecture: "unknown".to_string(),
65 vendor: "unknown".to_string(),
66 model_name: "unknown".to_string(),
67 base_frequency_mhz: None,
68 max_frequency_mhz: None,
69 features: Vec::new(),
70 }
71 }
72}
73
74impl Default for MemoryInfo {
75 fn default() -> Self {
76 Self {
77 total_memory_bytes: 0,
78 available_memory_bytes: 0,
79 page_size_bytes: 4096, }
81 }
82}
83
84impl HardwareDetector {
85 pub fn new() -> Self {
87 Self {
88 cpu_info: Self::detect_cpu_info(),
89 memory_info: Self::detect_memory_info(),
90 cache_info: Self::detect_cache_info(),
91 }
92 }
93
94 pub fn cpu_cores(&self) -> usize {
96 self.cpu_info.cores
97 }
98
99 pub fn logical_cores(&self) -> usize {
101 self.cpu_info.logical_cores
102 }
103
104 pub fn cpu_architecture(&self) -> &str {
106 &self.cpu_info.architecture
107 }
108
109 pub fn cpu_vendor(&self) -> &str {
111 &self.cpu_info.vendor
112 }
113
114 pub fn total_memory(&self) -> u64 {
116 self.memory_info.total_memory_bytes
117 }
118
119 pub fn available_memory(&self) -> u64 {
121 self.memory_info.available_memory_bytes
122 }
123
124 pub fn has_avx2(&self) -> bool {
126 self.cpu_info.features.iter().any(|f| f.contains("avx2"))
127 }
128
129 pub fn has_avx512(&self) -> bool {
131 self.cpu_info.features.iter().any(|f| f.contains("avx512"))
132 }
133
134 pub fn has_sse42(&self) -> bool {
136 self.cpu_info.features.iter().any(|f| f.contains("sse4_2"))
137 }
138
139 pub fn has_neon(&self) -> bool {
141 self.cpu_info.features.iter().any(|f| f.contains("neon"))
142 }
143
144 pub fn is_arm(&self) -> bool {
146 self.cpu_info.architecture.contains("arm") || self.cpu_info.architecture.contains("aarch")
147 }
148
149 pub fn is_x86(&self) -> bool {
151 self.cpu_info.architecture.contains("x86") || self.cpu_info.architecture.contains("x64")
152 }
153
154 pub fn l3_cache_size_kb(&self) -> Option<u32> {
156 self.cache_info.l3_cache_kb
157 }
158
159 pub fn cache_line_size(&self) -> Option<u32> {
161 self.cache_info.cache_line_size_bytes
162 }
163
164 pub fn cpu_features(&self) -> &[String] {
166 &self.cpu_info.features
167 }
168
169 fn detect_cpu_info() -> CpuInfo {
171 let logical_cores = num_cpus::get();
180 let raw_physical = num_cpus::get_physical();
181 let cores = raw_physical.min(logical_cores);
183
184 let mut cpu_info = CpuInfo {
185 logical_cores,
186 cores,
187 architecture: std::env::consts::ARCH.to_string(),
188 ..Default::default()
189 };
190
191 #[cfg(target_arch = "x86_64")]
193 {
194 Self::detect_x86_features(&mut cpu_info);
195 }
196
197 #[cfg(target_arch = "aarch64")]
198 {
199 Self::detect_arm_features(&mut cpu_info);
200 }
201
202 cpu_info
203 }
204
205 #[cfg(target_arch = "x86_64")]
206 fn detect_x86_features(cpu_info: &mut CpuInfo) {
207 if is_x86_feature_detected!("sse") {
208 cpu_info.features.push("sse".to_string());
209 }
210 if is_x86_feature_detected!("sse2") {
211 cpu_info.features.push("sse2".to_string());
212 }
213 if is_x86_feature_detected!("sse3") {
214 cpu_info.features.push("sse3".to_string());
215 }
216 if is_x86_feature_detected!("sse4.1") {
217 cpu_info.features.push("sse4_1".to_string());
218 }
219 if is_x86_feature_detected!("sse4.2") {
220 cpu_info.features.push("sse4_2".to_string());
221 }
222 if is_x86_feature_detected!("avx") {
223 cpu_info.features.push("avx".to_string());
224 }
225 if is_x86_feature_detected!("avx2") {
226 cpu_info.features.push("avx2".to_string());
227 }
228 if is_x86_feature_detected!("fma") {
229 cpu_info.features.push("fma".to_string());
230 }
231
232 cpu_info.vendor = "Intel/AMD".to_string(); }
235
236 #[cfg(target_arch = "aarch64")]
237 fn detect_arm_features(cpu_info: &mut CpuInfo) {
238 cpu_info.vendor = "ARM".to_string();
239 cpu_info.features.push("neon".to_string()); }
241
242 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
243 fn detect_x86_features(_cpu_info: &mut CpuInfo) {}
244
245 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
246 fn detect_arm_features(_cpu_info: &mut CpuInfo) {}
247
248 fn detect_memory_info() -> MemoryInfo {
250 let mut memory_info = MemoryInfo::default();
251
252 #[cfg(feature = "sysinfo")]
254 {
255 use sysinfo::System;
256 let mut system = System::new_all();
257 system.refresh_memory();
258 memory_info.total_memory_bytes = system.total_memory() * 1024; memory_info.available_memory_bytes = system.available_memory() * 1024;
260 }
261
262 #[cfg(not(feature = "sysinfo"))]
264 {
265 #[cfg(target_os = "linux")]
267 {
268 if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
269 for line in meminfo.lines() {
270 if line.starts_with("MemTotal:") {
271 if let Some(kb_str) = line.split_whitespace().nth(1) {
272 if let Ok(kb) = kb_str.parse::<u64>() {
273 memory_info.total_memory_bytes = kb * 1024;
274 }
275 }
276 } else if line.starts_with("MemAvailable:") {
277 if let Some(kb_str) = line.split_whitespace().nth(1) {
278 if let Ok(kb) = kb_str.parse::<u64>() {
279 memory_info.available_memory_bytes = kb * 1024;
280 }
281 }
282 }
283 }
284 }
285 }
286
287 #[cfg(target_os = "macos")]
288 {
289 if let Ok(output) = std::process::Command::new("sysctl")
291 .arg("-n")
292 .arg("hw.memsize")
293 .output()
294 {
295 if let Ok(mem_str) = String::from_utf8(output.stdout) {
296 if let Ok(mem_bytes) = mem_str.trim().parse::<u64>() {
297 memory_info.total_memory_bytes = mem_bytes;
298 memory_info.available_memory_bytes = mem_bytes / 2; }
300 }
301 }
302 }
303
304 if memory_info.total_memory_bytes == 0 {
306 memory_info.total_memory_bytes = 8 * 1024 * 1024 * 1024; memory_info.available_memory_bytes = 4 * 1024 * 1024 * 1024; }
309 }
310
311 #[cfg(unix)]
313 {
314 unsafe {
315 let page_size = libc::sysconf(libc::_SC_PAGESIZE);
316 if page_size > 0 {
317 memory_info.page_size_bytes = page_size as usize;
318 }
319 }
320 }
321
322 memory_info
323 }
324
325 fn detect_cache_info() -> CacheInfo {
327 let mut cache_info = CacheInfo::default();
328
329 #[cfg(target_os = "linux")]
331 {
332 Self::detect_linux_cache_info(&mut cache_info);
333 }
334
335 #[cfg(target_os = "macos")]
336 {
337 Self::detect_macos_cache_info(&mut cache_info);
338 }
339
340 #[cfg(target_os = "windows")]
341 {
342 Self::detect_windows_cache_info(&mut cache_info);
343 }
344
345 cache_info
346 }
347
348 #[cfg(target_os = "linux")]
349 fn detect_linux_cache_info(cache_info: &mut CacheInfo) {
350 if let Ok(l1d) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index0/size") {
352 if let Ok(size) = l1d.trim().strip_suffix("K").unwrap_or(&l1d).parse::<u32>() {
353 cache_info.l1_data_cache_kb = Some(size);
354 }
355 }
356
357 if let Ok(l2) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index2/size") {
358 if let Ok(size) = l2.trim().strip_suffix("K").unwrap_or(&l2).parse::<u32>() {
359 cache_info.l2_cache_kb = Some(size);
360 }
361 }
362
363 if let Ok(l3) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index3/size") {
364 if let Ok(size) = l3.trim().strip_suffix("K").unwrap_or(&l3).parse::<u32>() {
365 cache_info.l3_cache_kb = Some(size);
366 }
367 }
368
369 cache_info.cache_line_size_bytes = Some(64);
371 }
372
373 #[cfg(target_os = "macos")]
374 fn detect_macos_cache_info(cache_info: &mut CacheInfo) {
375 cache_info.cache_line_size_bytes = Some(64); }
378
379 #[cfg(target_os = "windows")]
380 fn detect_windows_cache_info(cache_info: &mut CacheInfo) {
381 cache_info.cache_line_size_bytes = Some(64); }
384
385 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
386 fn detect_linux_cache_info(_cache_info: &mut CacheInfo) {}
387
388 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
389 fn detect_macos_cache_info(_cache_info: &mut CacheInfo) {}
390
391 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
392 fn detect_windows_cache_info(_cache_info: &mut CacheInfo) {}
393}
394
395impl Default for HardwareDetector {
396 fn default() -> Self {
397 Self::new()
398 }
399}
400
401#[derive(Debug, Clone)]
403pub struct OSInfo {
404 pub name: String,
405 pub version: String,
406 pub arch: String,
407 pub kernel_version: Option<String>,
408 pub is_64bit: bool,
409}
410
411impl OSInfo {
412 pub fn detect() -> Self {
414 Self {
415 name: std::env::consts::OS.to_string(),
416 version: Self::detect_os_version(),
417 arch: std::env::consts::ARCH.to_string(),
418 kernel_version: Self::detect_kernel_version(),
419 is_64bit: cfg!(target_pointer_width = "64"),
420 }
421 }
422
423 pub fn is_linux(&self) -> bool {
425 self.name == "linux"
426 }
427
428 pub fn is_macos(&self) -> bool {
430 self.name == "macos"
431 }
432
433 pub fn is_windows(&self) -> bool {
435 self.name == "windows"
436 }
437
438 pub fn is_unix(&self) -> bool {
440 cfg!(unix)
441 }
442
443 fn detect_os_version() -> String {
444 #[cfg(target_os = "linux")]
445 {
446 if let Ok(version) = std::fs::read_to_string("/proc/version") {
447 return version.lines().next().unwrap_or("unknown").to_string();
448 }
449 }
450
451 #[cfg(target_os = "macos")]
452 {
453 "macOS".to_string()
455 }
456 #[cfg(target_os = "windows")]
457 {
458 "Windows".to_string()
460 }
461 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
462 {
463 "unknown".to_string()
464 }
465 }
466
467 fn detect_kernel_version() -> Option<String> {
468 #[cfg(unix)]
469 {
470 if let Ok(output) = std::process::Command::new("uname").arg("-r").output() {
471 if let Ok(version) = String::from_utf8(output.stdout) {
472 return Some(version.trim().to_string());
473 }
474 }
475 }
476 None
477 }
478}
479
480#[derive(Debug, Clone)]
482pub struct RuntimeInfo {
483 pub rust_version: String,
484 pub compiler: String,
485 pub target_triple: String,
486 pub build_profile: String,
487 pub features: Vec<String>,
488}
489
490impl RuntimeInfo {
491 pub fn detect() -> Self {
493 let target_family = std::env::var("CARGO_CFG_TARGET_FAMILY")
494 .unwrap_or_else(|_| std::env::consts::FAMILY.to_string());
495 let target_triple = std::env::var("CARGO_CFG_TARGET_TRIPLE").unwrap_or_else(|_| {
496 format!(
497 "{}-{}-{}",
498 std::env::consts::ARCH,
499 std::env::consts::FAMILY,
500 std::env::consts::OS
501 )
502 });
503
504 Self {
505 rust_version: option_env!("CARGO_PKG_RUST_VERSION")
506 .unwrap_or("unknown")
507 .to_string(),
508 compiler: format!("{target_family} {}", rustc_version_runtime::version()),
509 target_triple,
510 build_profile: if cfg!(debug_assertions) {
511 "debug"
512 } else {
513 "release"
514 }
515 .to_string(),
516 features: Self::detect_features(),
517 }
518 }
519
520 pub fn is_debug(&self) -> bool {
522 self.build_profile == "debug"
523 }
524
525 pub fn is_release(&self) -> bool {
527 self.build_profile == "release"
528 }
529
530 fn detect_features() -> Vec<String> {
531 Vec::new()
543 }
544}
545
546#[derive(Debug, Clone)]
548pub struct PerformanceCharacteristics {
549 pub memory_bandwidth_gbps: f64,
550 pub l1_cache_latency_ns: f64,
551 pub l2_cache_latency_ns: f64,
552 pub l3_cache_latency_ns: f64,
553 pub memory_latency_ns: f64,
554 pub cpu_frequency_estimation_mhz: f64,
555 pub single_thread_performance_score: f64,
556 pub multi_thread_performance_score: f64,
557}
558
559impl PerformanceCharacteristics {
560 pub fn measure() -> Self {
562 let _start_time = Instant::now();
563
564 let memory_bandwidth = Self::measure_memory_bandwidth();
565 let cache_latencies = Self::measure_cache_latencies();
566 let cpu_freq = Self::estimate_cpu_frequency();
567 let single_perf = Self::measure_single_thread_performance();
568 let multi_perf = Self::measure_multi_thread_performance();
569
570 Self {
571 memory_bandwidth_gbps: memory_bandwidth,
572 l1_cache_latency_ns: cache_latencies.0,
573 l2_cache_latency_ns: cache_latencies.1,
574 l3_cache_latency_ns: cache_latencies.2,
575 memory_latency_ns: cache_latencies.3,
576 cpu_frequency_estimation_mhz: cpu_freq,
577 single_thread_performance_score: single_perf,
578 multi_thread_performance_score: multi_perf,
579 }
580 }
581
582 fn measure_memory_bandwidth() -> f64 {
583 const SIZE: usize = 16 * 1024 * 1024; let mut data = vec![0u64; SIZE / 8];
586
587 let start = Instant::now();
588
589 for (i, item) in data.iter_mut().enumerate() {
591 *item = i as u64;
592 }
593
594 let mut sum = 0u64;
596 for &val in &data {
597 sum = sum.wrapping_add(val);
598 }
599
600 let elapsed = start.elapsed();
601 let bytes_processed = (SIZE * 2) as f64; let bandwidth_bps = bytes_processed / elapsed.as_secs_f64();
603
604 std::hint::black_box(sum);
606
607 bandwidth_bps / 1e9 }
609
610 fn measure_cache_latencies() -> (f64, f64, f64, f64) {
611 (1.0, 3.0, 10.0, 100.0) }
615
616 fn estimate_cpu_frequency() -> f64 {
617 let iterations = 10_000_000;
619 let start = Instant::now();
620
621 let mut counter = 0u64;
622 for _ in 0..iterations {
623 counter = counter.wrapping_add(1);
624 }
625
626 let elapsed = start.elapsed();
627 std::hint::black_box(counter);
628
629 let cycles_per_second = iterations as f64 / elapsed.as_secs_f64();
631 cycles_per_second / 1e6 }
633
634 fn measure_single_thread_performance() -> f64 {
635 let start = Instant::now();
637 let mut sum = 0.0;
638
639 for i in 0..1_000_000 {
640 sum += (i as f64).sqrt().sin().cos();
641 }
642
643 let elapsed = start.elapsed();
644 std::hint::black_box(sum);
645
646 1_000_000.0 / elapsed.as_secs_f64()
648 }
649
650 fn measure_multi_thread_performance() -> f64 {
651 use std::thread;
652
653 let num_threads = num_cpus::get();
654 let start = Instant::now();
655
656 let handles: Vec<_> = (0..num_threads)
657 .map(|_| {
658 thread::spawn(|| {
659 let mut sum = 0.0;
660 for i in 0..100_000 {
661 sum += (i as f64).sqrt().sin().cos();
662 }
663 sum
664 })
665 })
666 .collect();
667
668 let results: Vec<_> = handles
669 .into_iter()
670 .map(|h| h.join().expect("operation should succeed"))
671 .collect();
672 let elapsed = start.elapsed();
673
674 std::hint::black_box(results);
675
676 (num_threads * 100_000) as f64 / elapsed.as_secs_f64()
678 }
679}
680
681#[derive(Debug, Clone)]
683pub struct FeatureChecker {
684 hardware: HardwareDetector,
685 os_info: OSInfo,
686 #[allow(dead_code)]
687 runtime: RuntimeInfo,
688}
689
690impl FeatureChecker {
691 pub fn new() -> Self {
693 Self {
694 hardware: HardwareDetector::new(),
695 os_info: OSInfo::detect(),
696 runtime: RuntimeInfo::detect(),
697 }
698 }
699
700 pub fn simd_available(&self) -> bool {
702 self.hardware.has_sse42() || self.hardware.has_avx2() || self.hardware.has_neon()
703 }
704
705 pub fn parallel_beneficial(&self) -> bool {
707 self.hardware.logical_cores() > 1
708 }
709
710 pub fn memory_mapping_available(&self) -> bool {
712 self.os_info.is_unix() || self.os_info.is_windows()
713 }
714
715 pub fn high_resolution_timer_available(&self) -> bool {
717 true }
719
720 pub fn recommended_thread_count(&self) -> usize {
722 self.hardware.logical_cores().min(32)
724 }
725
726 pub fn recommended_simd_width(&self) -> usize {
728 if self.hardware.has_avx512() {
729 16 } else if self.hardware.has_avx2() {
731 8 } else if self.hardware.has_sse42() || self.hardware.has_neon() {
733 4 } else {
735 1 }
737 }
738
739 pub fn optimization_recommended(&self, optimization: &str) -> bool {
741 match optimization {
742 "simd" => self.simd_available(),
743 "parallel" => self.parallel_beneficial(),
744 "cache_friendly" => self.hardware.l3_cache_size_kb().is_some(),
745 "memory_pool" => self.hardware.total_memory() > 1_000_000_000, _ => false,
747 }
748 }
749
750 pub fn get_recommendations(&self) -> Vec<String> {
752 let mut recommendations = Vec::new();
753
754 if self.simd_available() {
755 recommendations.push("Enable SIMD optimizations".to_string());
756 }
757
758 if self.parallel_beneficial() {
759 recommendations.push(format!(
760 "Use parallel processing with {} threads",
761 self.recommended_thread_count()
762 ));
763 }
764
765 if let Some(cache_size) = self.hardware.l3_cache_size_kb() {
766 recommendations.push(format!("Optimize for {cache_size}KB L3 cache"));
767 }
768
769 if self.hardware.total_memory() < 1_000_000_000 {
770 recommendations.push("Consider memory usage optimizations".to_string());
771 }
772
773 recommendations
774 }
775}
776
777impl Default for FeatureChecker {
778 fn default() -> Self {
779 Self::new()
780 }
781}
782
783static GLOBAL_ENV_INFO: OnceLock<EnvironmentInfo> = OnceLock::new();
785
786#[derive(Debug, Clone)]
788pub struct EnvironmentInfo {
789 pub hardware: HardwareDetector,
790 pub os_info: OSInfo,
791 pub runtime: RuntimeInfo,
792 pub performance: PerformanceCharacteristics,
793 pub features: FeatureChecker,
794}
795
796impl EnvironmentInfo {
797 pub fn global() -> &'static EnvironmentInfo {
799 GLOBAL_ENV_INFO.get_or_init(EnvironmentInfo::detect)
800 }
801
802 pub fn detect() -> Self {
804 let hardware = HardwareDetector::new();
805 let os_info = OSInfo::detect();
806 let runtime = RuntimeInfo::detect();
807 let performance = PerformanceCharacteristics::measure();
808 let features = FeatureChecker::new();
809
810 Self {
811 hardware,
812 os_info,
813 runtime,
814 performance,
815 features,
816 }
817 }
818
819 pub fn summary(&self) -> String {
821 format!(
822 "Environment Summary:\n\
823 OS: {} {} ({})\n\
824 CPU: {} cores ({} logical), {}\n\
825 Memory: {:.1} GB total, {:.1} GB available\n\
826 Runtime: {} {}\n\
827 Features: SIMD={}, Parallel={}, Cache={}KB",
828 self.os_info.name,
829 self.os_info.version,
830 self.os_info.arch,
831 self.hardware.cpu_cores(),
832 self.hardware.logical_cores(),
833 self.hardware.cpu_architecture(),
834 self.hardware.total_memory() as f64 / 1e9,
835 self.hardware.available_memory() as f64 / 1e9,
836 self.runtime.compiler,
837 self.runtime.build_profile,
838 self.features.simd_available(),
839 self.features.parallel_beneficial(),
840 self.hardware.l3_cache_size_kb().unwrap_or(0)
841 )
842 }
843}
844
845#[allow(non_snake_case)]
846#[cfg(test)]
847mod tests {
848 use super::*;
849
850 #[test]
851 fn test_hardware_detector() {
852 let hw = HardwareDetector::new();
853
854 assert!(hw.cpu_cores() >= 1);
855 assert!(hw.logical_cores() >= 1); assert!(!hw.cpu_architecture().is_empty());
857 assert!(hw.total_memory() > 0);
858 }
859
860 #[test]
861 fn test_os_info() {
862 let os = OSInfo::detect();
863
864 assert!(!os.name.is_empty());
865 assert!(!os.arch.is_empty());
866
867 assert!(os.is_linux() || os.is_macos() || os.is_windows() || !os.name.is_empty());
869 }
870
871 #[test]
872 fn test_runtime_info() {
873 let runtime = RuntimeInfo::detect();
874
875 assert!(!runtime.compiler.is_empty());
876 assert!(!runtime.target_triple.is_empty());
877 assert!(runtime.is_debug() || runtime.is_release());
878 }
879
880 #[test]
881 fn test_performance_characteristics() {
882 let perf = PerformanceCharacteristics::measure();
883
884 assert!(perf.memory_bandwidth_gbps > 0.0);
885 assert!(perf.cpu_frequency_estimation_mhz > 0.0);
886 assert!(perf.single_thread_performance_score > 0.0);
887 assert!(perf.multi_thread_performance_score > 0.0);
888 }
889
890 #[test]
891 fn test_feature_checker() {
892 let checker = FeatureChecker::new();
893
894 assert!(checker.recommended_thread_count() >= 1);
895 assert!(checker.recommended_simd_width() >= 1);
896 assert!(checker.high_resolution_timer_available());
897 }
898
899 #[test]
900 fn test_environment_info() {
901 let env = EnvironmentInfo::detect();
902 let summary = env.summary();
903
904 assert!(!summary.is_empty());
905 assert!(summary.contains("Environment Summary"));
906
907 let global_env = EnvironmentInfo::global();
909 assert!(!global_env.summary().is_empty());
910 }
911
912 #[test]
913 fn test_cpu_features() {
914 let hw = HardwareDetector::new();
915
916 let _has_avx2 = hw.has_avx2();
918 let _has_sse42 = hw.has_sse42();
919 let _has_neon = hw.has_neon();
920 let _is_arm = hw.is_arm();
921 let _is_x86 = hw.is_x86();
922 }
923
924 #[test]
925 fn test_optimization_recommendations() {
926 let checker = FeatureChecker::new();
927 let recommendations = checker.get_recommendations();
928
929 assert!(!recommendations.is_empty());
931
932 let _simd_rec = checker.optimization_recommended("simd");
934 let _parallel_rec = checker.optimization_recommended("parallel");
935 let _cache_rec = checker.optimization_recommended("cache_friendly");
936 }
937}