1#![allow(dead_code)]
43use crate::error_handling::AutogradResult;
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
47pub enum SimdCapability {
48 None,
50 SSE41,
52 AVX2,
54 AVX512,
56 NEON,
58}
59
60impl SimdCapability {
61 pub fn name(&self) -> &'static str {
63 match self {
64 Self::None => "Scalar",
65 Self::SSE41 => "SSE4.1",
66 Self::AVX2 => "AVX2",
67 Self::AVX512 => "AVX-512",
68 Self::NEON => "NEON",
69 }
70 }
71
72 pub fn speedup_factor(&self) -> f32 {
74 match self {
75 Self::None => 1.0,
76 Self::SSE41 => 2.0,
77 Self::AVX2 => 4.0,
78 Self::AVX512 => 8.0,
79 Self::NEON => 2.0,
80 }
81 }
82
83 #[allow(unreachable_code)]
85 pub fn detect() -> Self {
86 #[cfg(all(target_arch = "x86_64", feature = "simd"))]
87 {
88 if is_x86_feature_detected!("avx512f") {
89 return Self::AVX512;
90 }
91 if is_x86_feature_detected!("avx2") {
92 return Self::AVX2;
93 }
94 if is_x86_feature_detected!("sse4.1") {
95 return Self::SSE41;
96 }
97 }
98
99 #[cfg(all(target_arch = "aarch64", feature = "simd"))]
100 {
101 return Self::NEON;
103 }
104
105 Self::None
106 }
107}
108
109#[derive(Debug, Clone)]
111pub struct SimdConfig {
112 pub preferred_capability: Option<SimdCapability>,
114 pub min_simd_size: usize,
116 pub enable_alignment: bool,
118 pub alignment_bytes: usize,
120}
121
122impl Default for SimdConfig {
123 fn default() -> Self {
124 Self {
125 preferred_capability: None,
126 min_simd_size: 64,
127 enable_alignment: true,
128 alignment_bytes: 32, }
130 }
131}
132
133impl SimdConfig {
134 pub fn new() -> Self {
136 Self::default()
137 }
138
139 pub fn with_capability(mut self, capability: SimdCapability) -> Self {
141 self.preferred_capability = Some(capability);
142 self
143 }
144
145 pub fn with_min_simd_size(mut self, min_size: usize) -> Self {
147 self.min_simd_size = min_size;
148 self
149 }
150
151 pub fn with_alignment(mut self, enabled: bool) -> Self {
153 self.enable_alignment = enabled;
154 self
155 }
156
157 pub fn with_alignment_bytes(mut self, bytes: usize) -> Self {
159 self.alignment_bytes = bytes;
160 self
161 }
162}
163
164#[derive(Debug, Clone, Default)]
166pub struct SimdStats {
167 pub total_ops: usize,
169 pub total_time_ms: f64,
171 pub avg_speedup: f64,
173 pub aligned_ops: usize,
175 pub unaligned_ops: usize,
177}
178
179pub struct SimdGradientComputer {
181 config: SimdConfig,
182 capability: SimdCapability,
183 stats: SimdStats,
184}
185
186impl SimdGradientComputer {
187 pub fn new() -> Self {
189 let config = SimdConfig::default();
190 let capability = SimdCapability::detect();
191 Self {
192 config,
193 capability,
194 stats: SimdStats::default(),
195 }
196 }
197
198 pub fn with_config(config: SimdConfig) -> Self {
200 let capability = config
201 .preferred_capability
202 .unwrap_or_else(SimdCapability::detect);
203 Self {
204 config,
205 capability,
206 stats: SimdStats::default(),
207 }
208 }
209
210 pub fn config(&self) -> &SimdConfig {
212 &self.config
213 }
214
215 pub fn capability(&self) -> SimdCapability {
217 self.capability
218 }
219
220 pub fn detect_capability(&self) -> SimdCapability {
222 SimdCapability::detect()
223 }
224
225 pub fn stats(&self) -> &SimdStats {
227 &self.stats
228 }
229
230 pub fn reset_stats(&mut self) {
232 self.stats = SimdStats::default();
233 }
234
235 pub fn should_use_simd(&self, tensor_size: usize) -> bool {
237 self.capability != SimdCapability::None && tensor_size >= self.config.min_simd_size
238 }
239
240 pub fn is_aligned<T>(&self, data: &[T]) -> bool {
242 let ptr = data.as_ptr() as usize;
243 ptr % self.config.alignment_bytes == 0
244 }
245
246 #[cfg(feature = "simd")]
247 pub fn compute_simd<T>(&mut self, data: &[T]) -> AutogradResult<Vec<T>>
252 where
253 T: Clone + Copy + Send + Sync,
254 {
255 use std::time::Instant;
256
257 if !self.should_use_simd(data.len()) {
258 tracing::debug!(
259 "Tensor too small for SIMD ({} elements), using scalar fallback",
260 data.len()
261 );
262 return Ok(data.to_vec());
263 }
264
265 let start = Instant::now();
266 let is_aligned = self.is_aligned(data);
267
268 if is_aligned {
269 self.stats.aligned_ops += 1;
270 } else {
271 self.stats.unaligned_ops += 1;
272 tracing::debug!("Data is not aligned, SIMD performance may be reduced");
273 }
274
275 let result = data.to_vec(); let elapsed = start.elapsed().as_secs_f64() * 1000.0;
290 self.stats.total_ops += 1;
291 self.stats.total_time_ms += elapsed;
292
293 Ok(result)
294 }
295
296 #[cfg(not(feature = "simd"))]
297 pub fn compute_simd<T>(&mut self, data: &[T]) -> AutogradResult<Vec<T>>
299 where
300 T: Clone,
301 {
302 tracing::warn!("SIMD feature not enabled, using scalar fallback");
303 Ok(data.to_vec())
304 }
305
306 #[cfg(feature = "simd")]
307 pub fn simd_element_wise<T, F>(&mut self, data: &[T], scalar_op: F) -> AutogradResult<Vec<T>>
312 where
313 T: Clone + Copy + Send + Sync,
314 F: Fn(T) -> T,
315 {
316 if !self.should_use_simd(data.len()) {
317 return Ok(data.iter().map(|&x| scalar_op(x)).collect());
318 }
319
320 Ok(data.iter().map(|&x| scalar_op(x)).collect())
323 }
324
325 #[cfg(not(feature = "simd"))]
326 pub fn simd_element_wise<T, F>(&mut self, data: &[T], scalar_op: F) -> AutogradResult<Vec<T>>
328 where
329 T: Clone + Copy,
330 F: Fn(T) -> T,
331 {
332 Ok(data.iter().map(|&x| scalar_op(x)).collect())
333 }
334
335 pub fn report_performance(&self) -> String {
337 format!(
338 "SIMD Gradient Computation Statistics:\n\
339 - Capability: {} ({:.1}x theoretical speedup)\n\
340 - Total operations: {}\n\
341 - Total time: {:.2}ms\n\
342 - Aligned operations: {}\n\
343 - Unaligned operations: {}\n\
344 - Average speedup: {:.2}x\n\
345 - Average time per op: {:.2}ms",
346 self.capability.name(),
347 self.capability.speedup_factor(),
348 self.stats.total_ops,
349 self.stats.total_time_ms,
350 self.stats.aligned_ops,
351 self.stats.unaligned_ops,
352 self.stats.avg_speedup,
353 if self.stats.total_ops > 0 {
354 self.stats.total_time_ms / self.stats.total_ops as f64
355 } else {
356 0.0
357 }
358 )
359 }
360}
361
362impl Default for SimdGradientComputer {
363 fn default() -> Self {
364 Self::new()
365 }
366}
367
368static GLOBAL_SIMD_COMPUTER: once_cell::sync::Lazy<parking_lot::RwLock<SimdGradientComputer>> =
370 once_cell::sync::Lazy::new(|| parking_lot::RwLock::new(SimdGradientComputer::new()));
371
372pub fn get_global_simd_computer() -> parking_lot::RwLockReadGuard<'static, SimdGradientComputer> {
374 GLOBAL_SIMD_COMPUTER.read()
375}
376
377pub fn get_global_simd_computer_mut() -> parking_lot::RwLockWriteGuard<'static, SimdGradientComputer>
379{
380 GLOBAL_SIMD_COMPUTER.write()
381}
382
383pub fn configure_global_simd(config: SimdConfig) {
385 let mut computer = GLOBAL_SIMD_COMPUTER.write();
386 *computer = SimdGradientComputer::with_config(config);
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn test_simd_capability_detection() {
395 let capability = SimdCapability::detect();
396 println!("Detected SIMD capability: {:?}", capability);
397 }
399
400 #[test]
401 fn test_simd_capability_names() {
402 assert_eq!(SimdCapability::None.name(), "Scalar");
403 assert_eq!(SimdCapability::SSE41.name(), "SSE4.1");
404 assert_eq!(SimdCapability::AVX2.name(), "AVX2");
405 assert_eq!(SimdCapability::NEON.name(), "NEON");
406 }
407
408 #[test]
409 fn test_simd_config() {
410 let config = SimdConfig::new()
411 .with_capability(SimdCapability::AVX2)
412 .with_min_simd_size(128)
413 .with_alignment_bytes(32);
414
415 assert_eq!(config.preferred_capability, Some(SimdCapability::AVX2));
416 assert_eq!(config.min_simd_size, 128);
417 assert_eq!(config.alignment_bytes, 32);
418 }
419
420 #[test]
421 fn test_simd_computer_creation() {
422 let computer = SimdGradientComputer::new();
423 println!(
424 "Created SIMD computer with capability: {:?}",
425 computer.capability()
426 );
427 }
428
429 #[test]
430 fn test_should_use_simd() {
431 let computer = SimdGradientComputer::new();
432
433 assert!(!computer.should_use_simd(32));
435
436 let should_use = computer.should_use_simd(1000);
438 assert_eq!(should_use, computer.capability() != SimdCapability::None);
439 }
440
441 #[test]
442 fn test_is_aligned() {
443 let computer = SimdGradientComputer::new();
444 let data = vec![1.0f32; 100];
445
446 let _ = computer.is_aligned(&data);
448 }
449
450 #[test]
451 fn test_report_performance() {
452 let computer = SimdGradientComputer::new();
453 let report = computer.report_performance();
454
455 assert!(report.contains("SIMD Gradient Computation Statistics"));
456 assert!(report.contains("Capability:"));
457 }
458
459 #[test]
460 fn test_global_simd_computer() {
461 let config = SimdConfig::new().with_min_simd_size(256);
462 configure_global_simd(config);
463
464 let computer = get_global_simd_computer();
465 assert_eq!(computer.config().min_simd_size, 256);
466 }
467}