Recientemente encontré este excelente PDF en el Construction of a high performance FFTs de Eric Postpischil. Después de haber desarrollado varias FFT sé lo difícil que es competir con las bibliotecas comerciales. Créame que lo está haciendo bien si su FFT es solo 4 veces más lenta que Intel o FFTW, ¡no 40 veces! Sin embargo, puedes competir, y así es cómo.
Para resumir ese artículo, el autor afirma que las FFT de Radix2 son simples pero ineficientes, la construcción más eficiente es la FFT radix4. Un método aún más eficiente es el Radix8, sin embargo, esto a menudo no encaja en los registros de una CPU, por lo que se prefiere Radix4.
Las FFT se pueden construir en etapas, por lo que para calcular una FFT de 1024 puntos se pueden realizar 10 etapas de la FFT Radix2 (como 2^10 - 1024) o 5 etapas de la FFT Radix4 (4^5 = 1024) . Incluso puede calcular una FFT de 1024 puntos en etapas de 8 * 4 * 4 * 4 * 2 si así lo desea.Menos etapas significan menos lecturas y escrituras en la memoria (el cuello de botella para el rendimiento de FFT es el ancho de banda de la memoria) por lo tanto, elegir dinámicamente radix 4, 8 o superior es una necesidad. La etapa Radix4 es particularmente eficiente ya que todos los pesos son 1 + 0i, 0 + 1i, -1 + 0i, 0-1i y el código de mariposa Radix4 se puede escribir para que quepa completamente en la memoria caché.
En segundo lugar, cada etapa en la FFT no es la misma. La primera etapa, los pesos son todos iguales a 1 + 0i. no tiene sentido calcular este peso e incluso multiplicarlo ya que es un complejo multiplicado por 1, por lo que la primera etapa se puede realizar sin pesos. La etapa final también se puede tratar de manera diferente y se puede usar para realizar la decimación en el tiempo (inversión de bit). El documento de Eric Postpischil cubre todo esto.
Los pesos se pueden precalcular y almacenar en una tabla. Los cálculos Sin/Cos demoran alrededor de 100-150 ciclos cada uno en el hardware x86, por lo que precomputarlos puede ahorrar un 10-20% del tiempo total de cálculo, ya que el acceso a la memoria es, en este caso, más rápido que los cálculos de la CPU. El uso de algoritmos rápidos para calcular sincos de una sola vez es particularmente beneficioso (tenga en cuenta que cos es igual a sqrt (1.0 - seno senoidal), o usando tablas de búsqueda, cos es solo un cambio de fase de seno).
Finalmente, una vez que tenga su implementación aerodinámica FFT puede utilizar la vectorización SIMD para calcular 4x coma flotante o 2x operaciones de punto flotante doble por ciclo dentro de la rutina de mariposa para otra mejora de 100-300% de velocidad. ¡Tomando todo lo anterior, te harás una FFT bastante resbaladiza y rápida!
Para ir más lejos puede realizar la optimización sobre la marcha al proporcionar diferentes implementaciones de las etapas de FFT dirigidas a arquitecturas de procesador específicas. El tamaño de la caché, el recuento de registros, los conjuntos de instrucciones SSE/SSE2/3/4, etc., difieren según la máquina, por lo que la elección de un enfoque único para todos es a menudo superada por las rutinas específicas. En FFTW, por ejemplo, muchas FFT de tamaño más pequeño son implementaciones altamente optimizadas desenrolladas (sin bucles) dirigidas a una arquitectura específica. Al combinar estos constructos más pequeños (como las rutinas RadixN) puede elegir la rutina más rápida y mejor para la tarea en cuestión.
A menos que necesite escribirlo usted mismo para fines de comprensión, FFTW (http://www.fftw.org/) es una gran biblioteca. Es una implementación autoajustable, súper rápida y confiable, y puede llamarla desde C++ sin problemas (consulte http://www.fftw.org/faq/section2.html#cplusplus) –
. Me gustó mucho FFTReal. http://ldesoras.free.fr/prod.html –
¿Por qué escribe su propia implementación en lugar de utilizar una de las innumerables bibliotecas que existen, que probablemente sean todas más rápidas, mejor probadas, más precisas y con más funciones? – PlasmaHH