1use crate::error::{FFTError, FFTResult};
8use crate::fft::{fft, ifft};
9#[cfg(feature = "oxifft")]
10use crate::oxifft_plan_cache;
11#[cfg(feature = "oxifft")]
12use oxifft::{Complex as OxiComplex, Direction};
13use scirs2_core::ndarray::{Array, ArrayBase, Data};
14use scirs2_core::numeric::Complex64;
15use scirs2_core::numeric::NumCast;
16use std::collections::HashMap;
17use std::fmt::Debug;
18use std::sync::atomic::{AtomicUsize, Ordering};
19use std::sync::{Arc, Mutex};
20use std::time::{Duration, Instant};
21
22#[cfg(feature = "simd")]
24use scirs2_core::simd_ops::{
25 simd_add_f32_adaptive, simd_dot_f32_ultra, simd_fma_f32_ultra, simd_mul_f32_hyperoptimized,
26 PlatformCapabilities, SimdUnifiedOps,
27};
28
29#[cfg(feature = "parallel")]
30use scirs2_core::parallel_ops::*;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub enum OptimizationLevel {
35 Default,
37 Maximum,
39 Performance,
41 SizeSpecific,
43 Simd,
45 CacheEfficient,
47 Basic,
49 Balanced,
51 Auto,
53 TlbOptimized,
55 UltraOptimized,
57}
58
59#[derive(Debug, Clone)]
61pub struct PerformanceMetrics {
62 pub algorithm: String,
64
65 pub size: usize,
67
68 pub duration: Duration,
70
71 pub mflops: f64,
73
74 pub optimization_level: OptimizationLevel,
76}
77
78#[derive(Debug, Clone)]
80pub struct OptimizedConfig {
81 pub optimization_level: OptimizationLevel,
83 pub threads: Option<usize>,
85 pub use_simd: bool,
87 pub vectorized: bool,
89 pub collect_metrics: bool,
91 pub max_fft_size: usize,
93 pub enable_inplace: bool,
95 pub enable_multithreading: bool,
97 pub cache_line_size: usize,
99 pub l1_cache_size: usize,
101 pub l2_cache_size: usize,
103}
104
105impl Default for OptimizedConfig {
106 fn default() -> Self {
107 Self {
108 optimization_level: OptimizationLevel::Default,
109 threads: None,
110 use_simd: true,
111 vectorized: true,
112 collect_metrics: false,
113 max_fft_size: 1024, enable_inplace: true,
115 enable_multithreading: true,
116 cache_line_size: 64, l1_cache_size: 32 * 1024, l2_cache_size: 256 * 1024, }
120 }
121}
122
123pub struct OptimizedFFT {
125 config: OptimizedConfig,
127 stats: PerformanceStats,
129 collect_stats: bool,
131 #[allow(dead_code)]
133 metrics: Arc<Mutex<HashMap<(usize, OptimizationLevel), PerformanceMetrics>>>,
134
135 #[allow(dead_code)]
137 total_ffts: AtomicUsize,
138}
139
140#[derive(Debug, Default, Clone)]
142pub struct PerformanceStats {
143 pub operation_count: usize,
145 pub total_time_ns: u64,
147 pub max_time_ns: u64,
149 pub min_time_ns: u64,
151 pub total_flops: u64,
153}
154
155impl PerformanceStats {
156 pub fn avg_time_ns(&self) -> u64 {
158 if self.operation_count == 0 {
159 0
160 } else {
161 self.total_time_ns / self.operation_count as u64
162 }
163 }
164
165 pub fn avg_flops(&self) -> f64 {
167 if self.total_time_ns == 0 {
168 0.0
169 } else {
170 self.total_flops as f64 / (self.total_time_ns as f64 / 1_000_000_000.0)
171 }
172 }
173
174 pub fn reset(&mut self) {
176 *self = PerformanceStats::default();
177 }
178}
179
180impl OptimizedFFT {
181 pub fn new(config: OptimizedConfig) -> Self {
183 Self {
184 config,
185 stats: PerformanceStats::default(),
186 collect_stats: false,
187 metrics: Arc::new(Mutex::new(HashMap::new())),
188 total_ffts: AtomicUsize::new(0),
189 }
190 }
191
192 pub fn set_collect_stats(&mut self, enable: bool) {
194 self.collect_stats = enable;
195 }
196
197 pub fn get_stats(&self) -> &PerformanceStats {
199 &self.stats
200 }
201
202 pub fn reset_stats(&mut self) {
204 self.stats.reset();
205 }
206
207 pub fn get_metrics(&self, size: usize, level: OptimizationLevel) -> Option<PerformanceMetrics> {
209 if let Ok(db) = self.metrics.lock() {
210 db.get(&(size, level)).cloned()
211 } else {
212 None
213 }
214 }
215
216 pub fn get_all_metrics(&self) -> Vec<PerformanceMetrics> {
218 if let Ok(db) = self.metrics.lock() {
219 db.values().cloned().collect()
220 } else {
221 Vec::new()
222 }
223 }
224
225 #[allow(dead_code)]
227 fn compute_twiddle_factors(&self, size: usize) -> Vec<Complex64> {
228 let mut twiddles = Vec::with_capacity(size / 2);
229 let factor = -2.0 * std::f64::consts::PI / size as f64;
230
231 for k in 0..size / 2 {
232 let angle = factor * k as f64;
233 twiddles.push(Complex64::new(angle.cos(), angle.sin()));
234 }
235
236 twiddles
237 }
238
239 pub fn fft<T>(&mut self, input: &[T], n: Option<usize>) -> FFTResult<Vec<Complex64>>
241 where
242 T: NumCast + Copy + Debug,
243 {
244 let start = Instant::now();
245 let size = n.unwrap_or(input.len()).min(self.config.max_fft_size); let mut data: Vec<Complex64> = input
249 .iter()
250 .take(size) .map(|&val| {
252 let val_f64 = NumCast::from(val).ok_or_else(|| {
253 FFTError::ValueError(format!("Could not convert {:?} to f64", val))
254 });
255 match val_f64 {
256 Ok(v) => Ok(Complex64::new(v, 0.0)),
257 Err(e) => Err(e),
258 }
259 })
260 .collect::<FFTResult<Vec<_>>>()?;
261
262 match data.len().cmp(&size) {
264 std::cmp::Ordering::Less => {
265 data.resize(size, Complex64::new(0.0, 0.0));
266 }
267 std::cmp::Ordering::Greater => {
268 data.truncate(size);
269 }
270 std::cmp::Ordering::Equal => {
271 }
273 }
274
275 let algorithm = self.select_algorithm(size);
277
278 let result = match algorithm.as_str() {
280 "radix2" => self.radix2_fft(&mut data),
281 "bluestein" => self.bluestein_fft(&mut data),
282 "prime_factor" => self.prime_factor_fft(&mut data),
283 "default" => self.default_fft(&data),
284 _ => self.default_fft(&data),
285 }?;
286
287 if self.collect_stats {
289 let elapsed = start.elapsed();
290 let elapsed_ns = elapsed.as_nanos() as u64;
291 self.stats.operation_count += 1;
292 self.stats.total_time_ns += elapsed_ns;
293 self.stats.max_time_ns = self.stats.max_time_ns.max(elapsed_ns);
294 if self.stats.min_time_ns == 0 {
295 self.stats.min_time_ns = elapsed_ns;
296 } else {
297 self.stats.min_time_ns = self.stats.min_time_ns.min(elapsed_ns);
298 }
299
300 let flops = (5.0 * size as f64 * (size as f64).log2()) as u64;
302 self.stats.total_flops += flops;
303 }
304
305 if self.config.collect_metrics {
307 let duration = start.elapsed();
308 let op_count = 5.0 * size as f64 * (size as f64).log2(); let mflops = op_count / duration.as_secs_f64() / 1_000_000.0;
310
311 let metrics = PerformanceMetrics {
312 algorithm,
313 size,
314 duration,
315 mflops,
316 optimization_level: self.config.optimization_level,
317 };
318
319 if let Ok(mut db) = self.metrics.lock() {
320 db.insert((size, self.config.optimization_level), metrics);
321 }
322
323 self.total_ffts.fetch_add(1, Ordering::SeqCst);
324 }
325
326 Ok(result)
327 }
328
329 pub fn ifft(&mut self, input: &[Complex64], n: Option<usize>) -> FFTResult<Vec<Complex64>> {
331 let start = Instant::now();
332 let size = n.unwrap_or(input.len()).min(self.config.max_fft_size); let data: Vec<Complex64> = input.iter().take(size).copied().collect();
336
337 let algorithm = self.select_algorithm(size);
339
340 let result = match algorithm.as_str() {
342 "radix2" => self.radix2_ifft(&data),
343 "bluestein" => self.bluestein_ifft(&data),
344 "prime_factor" => self.prime_factor_ifft(&data),
345 _ => ifft(&data, Some(size)),
346 }?;
347
348 if self.config.collect_metrics {
350 let duration = start.elapsed();
351 let op_count = 5.0 * size as f64 * (size as f64).log2(); let mflops = op_count / duration.as_secs_f64() / 1_000_000.0;
353
354 let metrics = PerformanceMetrics {
355 algorithm,
356 size,
357 duration,
358 mflops,
359 optimization_level: self.config.optimization_level,
360 };
361
362 if let Ok(mut db) = self.metrics.lock() {
363 db.insert((size, self.config.optimization_level), metrics);
364 }
365
366 self.total_ffts.fetch_add(1, Ordering::SeqCst);
367 }
368
369 Ok(result)
370 }
371
372 fn select_algorithm(&self, size: usize) -> String {
374 match self.config.optimization_level {
375 OptimizationLevel::Default | OptimizationLevel::Basic => {
376 if size.is_power_of_two() {
378 "radix2".to_string()
379 } else {
380 "default".to_string()
381 }
382 }
383 OptimizationLevel::Balanced => {
384 if size.is_power_of_two() {
386 "radix2".to_string()
387 } else if size <= 1024 {
388 "bluestein".to_string()
389 } else {
390 "default".to_string()
391 }
392 }
393 OptimizationLevel::Maximum | OptimizationLevel::Performance => {
394 if size.is_power_of_two() {
396 "radix2".to_string()
397 } else if size % 2 != 0 && size % 3 != 0 && size % 5 != 0 {
398 "bluestein".to_string()
399 } else {
400 "prime_factor".to_string()
401 }
402 }
403 OptimizationLevel::Auto => {
404 if size.is_power_of_two() {
409 "radix2".to_string()
410 } else if size <= 1024 {
411 "bluestein".to_string()
412 } else if size % 2 == 0 || size % 3 == 0 || size % 5 == 0 {
413 "prime_factor".to_string()
414 } else {
415 "bluestein".to_string()
416 }
417 }
418 OptimizationLevel::SizeSpecific => {
419 if size.is_power_of_two() {
421 "radix2".to_string()
422 } else if size <= 16 {
423 "small_size".to_string()
424 } else {
425 "default".to_string()
426 }
427 }
428 OptimizationLevel::Simd => {
429 "simd".to_string()
431 }
432 OptimizationLevel::CacheEfficient => {
433 "cache_efficient".to_string()
435 }
436 OptimizationLevel::TlbOptimized => {
437 if size.is_power_of_two() {
439 "radix2_tlb".to_string()
440 } else {
441 "default".to_string()
442 }
443 }
444 OptimizationLevel::UltraOptimized => {
445 if size.is_power_of_two() {
447 "radix2_ultra".to_string()
448 } else {
449 "default".to_string()
450 }
451 }
452 }
453 }
454
455 fn default_fft(&self, input: &[Complex64]) -> FFTResult<Vec<Complex64>> {
457 #[cfg(feature = "oxifft")]
458 {
459 let input_oxi: Vec<OxiComplex<f64>> =
461 input.iter().map(|c| OxiComplex::new(c.re, c.im)).collect();
462 let mut output: Vec<OxiComplex<f64>> = vec![OxiComplex::zero(); input.len()];
463
464 oxifft_plan_cache::execute_c2c(&input_oxi, &mut output, Direction::Forward)?;
466
467 let result: Vec<Complex64> = output
469 .into_iter()
470 .map(|c| Complex64::new(c.re, c.im))
471 .collect();
472
473 Ok(result)
474 }
475
476 #[cfg(not(feature = "oxifft"))]
477 {
478 #[cfg(feature = "rustfft-backend")]
479 {
480 let mut planner = FftPlanner::new();
481 let fft = planner.plan_fft_forward(input.len());
482
483 let mut buffer = input.to_vec();
484 fft.process(&mut buffer);
485
486 Ok(buffer)
487 }
488
489 {
490 Err(FFTError::ComputationError(
491 "No FFT backend available. Enable either 'oxifft' or 'rustfft-backend' feature.".to_string()
492 ))
493 }
494 }
495 }
496
497 pub fn benchmark_sizes(
499 &mut self,
500 min_size: usize,
501 max_size: usize,
502 step: usize,
503 ) -> FFTResult<HashMap<usize, PerformanceMetrics>> {
504 let mut results = HashMap::new();
505
506 let original_collect = self.config.collect_metrics;
508 self.config.collect_metrics = true;
509
510 let actual_max = max_size.min(self.config.max_fft_size);
512
513 for size in (min_size..=actual_max).step_by(step) {
514 let data: Vec<f64> = (0..size).map(|i| (i as f64).sin()).collect();
516
517 let start = Instant::now();
519 let _ = self.fft(&data, Some(size))?;
520 let duration = start.elapsed();
521
522 let op_count = 5.0 * size as f64 * (size as f64).log2();
524 let mflops = op_count / duration.as_secs_f64() / 1_000_000.0;
525
526 let algorithm = self.select_algorithm(size);
528 let metrics = PerformanceMetrics {
529 algorithm,
530 size,
531 duration,
532 mflops,
533 optimization_level: self.config.optimization_level,
534 };
535
536 results.insert(size, metrics);
537 }
538
539 self.config.collect_metrics = original_collect;
541
542 Ok(results)
543 }
544
545 fn radix2_fft(&self, data: &mut [Complex64]) -> FFTResult<Vec<Complex64>> {
548 match self.config.optimization_level {
549 OptimizationLevel::TlbOptimized | OptimizationLevel::UltraOptimized => {
550 #[cfg(feature = "simd")]
551 {
552 self.radix2_fft_tlb_optimized(data)
553 }
554 #[cfg(not(feature = "simd"))]
555 {
556 fft(data, None)
558 }
559 }
560 OptimizationLevel::Simd => {
561 #[cfg(feature = "simd")]
562 {
563 self.radix2_fft_simd_optimized(data)
564 }
565 #[cfg(not(feature = "simd"))]
566 {
567 fft(data, None)
569 }
570 }
571 _ => {
572 fft(data, None)
574 }
575 }
576 }
577
578 fn bluestein_fft(&self, data: &mut [Complex64]) -> FFTResult<Vec<Complex64>> {
579 fft(data, None)
582 }
583
584 fn prime_factor_fft(&self, data: &mut [Complex64]) -> FFTResult<Vec<Complex64>> {
585 fft(data, None)
588 }
589
590 fn radix2_ifft(&self, data: &[Complex64]) -> FFTResult<Vec<Complex64>> {
591 ifft(data, None)
594 }
595
596 fn bluestein_ifft(&self, data: &[Complex64]) -> FFTResult<Vec<Complex64>> {
597 ifft(data, None)
600 }
601
602 fn prime_factor_ifft(&self, data: &[Complex64]) -> FFTResult<Vec<Complex64>> {
603 ifft(data, None)
606 }
607
608 #[allow(dead_code)]
610 fn maximum_optimized_fft(&self, input: &[Complex64]) -> FFTResult<Vec<Complex64>> {
611 self.default_fft(input)
614 }
615
616 #[allow(dead_code)]
618 fn size_specific_fft(&self, input: &[Complex64]) -> FFTResult<Vec<Complex64>> {
619 let n = input.len();
620
621 if n.is_power_of_two() {
623 return self.power_of_two_fft(input);
624 }
625
626 if n <= 16 {
628 return self.small_size_fft(input);
629 }
630
631 self.default_fft(input)
633 }
634
635 #[allow(dead_code)]
637 fn power_of_two_fft(&self, input: &[Complex64]) -> FFTResult<Vec<Complex64>> {
638 self.default_fft(input)
642 }
643
644 #[allow(dead_code)]
646 fn small_size_fft(&self, input: &[Complex64]) -> FFTResult<Vec<Complex64>> {
647 self.default_fft(input)
651 }
652
653 #[allow(dead_code)]
655 fn simd_optimized_fft(&self, input: &[Complex64]) -> FFTResult<Vec<Complex64>> {
656 #[cfg(any(target_feature = "sse", target_feature = "avx"))]
657 {
658 }
661
662 self.default_fft(input)
664 }
665
666 #[allow(dead_code)]
668 fn cache_efficient_fft(&self, input: &[Complex64]) -> FFTResult<Vec<Complex64>> {
669 self.default_fft(input)
672 }
673
674 pub fn fft2<S>(
676 &mut self,
677 input: &ArrayBase<S, scirs2_core::ndarray::Ix2>,
678 ) -> FFTResult<Array<Complex64, scirs2_core::ndarray::Ix2>>
679 where
680 S: Data,
681 S::Elem: NumCast + Copy + Debug,
682 {
683 let shape = input.shape();
685
686 let rows = shape[0].min(self.config.max_fft_size / 2);
688 let cols = shape[1].min(self.config.max_fft_size / 2);
689
690 let mut output = Array::zeros((rows, cols));
692
693 for i in 0..rows {
695 let row: Vec<_> = input
696 .slice(scirs2_core::ndarray::s![i, ..cols])
697 .iter()
698 .map(|&val| {
699 let val_f64 = NumCast::from(val).ok_or_else(|| {
700 FFTError::ValueError("Could not convert to f64".to_string())
701 })?;
702 Ok(Complex64::new(val_f64, 0.0))
703 })
704 .collect::<FFTResult<Vec<_>>>()?;
705
706 let row_fft = self.fft(&row, None)?;
707 for (j, val) in row_fft.iter().enumerate().take(cols) {
708 output[[i, j]] = *val;
709 }
710 }
711
712 for j in 0..cols {
714 let mut col = Vec::with_capacity(rows);
715 for i in 0..rows {
716 col.push(output[[i, j]]);
717 }
718
719 let col_fft = self.fft(&col, None)?;
720 for (i, val) in col_fft.iter().enumerate().take(rows) {
721 output[[i, j]] = *val;
722 }
723 }
724
725 Ok(output)
728 }
729
730 #[allow(dead_code)]
732 fn detect_cpu_features(&self) -> Vec<String> {
733 vec![
736 "sse".to_string(),
737 "sse2".to_string(),
738 "sse3".to_string(),
739 "sse4.1".to_string(),
740 "avx".to_string(),
741 ]
742 }
743
744 pub fn suggest_optimal_size(&self, requestedsize: usize) -> usize {
746 let next_pow2 = requestedsize.next_power_of_two();
748
749 if requestedsize.is_power_of_two() {
754 return requestedsize;
755 }
756
757 if next_pow2 < requestedsize * 2 {
759 return next_pow2;
760 }
761
762 let mut best_size = requestedsize;
764 let mut best_score = usize::MAX;
765
766 for size in requestedsize..=next_pow2 {
768 let score = self.complexity_score(size);
770
771 if score < best_score {
772 best_score = score;
773 best_size = size;
774 }
775 }
776
777 best_size
778 }
779
780 fn complexity_score(&self, n: usize) -> usize {
783 if n.is_power_of_two() {
784 return 0;
786 }
787
788 let mut factors = 0;
790 let mut remaining = n;
791 let mut i = 2;
792
793 while i * i <= remaining {
794 while remaining % i == 0 {
795 factors += 1;
796 remaining /= i;
797 }
798 i += 1;
799 }
800
801 if remaining > 1 {
802 factors += 1;
803 }
804
805 factors * 100 + n.count_ones() as usize * 10
807 }
808
809 #[cfg(feature = "simd")]
824 fn radix2_fft_tlb_optimized(&self, data: &mut [Complex64]) -> FFTResult<Vec<Complex64>> {
825 let caps = PlatformCapabilities::detect();
826 let n = data.len();
827
828 if n >= 512 && caps.has_avx2() && n.is_power_of_two() {
830 self.radix2_fft_ultra_optimized(data, caps)
831 } else if n >= 64 && caps.simd_available {
832 self.radix2_fft_cache_optimized(data, caps)
833 } else {
834 fft(data, None)
836 }
837 }
838
839 #[cfg(feature = "simd")]
841 fn radix2_fft_ultra_optimized(
842 &self,
843 data: &mut [Complex64],
844 caps: PlatformCapabilities,
845 ) -> FFTResult<Vec<Complex64>> {
846 let n = data.len();
847 let mut result = data.to_vec();
848
849 let page_size = 4096; let tlb_entries = 64; let optimal_working_set = page_size * tlb_entries / 2; let complex_size = std::mem::size_of::<Complex64>();
856 let elements_per_page = page_size / complex_size;
857 let block_size = (optimal_working_set / complex_size).min(n / 4).max(64);
858
859 if n >= block_size * 4 {
861 self.radix2_fft_blocked_tlb_optimized(&mut result, block_size, caps)
863 } else {
864 self.radix2_fft_cache_optimized_impl(&mut result, caps)
866 }
867 }
868
869 #[cfg(feature = "simd")]
871 fn radix2_fft_blocked_tlb_optimized(
872 &self,
873 data: &mut [Complex64],
874 block_size: usize,
875 caps: PlatformCapabilities,
876 ) -> FFTResult<Vec<Complex64>> {
877 let n = data.len();
878
879 self.bit_reverse_tlb_optimized(data, block_size);
881
882 let mut step = 2;
884 while step <= n {
885 let half_step = step / 2;
886
887 for block_start in (0..n).step_by(block_size) {
889 let block_end = (block_start + block_size).min(n);
890
891 for i in (block_start..block_end).step_by(step) {
893 if i + half_step < block_end {
894 self.butterfly_operation_simd_ultra(
895 &mut data[i..i + step],
896 half_step,
897 caps,
898 );
899 }
900 }
901 }
902 step *= 2;
903 }
904
905 Ok(data.to_vec())
906 }
907
908 #[cfg(feature = "simd")]
910 fn radix2_fft_cache_optimized(
911 &self,
912 data: &mut [Complex64],
913 caps: PlatformCapabilities,
914 ) -> FFTResult<Vec<Complex64>> {
915 let mut result = data.to_vec();
916 self.radix2_fft_cache_optimized_impl(&mut result, caps)
917 }
918
919 #[cfg(feature = "simd")]
921 fn radix2_fft_cache_optimized_impl(
922 &self,
923 data: &mut [Complex64],
924 caps: PlatformCapabilities,
925 ) -> FFTResult<Vec<Complex64>> {
926 let n = data.len();
927
928 self.bit_reverse_cache_aware(data, caps.cache_line_size());
930
931 let mut step = 2;
933 while step <= n {
934 let half_step = step / 2;
935
936 let cache_chunk_size = caps.cache_line_size() / std::mem::size_of::<Complex64>();
938
939 for i in (0..n).step_by(cache_chunk_size) {
940 let chunk_end = (i + cache_chunk_size).min(n);
941
942 for j in (i..chunk_end).step_by(step) {
943 if j + half_step < n {
944 self.butterfly_operation_simd_optimized(
945 &mut data[j..j + step],
946 half_step,
947 caps,
948 );
949 }
950 }
951 }
952 step *= 2;
953 }
954
955 Ok(data.to_vec())
956 }
957
958 #[cfg(feature = "simd")]
960 fn radix2_fft_simd_optimized(&self, data: &mut [Complex64]) -> FFTResult<Vec<Complex64>> {
961 let caps = PlatformCapabilities::detect();
962 let n = data.len();
963
964 if !n.is_power_of_two() {
965 return Err(FFTError::ValueError(
966 "Radix-2 FFT requires power-of-2 size".to_string(),
967 ));
968 }
969
970 let mut result = data.to_vec();
971
972 self.bit_reverse_simd(&mut result);
974
975 let mut step = 2;
977 while step <= n {
978 let half_step = step / 2;
979
980 for i in (0..n).step_by(step) {
981 self.butterfly_operation_simd(&mut result[i..i + step], half_step, caps);
982 }
983 step *= 2;
984 }
985
986 Ok(result)
987 }
988
989 #[cfg(feature = "simd")]
995 fn bit_reverse_tlb_optimized(&self, data: &mut [Complex64], block_size: usize) {
996 let n = data.len();
997 let log_n = (n as f64).log2() as usize;
998
999 for block_start in (0..n).step_by(block_size) {
1001 let block_end = (block_start + block_size).min(n);
1002
1003 for i in block_start..block_end {
1004 let mut reversed = 0;
1005 let mut temp = i;
1006
1007 for _ in 0..log_n {
1009 reversed = (reversed << 1) | (temp & 1);
1010 temp >>= 1;
1011 }
1012
1013 if reversed > i && reversed < n {
1014 data.swap(i, reversed);
1015 }
1016 }
1017 }
1018 }
1019
1020 #[cfg(feature = "simd")]
1022 fn bit_reverse_cache_aware(&self, data: &mut [Complex64], cache_line_size: usize) {
1023 let n = data.len();
1024 let log_n = (n as f64).log2() as usize;
1025 let chunk_size = cache_line_size / std::mem::size_of::<Complex64>();
1026
1027 for chunk_start in (0..n).step_by(chunk_size) {
1028 let chunk_end = (chunk_start + chunk_size).min(n);
1029
1030 for i in chunk_start..chunk_end {
1031 let reversed = self.reverse_bits(i, log_n);
1032 if reversed > i && reversed < n {
1033 data.swap(i, reversed);
1034 }
1035 }
1036 }
1037 }
1038
1039 #[cfg(feature = "simd")]
1041 fn bit_reverse_simd(&self, data: &mut [Complex64]) {
1042 let n = data.len();
1043 let log_n = (n as f64).log2() as usize;
1044
1045 for i in (0..n).step_by(8) {
1047 let end = (i + 8).min(n);
1048
1049 for j in i..end {
1050 let reversed = self.reverse_bits(j, log_n);
1051 if reversed > j && reversed < n {
1052 data.swap(j, reversed);
1053 }
1054 }
1055 }
1056 }
1057
1058 #[cfg(feature = "simd")]
1060 fn butterfly_operation_simd_ultra(
1061 &self,
1062 data: &mut [Complex64],
1063 half_step: usize,
1064 caps: PlatformCapabilities,
1065 ) {
1066 if data.len() >= half_step * 2 && caps.has_avx2() {
1067 self.butterfly_simd_avx2_ultra(data, half_step);
1069 } else {
1070 self.butterfly_operation_scalar(data, half_step);
1072 }
1073 }
1074
1075 #[cfg(feature = "simd")]
1077 fn butterfly_operation_simd_optimized(
1078 &self,
1079 data: &mut [Complex64],
1080 half_step: usize,
1081 caps: PlatformCapabilities,
1082 ) {
1083 if caps.simd_available && data.len() >= 8 {
1084 self.butterfly_simd_optimized(data, half_step);
1085 } else {
1086 self.butterfly_operation_scalar(data, half_step);
1087 }
1088 }
1089
1090 #[cfg(feature = "simd")]
1092 fn butterfly_operation_simd(
1093 &self,
1094 data: &mut [Complex64],
1095 half_step: usize,
1096 caps: PlatformCapabilities,
1097 ) {
1098 if caps.simd_available {
1099 self.butterfly_simd_basic(data, half_step);
1100 } else {
1101 self.butterfly_operation_scalar(data, half_step);
1102 }
1103 }
1104
1105 #[cfg(feature = "simd")]
1107 fn butterfly_simd_avx2_ultra(&self, data: &mut [Complex64], half_step: usize) {
1108 for i in 0..half_step {
1113 if i + half_step < data.len() {
1114 let w = self
1115 .complex_exp(-2.0 * std::f64::consts::PI * i as f64 / (2 * half_step) as f64);
1116 let temp = data[i + half_step] * w;
1117 data[i + half_step] = data[i] - temp;
1118 data[i] = data[i] + temp;
1119 }
1120 }
1121 }
1122
1123 #[cfg(feature = "simd")]
1125 fn butterfly_simd_optimized(&self, data: &mut [Complex64], half_step: usize) {
1126 let chunk_size = 8; for chunk_start in (0..half_step).step_by(chunk_size) {
1130 let chunk_end = (chunk_start + chunk_size).min(half_step);
1131
1132 for i in chunk_start..chunk_end {
1133 if i + half_step < data.len() {
1134 let w = self.complex_exp(
1135 -2.0 * std::f64::consts::PI * i as f64 / (2 * half_step) as f64,
1136 );
1137 let temp = data[i + half_step] * w;
1138 data[i + half_step] = data[i] - temp;
1139 data[i] = data[i] + temp;
1140 }
1141 }
1142 }
1143 }
1144
1145 #[cfg(feature = "simd")]
1147 fn butterfly_simd_basic(&self, data: &mut [Complex64], half_step: usize) {
1148 for i in 0..half_step {
1149 if i + half_step < data.len() {
1150 let w = self
1151 .complex_exp(-2.0 * std::f64::consts::PI * i as f64 / (2 * half_step) as f64);
1152 let temp = data[i + half_step] * w;
1153 data[i + half_step] = data[i] - temp;
1154 data[i] = data[i] + temp;
1155 }
1156 }
1157 }
1158
1159 fn butterfly_operation_scalar(&self, data: &mut [Complex64], half_step: usize) {
1161 for i in 0..half_step {
1162 if i + half_step < data.len() {
1163 let w = self
1164 .complex_exp(-2.0 * std::f64::consts::PI * i as f64 / (2 * half_step) as f64);
1165 let temp = data[i + half_step] * w;
1166 data[i + half_step] = data[i] - temp;
1167 data[i] = data[i] + temp;
1168 }
1169 }
1170 }
1171
1172 fn reverse_bits(&self, mut n: usize, bits: usize) -> usize {
1174 let mut result = 0;
1175 for _ in 0..bits {
1176 result = (result << 1) | (n & 1);
1177 n >>= 1;
1178 }
1179 result
1180 }
1181
1182 fn complex_exp(&self, angle: f64) -> Complex64 {
1184 Complex64::new(angle.cos(), angle.sin())
1185 }
1186}
1187
1188#[cfg(test)]
1189#[cfg(feature = "never")] mod tests {
1191 use super::*;
1192 use approx::assert_relative_eq;
1193
1194 #[test]
1195 fn test_optimized_fft_simple() {
1196 let config = OptimizedConfig::default();
1197 let mut fft = OptimizedFFT::new(config);
1198
1199 let input = vec![1.0, 0.0, 0.0, 0.0];
1201 let output = fft.fft(&input, None).expect("Operation failed");
1202
1203 assert_eq!(output.len(), 4);
1204 for val in &output {
1205 assert_relative_eq!(val.re, 1.0, epsilon = 1e-10);
1206 assert_relative_eq!(val.im, 0.0, epsilon = 1e-10);
1207 }
1208 }
1209
1210 #[test]
1211 fn test_stats_collection() {
1212 let config = OptimizedConfig::default();
1213 let mut fft = OptimizedFFT::new(config);
1214 fft.set_collect_stats(true);
1215
1216 let input = vec![1.0, 2.0, 3.0, 4.0];
1218 for _ in 0..5 {
1219 let _ = fft.fft(&input, None).expect("Operation failed");
1220 }
1221
1222 let stats = fft.get_stats();
1223 assert_eq!(stats.operation_count, 5);
1224 assert!(stats.total_time_ns > 0);
1225 assert!(stats.avg_time_ns() > 0);
1226 }
1227
1228 #[test]
1229 fn test_suggest_optimal_size() {
1230 let config = OptimizedConfig::default();
1231 let fft = OptimizedFFT::new(config);
1232
1233 assert_eq!(fft.suggest_optimal_size(64), 64);
1235
1236 let size_100 = fft.suggest_optimal_size(100);
1238 assert!(size_100 >= 100); }
1240
1241 #[test]
1242 fn test_different_optimization_levels() {
1243 let input = vec![1.0, 2.0, 3.0, 4.0];
1244
1245 let levels = [
1246 OptimizationLevel::Default,
1247 OptimizationLevel::Maximum,
1248 OptimizationLevel::SizeSpecific,
1249 OptimizationLevel::Simd,
1250 OptimizationLevel::CacheEfficient,
1251 OptimizationLevel::Basic,
1252 OptimizationLevel::Balanced,
1253 OptimizationLevel::Auto,
1254 ];
1255
1256 for level in &levels {
1257 let config = OptimizedConfig {
1258 optimization_level: *level,
1259 ..OptimizedConfig::default()
1260 };
1261
1262 let mut fft = OptimizedFFT::new(config);
1263 let result = fft.fft(&input, None);
1264 assert!(
1265 result.is_ok(),
1266 "FFT failed with optimization level {:?}",
1267 level
1268 );
1269 }
1270 }
1271}