1use std::sync::OnceLock;
7
8#[derive(Debug, Clone)]
10pub struct PlatformCapabilities {
11 pub simd_available: bool,
13 pub gpu_available: bool,
15 pub cuda_available: bool,
17 pub opencl_available: bool,
19 pub metal_available: bool,
21 pub avx2_available: bool,
23 pub avx512_available: bool,
25 pub neon_available: bool,
27 pub cpu_cores: usize,
29 pub arch: String,
31 pub os: String,
33}
34
35static CAPABILITIES: OnceLock<PlatformCapabilities> = OnceLock::new();
37
38impl PlatformCapabilities {
39 pub fn detect() -> &'static PlatformCapabilities {
41 CAPABILITIES.get_or_init(|| {
42 let mut caps = PlatformCapabilities {
43 simd_available: false,
44 gpu_available: false,
45 cuda_available: false,
46 opencl_available: false,
47 metal_available: false,
48 avx2_available: false,
49 avx512_available: false,
50 neon_available: false,
51 cpu_cores: std::thread::available_parallelism()
52 .map(|n| n.get())
53 .unwrap_or(1),
54 arch: std::env::consts::ARCH.to_string(),
55 os: std::env::consts::OS.to_string(),
56 };
57
58 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
60 {
61 caps.simd_available = is_x86_feature_detected!("sse2");
62 caps.avx2_available = is_x86_feature_detected!("avx2");
63 caps.avx512_available = is_x86_feature_detected!("avx512f");
64 }
65
66 #[cfg(target_arch = "aarch64")]
67 {
68 caps.simd_available = true; caps.neon_available = true;
70 }
71
72 caps.gpu_available = Self::detect_gpu();
74
75 #[cfg(feature = "cuda")]
77 {
78 caps.cuda_available = Self::detect_cuda();
79 }
80
81 #[cfg(feature = "opencl")]
83 {
84 caps.opencl_available = Self::detect_opencl();
85 }
86
87 #[cfg(all(target_os = "macos", feature = "metal"))]
89 {
90 caps.metal_available = Self::detect_metal();
91 }
92
93 caps
94 })
95 }
96
97 pub fn summary(&self) -> String {
99 let mut features = Vec::new();
100
101 if self.simd_available {
102 features.push("SIMD");
103
104 if self.avx2_available {
105 features.push("AVX2");
106 }
107 if self.avx512_available {
108 features.push("AVX512");
109 }
110 if self.neon_available {
111 features.push("NEON");
112 }
113 }
114
115 if self.gpu_available {
116 features.push("GPU");
117
118 if self.cuda_available {
119 features.push("CUDA");
120 }
121 if self.opencl_available {
122 features.push("OpenCL");
123 }
124 if self.metal_available {
125 features.push("Metal");
126 }
127 }
128
129 format!(
130 "{} ({} cores, {})",
131 features.join(", "),
132 self.cpu_cores,
133 self.arch
134 )
135 }
136
137 fn detect_gpu() -> bool {
139 std::env::var("CUDA_VISIBLE_DEVICES").is_ok()
141 || std::env::var("GPU_DEVICE_ORDINAL").is_ok()
142 || std::env::var("ROCR_VISIBLE_DEVICES").is_ok()
143 }
144
145 #[cfg(feature = "cuda")]
147 fn detect_cuda() -> bool {
148 std::env::var("CUDA_PATH").is_ok()
150 || std::path::Path::new("/usr/local/cuda").exists()
151 || std::path::Path::new("C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA")
152 .exists()
153 }
154
155 #[cfg(not(feature = "cuda"))]
156 #[allow(dead_code)]
157 fn detect_cuda() -> bool {
158 false
159 }
160
161 #[cfg(feature = "opencl")]
163 #[allow(dead_code)]
164 fn detect_opencl() -> bool {
165 #[cfg(target_os = "linux")]
167 {
168 std::path::Path::new("/usr/lib/libOpenCL.so").exists()
169 || std::path::Path::new("/usr/lib64/libOpenCL.so").exists()
170 }
171 #[cfg(target_os = "windows")]
172 {
173 std::path::Path::new("C:\\Windows\\System32\\OpenCL.dll").exists()
174 }
175 #[cfg(target_os = "macos")]
176 {
177 true }
179 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
180 {
181 false
182 }
183 }
184
185 #[cfg(not(feature = "opencl"))]
186 #[allow(dead_code)]
187 fn detect_opencl() -> bool {
188 false
189 }
190
191 #[cfg(all(target_os = "macos", feature = "metal"))]
193 #[allow(dead_code)]
194 fn detect_metal() -> bool {
195 true
197 }
198
199 #[cfg(not(all(target_os = "macos", feature = "metal")))]
200 #[allow(dead_code)]
201 fn detect_metal() -> bool {
202 false
203 }
204}
205
206pub struct AutoOptimizer {
208 capabilities: &'static PlatformCapabilities,
209}
210
211impl AutoOptimizer {
212 pub fn new() -> Self {
214 Self {
215 capabilities: PlatformCapabilities::detect(),
216 }
217 }
218
219 pub fn should_use_gpu(&self, problem_size: usize) -> bool {
221 self.capabilities.gpu_available && problem_size > 100_000
223 }
224
225 pub fn should_use_simd(&self, problem_size: usize) -> bool {
227 self.capabilities.simd_available && problem_size > 1000
229 }
230
231 pub fn should_use_parallel(&self, problem_size: usize) -> bool {
233 self.capabilities.cpu_cores > 1 && problem_size > 10_000
235 }
236
237 pub fn recommended_chunk_size(&self, total_size: usize) -> usize {
239 let ideal_chunks = self.capabilities.cpu_cores * 4;
241 let chunk_size = total_size / ideal_chunks;
242
243 chunk_size.clamp(1000, 100_000)
245 }
246}
247
248impl Default for AutoOptimizer {
249 fn default() -> Self {
250 Self::new()
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_platform_detection() {
260 let caps = PlatformCapabilities::detect();
261
262 assert!(caps.cpu_cores >= 1);
264
265 assert!(!caps.arch.is_empty());
267
268 assert!(!caps.os.is_empty());
270
271 println!("Platform capabilities: {}", caps.summary());
272 }
273
274 #[test]
275 fn test_auto_optimizer() {
276 let optimizer = AutoOptimizer::new();
277
278 assert!(!optimizer.should_use_gpu(100));
280
281 let _ = optimizer.should_use_simd(5000);
283
284 let chunk_size = optimizer.recommended_chunk_size(1_000_000);
286 assert!(chunk_size > 0);
287 }
288}