Skip to content

Commit aa658f9

Browse files
Merge pull request #72 from falseywinchnet/yxlmq0-main/optimize-performance-with-avx2/sse2
Add AVX2 float BODFT forward combine (8-position, FMA)
2 parents 6314da4 + 2b5ca83 commit aa658f9

2 files changed

Lines changed: 107 additions & 9 deletions

File tree

src/detail/bodft_avx2_notes.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,11 @@
2929
- Tightened the forward scratch schedule so the final forward combine writes directly into the caller output buffer instead of ping-ponging once more and copying `N/2` complex values at the end.
3030
- BODFT double inverse timing improved in this container from about 748 us to about 611 us at N=65536, and from about 22.7 ms to about 20.6 ms at N=1048576. Smaller sizes saw larger relative gains where the final-copy removal and inverse AVX2 combine reduce overhead together.
3131
- The remaining gap to the regular BFFT native path is now mostly scratch/data-order locality and float-specific AVX2 work rather than the absence of a double inverse vector combine.
32+
33+
## Float forward combine pass
34+
35+
- Added an eight-position AVX2/FMA single-precision forward combine path for the odd-frequency transform.
36+
- The float path now deinterleaves eight packed complex twiddles/children into 256-bit SoA real/imaginary lanes, does all three child twiddle products with `vfmaddps`/`vfmsubps`, and writes the four paired output blocks through an AVX2 interleave helper.
37+
- Partner bins are lane-reversed in registers with an AVX2 permute before storing, preserving the scalar/128-bit conjugate-pair layout while doubling the 128-bit float combine width on AVX2 hosts.
38+
- The existing 128-bit SSE2/NEON float combine remains the fallback for non-AVX2 builds and for tiny levels that cannot fill an eight-position vector.
39+
- Correctness was checked with the regular CTest suite and the standalone BODFT benchmark on an AVX2/FMA build; a separate SSE2/no-AVX build was also checked to keep the fallback path intact.

src/detail/bodft_kernel.hpp

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,81 @@ static inline int bodft_combine_fwd_avx2_f64(const complex_t* RESTRICT tab,
164164
return k;
165165
}
166166

167+
168+
static inline void bodft_uzp8_ps(const complex_f32_t* RESTRICT src, int k,
169+
__m256& re, __m256& im) {
170+
const __m256 a = _mm256_loadu_ps(&src[k].re);
171+
const __m256 b = _mm256_loadu_ps(&src[k + 4].re);
172+
const __m256 lo = _mm256_unpacklo_ps(a, b);
173+
const __m256 hi = _mm256_unpackhi_ps(a, b);
174+
const __m256 re_pairs = _mm256_shuffle_ps(lo, hi, _MM_SHUFFLE(1, 0, 1, 0));
175+
const __m256 im_pairs = _mm256_shuffle_ps(lo, hi, _MM_SHUFFLE(3, 2, 3, 2));
176+
re = _mm256_permutevar8x32_ps(re_pairs, _mm256_setr_epi32(0, 2, 4, 6, 1, 3, 5, 7));
177+
im = _mm256_permutevar8x32_ps(im_pairs, _mm256_setr_epi32(0, 2, 4, 6, 1, 3, 5, 7));
178+
}
179+
180+
static inline void bodft_store8_ps(complex_f32_t* RESTRICT dst, int k,
181+
__m256 re, __m256 im) {
182+
const __m256 lo = _mm256_unpacklo_ps(re, im);
183+
const __m256 hi = _mm256_unpackhi_ps(re, im);
184+
_mm256_storeu_ps(&dst[k].re, _mm256_permute2f128_ps(lo, hi, 0x20));
185+
_mm256_storeu_ps(&dst[k + 4].re, _mm256_permute2f128_ps(lo, hi, 0x31));
186+
}
187+
188+
static inline __m256 bodft_rev8_ps(__m256 v) {
189+
return _mm256_permutevar8x32_ps(v, _mm256_setr_epi32(7, 6, 5, 4, 3, 2, 1, 0));
190+
}
191+
192+
static inline int bodft_combine_fwd_avx2_f32(const complex_f32_t* RESTRICT tab,
193+
const complex_f32_t* RESTRICT tab2,
194+
const complex_f32_t* RESTRICT tab3,
195+
const complex_f32_t* RESTRICT c0,
196+
const complex_f32_t* RESTRICT c1,
197+
const complex_f32_t* RESTRICT c2,
198+
const complex_f32_t* RESTRICT c3,
199+
complex_f32_t* RESTRICT out,
200+
int M, int half) {
201+
const __m256 zero = _mm256_setzero_ps();
202+
int k = 0;
203+
for (; k + 8 <= half; k += 8) {
204+
__m256 tre, tim, t2re, t2im, t3re, t3im;
205+
__m256 c0re, c0im, c1re, c1im, c2re, c2im, c3re, c3im;
206+
bodft_uzp8_ps(tab, k, tre, tim);
207+
bodft_uzp8_ps(tab2, k, t2re, t2im);
208+
bodft_uzp8_ps(tab3, k, t3re, t3im);
209+
bodft_uzp8_ps(c0, k, c0re, c0im);
210+
bodft_uzp8_ps(c1, k, c1re, c1im);
211+
bodft_uzp8_ps(c2, k, c2re, c2im);
212+
bodft_uzp8_ps(c3, k, c3re, c3im);
213+
214+
const __m256 b0re = c0re, b0im = c0im;
215+
const __m256 b1re = _mm256_fmsub_ps(c1re, tre, _mm256_mul_ps(c1im, tim));
216+
const __m256 b1im = _mm256_fmadd_ps(c1re, tim, _mm256_mul_ps(c1im, tre));
217+
const __m256 b2re = _mm256_fmsub_ps(c2re, t2re, _mm256_mul_ps(c2im, t2im));
218+
const __m256 b2im = _mm256_fmadd_ps(c2re, t2im, _mm256_mul_ps(c2im, t2re));
219+
const __m256 b3re = _mm256_fmsub_ps(c3re, t3re, _mm256_mul_ps(c3im, t3im));
220+
const __m256 b3im = _mm256_fmadd_ps(c3re, t3im, _mm256_mul_ps(c3im, t3re));
221+
222+
const __m256 e0re = _mm256_add_ps(b0re, b2re), e0im = _mm256_add_ps(b0im, b2im);
223+
const __m256 e1re = _mm256_sub_ps(b0re, b2re), e1im = _mm256_sub_ps(b0im, b2im);
224+
const __m256 o0re = _mm256_add_ps(b1re, b3re), o0im = _mm256_add_ps(b1im, b3im);
225+
const __m256 o1re = _mm256_sub_ps(b1re, b3re), o1im = _mm256_sub_ps(b1im, b3im);
226+
227+
const __m256 yk_re = _mm256_add_ps(e0re, o0re), yk_im = _mm256_add_ps(e0im, o0im);
228+
const __m256 ykM_re = _mm256_add_ps(e1re, o1im), ykM_im = _mm256_sub_ps(e1im, o1re);
229+
const __m256 ykp_re = _mm256_sub_ps(e1re, o1im);
230+
const __m256 ykp_im = _mm256_sub_ps(zero, _mm256_add_ps(e1im, o1re));
231+
const __m256 ykpM_re = _mm256_sub_ps(e0re, o0re);
232+
const __m256 ykpM_im = _mm256_sub_ps(o0im, e0im);
233+
234+
bodft_store8_ps(out, k, yk_re, yk_im);
235+
bodft_store8_ps(out, k + M, ykM_re, ykM_im);
236+
bodft_store8_ps(out, M - 8 - k, bodft_rev8_ps(ykp_re), bodft_rev8_ps(ykp_im));
237+
bodft_store8_ps(out, M - 8 - k + M, bodft_rev8_ps(ykpM_re), bodft_rev8_ps(ykpM_im));
238+
}
239+
return k;
240+
}
241+
167242
static inline int bodft_combine_inv_avx2_f64(const complex_t* RESTRICT tab,
168243
const complex_t* RESTRICT tab2,
169244
const complex_t* RESTRICT tab3,
@@ -384,6 +459,19 @@ static inline void combine_fwd(const CT* RESTRICT tab, const CT* RESTRICT tab2,
384459
reinterpret_cast<complex_t*>(out), M, half);
385460
}
386461
#endif
462+
#if BRUUN_LEVEL >= 2
463+
if constexpr (sizeof(RT) == 4) {
464+
k = bodft_combine_fwd_avx2_f32(
465+
reinterpret_cast<const complex_f32_t*>(tab),
466+
reinterpret_cast<const complex_f32_t*>(tab2),
467+
reinterpret_cast<const complex_f32_t*>(tab3),
468+
reinterpret_cast<const complex_f32_t*>(c0),
469+
reinterpret_cast<const complex_f32_t*>(c1),
470+
reinterpret_cast<const complex_f32_t*>(c2),
471+
reinterpret_cast<const complex_f32_t*>(c3),
472+
reinterpret_cast<complex_f32_t*>(out), M, half);
473+
}
474+
#endif
387475
#if BRUUN_LEVEL >= 1
388476
if constexpr (sizeof(RT) == 8) {
389477
if (k == 0) k = bodft_combine_fwd_simd_f64(
@@ -396,15 +484,17 @@ static inline void combine_fwd(const CT* RESTRICT tab, const CT* RESTRICT tab2,
396484
reinterpret_cast<const complex_t*>(c3),
397485
reinterpret_cast<complex_t*>(out), M, half);
398486
} else {
399-
k = bodft_combine_fwd_simd_f32(
400-
reinterpret_cast<const complex_f32_t*>(tab),
401-
reinterpret_cast<const complex_f32_t*>(tab2),
402-
reinterpret_cast<const complex_f32_t*>(tab3),
403-
reinterpret_cast<const complex_f32_t*>(c0),
404-
reinterpret_cast<const complex_f32_t*>(c1),
405-
reinterpret_cast<const complex_f32_t*>(c2),
406-
reinterpret_cast<const complex_f32_t*>(c3),
407-
reinterpret_cast<complex_f32_t*>(out), M, half);
487+
if (k == 0) {
488+
k = bodft_combine_fwd_simd_f32(
489+
reinterpret_cast<const complex_f32_t*>(tab),
490+
reinterpret_cast<const complex_f32_t*>(tab2),
491+
reinterpret_cast<const complex_f32_t*>(tab3),
492+
reinterpret_cast<const complex_f32_t*>(c0),
493+
reinterpret_cast<const complex_f32_t*>(c1),
494+
reinterpret_cast<const complex_f32_t*>(c2),
495+
reinterpret_cast<const complex_f32_t*>(c3),
496+
reinterpret_cast<complex_f32_t*>(out), M, half);
497+
}
408498
}
409499
#endif
410500
for (; k < half; ++k) {

0 commit comments

Comments
 (0)