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 mut cpu_info = CpuInfo {
172 logical_cores: num_cpus::get(),
173 cores: num_cpus::get_physical(),
174 architecture: std::env::consts::ARCH.to_string(),
175 ..Default::default()
176 };
177
178 #[cfg(target_arch = "x86_64")]
180 {
181 Self::detect_x86_features(&mut cpu_info);
182 }
183
184 #[cfg(target_arch = "aarch64")]
185 {
186 Self::detect_arm_features(&mut cpu_info);
187 }
188
189 cpu_info
190 }
191
192 #[cfg(target_arch = "x86_64")]
193 fn detect_x86_features(cpu_info: &mut CpuInfo) {
194 if is_x86_feature_detected!("sse") {
195 cpu_info.features.push("sse".to_string());
196 }
197 if is_x86_feature_detected!("sse2") {
198 cpu_info.features.push("sse2".to_string());
199 }
200 if is_x86_feature_detected!("sse3") {
201 cpu_info.features.push("sse3".to_string());
202 }
203 if is_x86_feature_detected!("sse4.1") {
204 cpu_info.features.push("sse4_1".to_string());
205 }
206 if is_x86_feature_detected!("sse4.2") {
207 cpu_info.features.push("sse4_2".to_string());
208 }
209 if is_x86_feature_detected!("avx") {
210 cpu_info.features.push("avx".to_string());
211 }
212 if is_x86_feature_detected!("avx2") {
213 cpu_info.features.push("avx2".to_string());
214 }
215 if is_x86_feature_detected!("fma") {
216 cpu_info.features.push("fma".to_string());
217 }
218
219 cpu_info.vendor = "Intel/AMD".to_string(); }
222
223 #[cfg(target_arch = "aarch64")]
224 fn detect_arm_features(cpu_info: &mut CpuInfo) {
225 cpu_info.vendor = "ARM".to_string();
226 cpu_info.features.push("neon".to_string()); }
228
229 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
230 fn detect_x86_features(_cpu_info: &mut CpuInfo) {}
231
232 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
233 fn detect_arm_features(_cpu_info: &mut CpuInfo) {}
234
235 fn detect_memory_info() -> MemoryInfo {
237 let mut memory_info = MemoryInfo::default();
238
239 #[cfg(feature = "sysinfo")]
241 {
242 use sysinfo::System;
243 let mut system = System::new_all();
244 system.refresh_memory();
245 memory_info.total_memory_bytes = system.total_memory() * 1024; memory_info.available_memory_bytes = system.available_memory() * 1024;
247 }
248
249 #[cfg(not(feature = "sysinfo"))]
251 {
252 #[cfg(target_os = "linux")]
254 {
255 if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
256 for line in meminfo.lines() {
257 if line.starts_with("MemTotal:") {
258 if let Some(kb_str) = line.split_whitespace().nth(1) {
259 if let Ok(kb) = kb_str.parse::<u64>() {
260 memory_info.total_memory_bytes = kb * 1024;
261 }
262 }
263 } else if line.starts_with("MemAvailable:") {
264 if let Some(kb_str) = line.split_whitespace().nth(1) {
265 if let Ok(kb) = kb_str.parse::<u64>() {
266 memory_info.available_memory_bytes = kb * 1024;
267 }
268 }
269 }
270 }
271 }
272 }
273
274 #[cfg(target_os = "macos")]
275 {
276 if let Ok(output) = std::process::Command::new("sysctl")
278 .arg("-n")
279 .arg("hw.memsize")
280 .output()
281 {
282 if let Ok(mem_str) = String::from_utf8(output.stdout) {
283 if let Ok(mem_bytes) = mem_str.trim().parse::<u64>() {
284 memory_info.total_memory_bytes = mem_bytes;
285 memory_info.available_memory_bytes = mem_bytes / 2; }
287 }
288 }
289 }
290
291 if memory_info.total_memory_bytes == 0 {
293 memory_info.total_memory_bytes = 8 * 1024 * 1024 * 1024; memory_info.available_memory_bytes = 4 * 1024 * 1024 * 1024; }
296 }
297
298 #[cfg(unix)]
300 {
301 unsafe {
302 let page_size = libc::sysconf(libc::_SC_PAGESIZE);
303 if page_size > 0 {
304 memory_info.page_size_bytes = page_size as usize;
305 }
306 }
307 }
308
309 memory_info
310 }
311
312 fn detect_cache_info() -> CacheInfo {
314 let mut cache_info = CacheInfo::default();
315
316 #[cfg(target_os = "linux")]
318 {
319 Self::detect_linux_cache_info(&mut cache_info);
320 }
321
322 #[cfg(target_os = "macos")]
323 {
324 Self::detect_macos_cache_info(&mut cache_info);
325 }
326
327 #[cfg(target_os = "windows")]
328 {
329 Self::detect_windows_cache_info(&mut cache_info);
330 }
331
332 cache_info
333 }
334
335 #[cfg(target_os = "linux")]
336 fn detect_linux_cache_info(cache_info: &mut CacheInfo) {
337 if let Ok(l1d) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index0/size") {
339 if let Ok(size) = l1d.trim().strip_suffix("K").unwrap_or(&l1d).parse::<u32>() {
340 cache_info.l1_data_cache_kb = Some(size);
341 }
342 }
343
344 if let Ok(l2) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index2/size") {
345 if let Ok(size) = l2.trim().strip_suffix("K").unwrap_or(&l2).parse::<u32>() {
346 cache_info.l2_cache_kb = Some(size);
347 }
348 }
349
350 if let Ok(l3) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index3/size") {
351 if let Ok(size) = l3.trim().strip_suffix("K").unwrap_or(&l3).parse::<u32>() {
352 cache_info.l3_cache_kb = Some(size);
353 }
354 }
355
356 cache_info.cache_line_size_bytes = Some(64);
358 }
359
360 #[cfg(target_os = "macos")]
361 fn detect_macos_cache_info(cache_info: &mut CacheInfo) {
362 cache_info.cache_line_size_bytes = Some(64); }
365
366 #[cfg(target_os = "windows")]
367 fn detect_windows_cache_info(cache_info: &mut CacheInfo) {
368 cache_info.cache_line_size_bytes = Some(64); }
371
372 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
373 fn detect_linux_cache_info(_cache_info: &mut CacheInfo) {}
374
375 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
376 fn detect_macos_cache_info(_cache_info: &mut CacheInfo) {}
377
378 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
379 fn detect_windows_cache_info(_cache_info: &mut CacheInfo) {}
380}
381
382impl Default for HardwareDetector {
383 fn default() -> Self {
384 Self::new()
385 }
386}
387
388#[derive(Debug, Clone)]
390pub struct OSInfo {
391 pub name: String,
392 pub version: String,
393 pub arch: String,
394 pub kernel_version: Option<String>,
395 pub is_64bit: bool,
396}
397
398impl OSInfo {
399 pub fn detect() -> Self {
401 Self {
402 name: std::env::consts::OS.to_string(),
403 version: Self::detect_os_version(),
404 arch: std::env::consts::ARCH.to_string(),
405 kernel_version: Self::detect_kernel_version(),
406 is_64bit: cfg!(target_pointer_width = "64"),
407 }
408 }
409
410 pub fn is_linux(&self) -> bool {
412 self.name == "linux"
413 }
414
415 pub fn is_macos(&self) -> bool {
417 self.name == "macos"
418 }
419
420 pub fn is_windows(&self) -> bool {
422 self.name == "windows"
423 }
424
425 pub fn is_unix(&self) -> bool {
427 cfg!(unix)
428 }
429
430 fn detect_os_version() -> String {
431 #[cfg(target_os = "linux")]
432 {
433 if let Ok(version) = std::fs::read_to_string("/proc/version") {
434 return version.lines().next().unwrap_or("unknown").to_string();
435 }
436 }
437
438 #[cfg(target_os = "macos")]
439 {
440 "macOS".to_string()
442 }
443 #[cfg(target_os = "windows")]
444 {
445 "Windows".to_string()
447 }
448 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
449 {
450 "unknown".to_string()
451 }
452 }
453
454 fn detect_kernel_version() -> Option<String> {
455 #[cfg(unix)]
456 {
457 if let Ok(output) = std::process::Command::new("uname").arg("-r").output() {
458 if let Ok(version) = String::from_utf8(output.stdout) {
459 return Some(version.trim().to_string());
460 }
461 }
462 }
463 None
464 }
465}
466
467#[derive(Debug, Clone)]
469pub struct RuntimeInfo {
470 pub rust_version: String,
471 pub compiler: String,
472 pub target_triple: String,
473 pub build_profile: String,
474 pub features: Vec<String>,
475}
476
477impl RuntimeInfo {
478 pub fn detect() -> Self {
480 let target_family = std::env::var("CARGO_CFG_TARGET_FAMILY")
481 .unwrap_or_else(|_| std::env::consts::FAMILY.to_string());
482 let target_triple = std::env::var("CARGO_CFG_TARGET_TRIPLE").unwrap_or_else(|_| {
483 format!(
484 "{}-{}-{}",
485 std::env::consts::ARCH,
486 std::env::consts::FAMILY,
487 std::env::consts::OS
488 )
489 });
490
491 Self {
492 rust_version: option_env!("CARGO_PKG_RUST_VERSION")
493 .unwrap_or("unknown")
494 .to_string(),
495 compiler: format!("{target_family} {}", rustc_version_runtime::version()),
496 target_triple,
497 build_profile: if cfg!(debug_assertions) {
498 "debug"
499 } else {
500 "release"
501 }
502 .to_string(),
503 features: Self::detect_features(),
504 }
505 }
506
507 pub fn is_debug(&self) -> bool {
509 self.build_profile == "debug"
510 }
511
512 pub fn is_release(&self) -> bool {
514 self.build_profile == "release"
515 }
516
517 fn detect_features() -> Vec<String> {
518 Vec::new()
530 }
531}
532
533#[derive(Debug, Clone)]
535pub struct PerformanceCharacteristics {
536 pub memory_bandwidth_gbps: f64,
537 pub l1_cache_latency_ns: f64,
538 pub l2_cache_latency_ns: f64,
539 pub l3_cache_latency_ns: f64,
540 pub memory_latency_ns: f64,
541 pub cpu_frequency_estimation_mhz: f64,
542 pub single_thread_performance_score: f64,
543 pub multi_thread_performance_score: f64,
544}
545
546impl PerformanceCharacteristics {
547 pub fn measure() -> Self {
549 let _start_time = Instant::now();
550
551 let memory_bandwidth = Self::measure_memory_bandwidth();
552 let cache_latencies = Self::measure_cache_latencies();
553 let cpu_freq = Self::estimate_cpu_frequency();
554 let single_perf = Self::measure_single_thread_performance();
555 let multi_perf = Self::measure_multi_thread_performance();
556
557 Self {
558 memory_bandwidth_gbps: memory_bandwidth,
559 l1_cache_latency_ns: cache_latencies.0,
560 l2_cache_latency_ns: cache_latencies.1,
561 l3_cache_latency_ns: cache_latencies.2,
562 memory_latency_ns: cache_latencies.3,
563 cpu_frequency_estimation_mhz: cpu_freq,
564 single_thread_performance_score: single_perf,
565 multi_thread_performance_score: multi_perf,
566 }
567 }
568
569 fn measure_memory_bandwidth() -> f64 {
570 const SIZE: usize = 16 * 1024 * 1024; let mut data = vec![0u64; SIZE / 8];
573
574 let start = Instant::now();
575
576 for (i, item) in data.iter_mut().enumerate() {
578 *item = i as u64;
579 }
580
581 let mut sum = 0u64;
583 for &val in &data {
584 sum = sum.wrapping_add(val);
585 }
586
587 let elapsed = start.elapsed();
588 let bytes_processed = (SIZE * 2) as f64; let bandwidth_bps = bytes_processed / elapsed.as_secs_f64();
590
591 std::hint::black_box(sum);
593
594 bandwidth_bps / 1e9 }
596
597 fn measure_cache_latencies() -> (f64, f64, f64, f64) {
598 (1.0, 3.0, 10.0, 100.0) }
602
603 fn estimate_cpu_frequency() -> f64 {
604 let iterations = 10_000_000;
606 let start = Instant::now();
607
608 let mut counter = 0u64;
609 for _ in 0..iterations {
610 counter = counter.wrapping_add(1);
611 }
612
613 let elapsed = start.elapsed();
614 std::hint::black_box(counter);
615
616 let cycles_per_second = iterations as f64 / elapsed.as_secs_f64();
618 cycles_per_second / 1e6 }
620
621 fn measure_single_thread_performance() -> f64 {
622 let start = Instant::now();
624 let mut sum = 0.0;
625
626 for i in 0..1_000_000 {
627 sum += (i as f64).sqrt().sin().cos();
628 }
629
630 let elapsed = start.elapsed();
631 std::hint::black_box(sum);
632
633 1_000_000.0 / elapsed.as_secs_f64()
635 }
636
637 fn measure_multi_thread_performance() -> f64 {
638 use std::thread;
639
640 let num_threads = num_cpus::get();
641 let start = Instant::now();
642
643 let handles: Vec<_> = (0..num_threads)
644 .map(|_| {
645 thread::spawn(|| {
646 let mut sum = 0.0;
647 for i in 0..100_000 {
648 sum += (i as f64).sqrt().sin().cos();
649 }
650 sum
651 })
652 })
653 .collect();
654
655 let results: Vec<_> = handles
656 .into_iter()
657 .map(|h| h.join().expect("operation should succeed"))
658 .collect();
659 let elapsed = start.elapsed();
660
661 std::hint::black_box(results);
662
663 (num_threads * 100_000) as f64 / elapsed.as_secs_f64()
665 }
666}
667
668#[derive(Debug, Clone)]
670pub struct FeatureChecker {
671 hardware: HardwareDetector,
672 os_info: OSInfo,
673 #[allow(dead_code)]
674 runtime: RuntimeInfo,
675}
676
677impl FeatureChecker {
678 pub fn new() -> Self {
680 Self {
681 hardware: HardwareDetector::new(),
682 os_info: OSInfo::detect(),
683 runtime: RuntimeInfo::detect(),
684 }
685 }
686
687 pub fn simd_available(&self) -> bool {
689 self.hardware.has_sse42() || self.hardware.has_avx2() || self.hardware.has_neon()
690 }
691
692 pub fn parallel_beneficial(&self) -> bool {
694 self.hardware.logical_cores() > 1
695 }
696
697 pub fn memory_mapping_available(&self) -> bool {
699 self.os_info.is_unix() || self.os_info.is_windows()
700 }
701
702 pub fn high_resolution_timer_available(&self) -> bool {
704 true }
706
707 pub fn recommended_thread_count(&self) -> usize {
709 self.hardware.logical_cores().min(32)
711 }
712
713 pub fn recommended_simd_width(&self) -> usize {
715 if self.hardware.has_avx512() {
716 16 } else if self.hardware.has_avx2() {
718 8 } else if self.hardware.has_sse42() || self.hardware.has_neon() {
720 4 } else {
722 1 }
724 }
725
726 pub fn optimization_recommended(&self, optimization: &str) -> bool {
728 match optimization {
729 "simd" => self.simd_available(),
730 "parallel" => self.parallel_beneficial(),
731 "cache_friendly" => self.hardware.l3_cache_size_kb().is_some(),
732 "memory_pool" => self.hardware.total_memory() > 1_000_000_000, _ => false,
734 }
735 }
736
737 pub fn get_recommendations(&self) -> Vec<String> {
739 let mut recommendations = Vec::new();
740
741 if self.simd_available() {
742 recommendations.push("Enable SIMD optimizations".to_string());
743 }
744
745 if self.parallel_beneficial() {
746 recommendations.push(format!(
747 "Use parallel processing with {} threads",
748 self.recommended_thread_count()
749 ));
750 }
751
752 if let Some(cache_size) = self.hardware.l3_cache_size_kb() {
753 recommendations.push(format!("Optimize for {cache_size}KB L3 cache"));
754 }
755
756 if self.hardware.total_memory() < 1_000_000_000 {
757 recommendations.push("Consider memory usage optimizations".to_string());
758 }
759
760 recommendations
761 }
762}
763
764impl Default for FeatureChecker {
765 fn default() -> Self {
766 Self::new()
767 }
768}
769
770static GLOBAL_ENV_INFO: OnceLock<EnvironmentInfo> = OnceLock::new();
772
773#[derive(Debug, Clone)]
775pub struct EnvironmentInfo {
776 pub hardware: HardwareDetector,
777 pub os_info: OSInfo,
778 pub runtime: RuntimeInfo,
779 pub performance: PerformanceCharacteristics,
780 pub features: FeatureChecker,
781}
782
783impl EnvironmentInfo {
784 pub fn global() -> &'static EnvironmentInfo {
786 GLOBAL_ENV_INFO.get_or_init(EnvironmentInfo::detect)
787 }
788
789 pub fn detect() -> Self {
791 let hardware = HardwareDetector::new();
792 let os_info = OSInfo::detect();
793 let runtime = RuntimeInfo::detect();
794 let performance = PerformanceCharacteristics::measure();
795 let features = FeatureChecker::new();
796
797 Self {
798 hardware,
799 os_info,
800 runtime,
801 performance,
802 features,
803 }
804 }
805
806 pub fn summary(&self) -> String {
808 format!(
809 "Environment Summary:\n\
810 OS: {} {} ({})\n\
811 CPU: {} cores ({} logical), {}\n\
812 Memory: {:.1} GB total, {:.1} GB available\n\
813 Runtime: {} {}\n\
814 Features: SIMD={}, Parallel={}, Cache={}KB",
815 self.os_info.name,
816 self.os_info.version,
817 self.os_info.arch,
818 self.hardware.cpu_cores(),
819 self.hardware.logical_cores(),
820 self.hardware.cpu_architecture(),
821 self.hardware.total_memory() as f64 / 1e9,
822 self.hardware.available_memory() as f64 / 1e9,
823 self.runtime.compiler,
824 self.runtime.build_profile,
825 self.features.simd_available(),
826 self.features.parallel_beneficial(),
827 self.hardware.l3_cache_size_kb().unwrap_or(0)
828 )
829 }
830}
831
832#[allow(non_snake_case)]
833#[cfg(test)]
834mod tests {
835 use super::*;
836
837 #[test]
838 fn test_hardware_detector() {
839 let hw = HardwareDetector::new();
840
841 assert!(hw.cpu_cores() >= 1);
842 assert!(hw.logical_cores() >= 1); assert!(!hw.cpu_architecture().is_empty());
844 assert!(hw.total_memory() > 0);
845 }
846
847 #[test]
848 fn test_os_info() {
849 let os = OSInfo::detect();
850
851 assert!(!os.name.is_empty());
852 assert!(!os.arch.is_empty());
853
854 assert!(os.is_linux() || os.is_macos() || os.is_windows() || !os.name.is_empty());
856 }
857
858 #[test]
859 fn test_runtime_info() {
860 let runtime = RuntimeInfo::detect();
861
862 assert!(!runtime.compiler.is_empty());
863 assert!(!runtime.target_triple.is_empty());
864 assert!(runtime.is_debug() || runtime.is_release());
865 }
866
867 #[test]
868 fn test_performance_characteristics() {
869 let perf = PerformanceCharacteristics::measure();
870
871 assert!(perf.memory_bandwidth_gbps > 0.0);
872 assert!(perf.cpu_frequency_estimation_mhz > 0.0);
873 assert!(perf.single_thread_performance_score > 0.0);
874 assert!(perf.multi_thread_performance_score > 0.0);
875 }
876
877 #[test]
878 fn test_feature_checker() {
879 let checker = FeatureChecker::new();
880
881 assert!(checker.recommended_thread_count() >= 1);
882 assert!(checker.recommended_simd_width() >= 1);
883 assert!(checker.high_resolution_timer_available());
884 }
885
886 #[test]
887 fn test_environment_info() {
888 let env = EnvironmentInfo::detect();
889 let summary = env.summary();
890
891 assert!(!summary.is_empty());
892 assert!(summary.contains("Environment Summary"));
893
894 let global_env = EnvironmentInfo::global();
896 assert!(!global_env.summary().is_empty());
897 }
898
899 #[test]
900 fn test_cpu_features() {
901 let hw = HardwareDetector::new();
902
903 let _has_avx2 = hw.has_avx2();
905 let _has_sse42 = hw.has_sse42();
906 let _has_neon = hw.has_neon();
907 let _is_arm = hw.is_arm();
908 let _is_x86 = hw.is_x86();
909 }
910
911 #[test]
912 fn test_optimization_recommendations() {
913 let checker = FeatureChecker::new();
914 let recommendations = checker.get_recommendations();
915
916 assert!(!recommendations.is_empty());
918
919 let _simd_rec = checker.optimization_recommended("simd");
921 let _parallel_rec = checker.optimization_recommended("parallel");
922 let _cache_rec = checker.optimization_recommended("cache_friendly");
923 }
924}