1use num_complex::Complex;
2
3use std::any::TypeId;
4use std::sync::Arc;
5
6use crate::array_utils::{bitreversed_transpose, workaround_transmute_mut};
7use crate::{common::FftNum, FftDirection};
8use crate::{Direction, Fft, Length};
9
10use super::SseNum;
11
12use super::sse_vector::{Rotation90, SseArray, SseArrayMut, SseVector};
13
14pub struct SseRadix4<S: SseNum, T> {
18 twiddles: Box<[S::VectorType]>,
19 rotation: Rotation90<S::VectorType>,
20
21 base_fft: Arc<dyn Fft<T>>,
22 base_len: usize,
23
24 len: usize,
25 direction: FftDirection,
26}
27
28impl<S: SseNum, T: FftNum> SseRadix4<S, T> {
29 #[inline]
31 pub fn new(k: u32, base_fft: Arc<dyn Fft<T>>) -> Result<Self, ()> {
32 let id_a = TypeId::of::<S>();
36 let id_t = TypeId::of::<T>();
37 assert_eq!(id_a, id_t);
38
39 let has_sse = is_x86_feature_detected!("sse4.1");
40 if has_sse {
41 Ok(unsafe { Self::new_with_sse(k, base_fft) })
43 } else {
44 Err(())
45 }
46 }
47
48 #[target_feature(enable = "sse4.1")]
49 unsafe fn new_with_sse(k: u32, base_fft: Arc<dyn Fft<T>>) -> Self {
50 let direction = base_fft.fft_direction();
51 let base_len = base_fft.len();
52
53 assert!(base_len % (2 * S::VectorType::COMPLEX_PER_VECTOR) == 0 && base_len > 0);
55
56 let len = base_len * (1 << (k * 2));
57
58 const ROW_COUNT: usize = 4;
63 let mut cross_fft_len = base_len * ROW_COUNT;
64 let mut twiddle_factors = Vec::with_capacity(len * 2);
65 while cross_fft_len <= len {
66 let num_scalar_columns = cross_fft_len / ROW_COUNT;
67 let num_vector_columns = num_scalar_columns / S::VectorType::COMPLEX_PER_VECTOR;
68
69 for i in 0..num_vector_columns {
70 for k in 1..ROW_COUNT {
71 twiddle_factors.push(SseVector::make_mixedradix_twiddle_chunk(
72 i * S::VectorType::COMPLEX_PER_VECTOR,
73 k,
74 cross_fft_len,
75 direction,
76 ));
77 }
78 }
79 cross_fft_len *= ROW_COUNT;
80 }
81
82 Self {
83 twiddles: twiddle_factors.into_boxed_slice(),
84 rotation: SseVector::make_rotate90(direction),
85
86 base_fft,
87 base_len,
88
89 len,
90 direction,
91 }
92 }
93
94 #[target_feature(enable = "sse4.1")]
95 unsafe fn perform_fft_immut(
96 &self,
97 input: &[Complex<T>],
98 output: &mut [Complex<T>],
99 _scratch: &mut [Complex<T>],
100 ) {
101 if self.len() == self.base_len {
103 output.copy_from_slice(input);
104 } else {
105 bitreversed_transpose::<Complex<T>, 4>(self.base_len, input, output);
106 }
107
108 self.base_fft.process_with_scratch(output, &mut []);
110
111 const ROW_COUNT: usize = 4;
113 let mut cross_fft_len = self.base_len * ROW_COUNT;
114 let mut layer_twiddles: &[S::VectorType] = &self.twiddles;
115
116 while cross_fft_len <= input.len() {
117 let num_rows = input.len() / cross_fft_len;
118 let num_scalar_columns = cross_fft_len / ROW_COUNT;
119 let num_vector_columns = num_scalar_columns / S::VectorType::COMPLEX_PER_VECTOR;
120
121 for i in 0..num_rows {
122 butterfly_4::<S, T>(
123 &mut output[i * cross_fft_len..],
124 layer_twiddles,
125 num_scalar_columns,
126 &self.rotation,
127 )
128 }
129
130 let twiddle_offset = num_vector_columns * (ROW_COUNT - 1);
132 layer_twiddles = &layer_twiddles[twiddle_offset..];
133
134 cross_fft_len *= ROW_COUNT;
135 }
136 }
137
138 #[target_feature(enable = "sse4.1")]
139 unsafe fn perform_fft_out_of_place(
140 &self,
141 input: &[Complex<T>],
142 output: &mut [Complex<T>],
143 _scratch: &mut [Complex<T>],
144 ) {
145 self.perform_fft_immut(input, output, _scratch);
146 }
147}
148boilerplate_fft_sse_oop!(SseRadix4, |this: &SseRadix4<_, _>| this.len);
149
150#[target_feature(enable = "sse4.1")]
151unsafe fn butterfly_4<S: SseNum, T: FftNum>(
152 data: &mut [Complex<T>],
153 twiddles: &[S::VectorType],
154 num_ffts: usize,
155 rotation: &Rotation90<S::VectorType>,
156) {
157 let unroll_offset = S::VectorType::COMPLEX_PER_VECTOR;
158
159 let mut idx = 0usize;
160 let mut buffer: &mut [Complex<S>] = workaround_transmute_mut(data);
161 for tw in twiddles
162 .chunks_exact(6)
163 .take(num_ffts / (S::VectorType::COMPLEX_PER_VECTOR * 2))
164 {
165 let mut scratcha = [
166 buffer.load_complex(idx + 0 * num_ffts),
167 buffer.load_complex(idx + 1 * num_ffts),
168 buffer.load_complex(idx + 2 * num_ffts),
169 buffer.load_complex(idx + 3 * num_ffts),
170 ];
171 let mut scratchb = [
172 buffer.load_complex(idx + 0 * num_ffts + unroll_offset),
173 buffer.load_complex(idx + 1 * num_ffts + unroll_offset),
174 buffer.load_complex(idx + 2 * num_ffts + unroll_offset),
175 buffer.load_complex(idx + 3 * num_ffts + unroll_offset),
176 ];
177
178 scratcha[1] = SseVector::mul_complex(scratcha[1], tw[0]);
179 scratcha[2] = SseVector::mul_complex(scratcha[2], tw[1]);
180 scratcha[3] = SseVector::mul_complex(scratcha[3], tw[2]);
181 scratchb[1] = SseVector::mul_complex(scratchb[1], tw[3]);
182 scratchb[2] = SseVector::mul_complex(scratchb[2], tw[4]);
183 scratchb[3] = SseVector::mul_complex(scratchb[3], tw[5]);
184
185 let scratcha = SseVector::column_butterfly4(scratcha, *rotation);
186 let scratchb = SseVector::column_butterfly4(scratchb, *rotation);
187
188 buffer.store_complex(scratcha[0], idx + 0 * num_ffts);
189 buffer.store_complex(scratchb[0], idx + 0 * num_ffts + unroll_offset);
190 buffer.store_complex(scratcha[1], idx + 1 * num_ffts);
191 buffer.store_complex(scratchb[1], idx + 1 * num_ffts + unroll_offset);
192 buffer.store_complex(scratcha[2], idx + 2 * num_ffts);
193 buffer.store_complex(scratchb[2], idx + 2 * num_ffts + unroll_offset);
194 buffer.store_complex(scratcha[3], idx + 3 * num_ffts);
195 buffer.store_complex(scratchb[3], idx + 3 * num_ffts + unroll_offset);
196
197 idx += S::VectorType::COMPLEX_PER_VECTOR * 2;
198 }
199}
200
201#[cfg(test)]
202mod unit_tests {
203 use super::*;
204 use crate::test_utils::{check_fft_algorithm, construct_base};
205
206 #[test]
207 fn test_sse_radix4_64() {
208 for base in [2, 4, 6, 8, 12, 16] {
209 let base_forward = construct_base(base, FftDirection::Forward);
210 let base_inverse = construct_base(base, FftDirection::Inverse);
211 for k in 0..4 {
212 test_sse_radix4_64_with_base(k, Arc::clone(&base_forward));
213 test_sse_radix4_64_with_base(k, Arc::clone(&base_inverse));
214 }
215 }
216 }
217
218 fn test_sse_radix4_64_with_base(k: u32, base_fft: Arc<dyn Fft<f64>>) {
219 let len = base_fft.len() * 4usize.pow(k);
220 let direction = base_fft.fft_direction();
221 let fft = SseRadix4::<f64, f64>::new(k, base_fft).unwrap();
222 check_fft_algorithm::<f64>(&fft, len, direction);
223 }
224
225 #[test]
226 fn test_sse_radix4_32() {
227 for base in [4, 8, 12, 16] {
228 let base_forward = construct_base(base, FftDirection::Forward);
229 let base_inverse = construct_base(base, FftDirection::Inverse);
230 for k in 0..4 {
231 test_sse_radix4_32_with_base(k, Arc::clone(&base_forward));
232 test_sse_radix4_32_with_base(k, Arc::clone(&base_inverse));
233 }
234 }
235 }
236
237 fn test_sse_radix4_32_with_base(k: u32, base_fft: Arc<dyn Fft<f32>>) {
238 let len = base_fft.len() * 4usize.pow(k);
239 let direction = base_fft.fft_direction();
240 let fft = SseRadix4::<f32, f32>::new(k, base_fft).unwrap();
241 check_fft_algorithm::<f32>(&fft, len, direction);
242 }
243}