JUCE's FFT wrappers are slow and bug-prone

It’s great to have nice wrappers for Intel’s/Apple’s/FFTW’s FFTs.
But sadly they are all slower by non-negligible factors than the original providers :frowning:
This is due to all of the wrappers doing the following in their performRealOnlyForwardTransform methods:

AppleFFT:
    for (; i < size; ++i)
        out[i] = std::conj (out[size - i]);
FFTWImpl:
    for (auto i = size >> 1; i < size; ++i)
        out[i] = std::conj (out[size - i]);
IntelFFT:
    for (auto i = size >> 1; i < size; ++i)
        out[i] = std::conj (out[size - i]);

This is to provide the odd interface that juce chose to have for the real-transform - provide the negative frequencies which are redundant and should be ignored.
It’s no coincidence that all other FFT libraries chose to have a different API which doesn’t provide redundant results, as it has better performance as well as being less bug-prone for users. Currently users using performRealOnlyInverseTransform could get confused becuase either using the wrappers the second half of the input array is ignored or using juce’s fallback FFT they could get weird results which are not the inverse fft of the data provided.

(note that the real-ffts are the most frequently used transforms, for example used for implementing convolutions)

6 Likes

OK there is fix for this on develop with commit e61292f.

If anybody can submit a patch on how to modify the fallback engine to only write (real -> complex) or read (complex -> read) half of the coefficients that would be superb. Then I could also drop the double size memory requirement.

5 Likes

I just opened a PR for this fix. Though instead of making the fallback engine more efficient like you suggested, I found it easier to make it slightly less efficient by adding another internal buffer, but it’s only the fallback engine after all.

1 Like