It cannot out-perform IPP/KFR on x86-64 but should be on par with and likely faster than other libraries. Besdies that, I would guess IPP/KFR are dispatching to AVX2 while zldsp::fft is forced on SSE2, otherwise you shall not see a big gap.
It may get even faster if you use SoA for complex numbers (especially around medium orders). And SoA would also benifit spectrum processing a lot.
Yes, IPP is dispatching to AVX/AVX2. Here are the results for real FFT I have on my Ryzen 5800 (AoS):
Intel IPP ippSP AVX2 (l9) 2026.0.0 (r0xa7ad6ebc), enabled: avx2, cpu: avx2
| size | c/s | fwd | inv | GFLOP | inv | name | type | snr dB |
| ----: | ---: | ---: | ---: | ----: | ---: | ---------- | ------ | -----: |
| 8192 | 5.4 | 2.5 | 3.0 | 43.9 | 36.9 | Intel IPP | float | -135.0 |
| 8192 | 9.5 | 4.7 | 4.8 | 24.6 | 24.1 | PFFFT | float | -135.1 |
| 8192 | 8.2 | 4.0 | 4.2 | 27.6 | 27.7 | zldsp::fft | float | -134.2 |
| 8192 | 9.2 | 4.3 | 4.9 | 26.5 | 22.8 | Intel IPP | double | -309.5 |
| 8192 | 16.5 | 8.0 | 8.5 | 12.2 | 13.9 | PFFFT | double | -309.6 |
| 8192 | 15.4 | 7.5 | 7.9 | 15.2 | 14.1 | zldsp::fft | double | -307.4 |
| 65536 | 6.1 | 2.8 | 3.3 | 48.1 | 44.8 | Intel IPP | float | -133.9 |
| 65536 | 11.1 | 5.5 | 5.6 | 25.6 | 24.4 | PFFFT | float | -134.0 |
| 65536 | 12.1 | 6.0 | 6.2 | 23.7 | 24.1 | zldsp::fft | float | -133.2 |
| 65536 | 11.6 | 5.5 | 6.0 | 25.8 | 23.4 | Intel IPP | double | -307.7 |
| 65536 | 20.8 | 10.2 | 10.6 | 14.1 | 14.0 | PFFFT | double | -308.6 |
| 65536 | 20.0 | 9.7 | 10.3 | 14.7 | 14.5 | zldsp::fft | double | -306.2 |
SoA is perhaps faster for FFT, but then you generally have to convert to AoS if you do any subsequent processing and want to benefit from accelerated functions which mostly expect AoS.
Techinically it is possible. And technically it is possible to dispatch the whole processBlock() function. It is just so painful to arrange all things in that way … A much easier solution is to compile two versions and detect the SIMD via app/plugin installer, which should also avoid any runtime dispatch overhead.
The runtime dispatch overhead should be negligible for any real-world FFT size, and infinitesmal with regards to the AVX2 speedup. Thats’ what most FFT libs do anyway.
Compiling two versions is technically feasible but a major PITA for developers and users: you have to do double the builds, tests, installers, etc. Plus most users have no idea what AVX even means.
I understand Highway provides a simple macro mechanism to do that, perhaps I could submit a PR.
Given that input/output might be pointed to the same address, I am not sure whether it is safe to do so, if the compiler does some optimization there?
Unless the data is marked `__restrict`, a sane compiler should assume the pointers could alias.
Usually libraries that can work in place (is this the case of zldsp::fft ?), they usually provide two interfaces, one explictely marked ‘in place’ with a single T buffer, and another ‘out of place’ with `const T*, T*`