我有一些 C++ 代码,随着时间的推移,它已经成为一个有用的 FFT 库,并且使用 SSE 和 AVX 指令使其运行得相当快。当然,这一切都只是基于 radix-2 算法,但它仍然成立。我最近想抓挠的是让蝴蝶计算与 FMA 指令一起工作。基本的 radix-2 蝴蝶由 4 个乘法和 6 个加法或减法组成。一个简单的方法是用 2 个 FMA 指令替换 2 个加减法和 2 个乘法,从而得到一个数学上相同的蝶形,但显然有更好的方法来做到这一点:
ci1 = ci1 / cr1
u0 = zinr(0)
v0 = zini(0)
r = zinr(1)
s = sini(1)
u1 = r - s * ci1
v1 = r * ci1 + s
zoutr(0) = u0 + u1 * cr1
zouti(0) = v0 + v1 * cr1
zoutr(1) = u0 - u1 * cr1
zouti(1) = v0 - v1 * cr1
作者用 6 个 FMA 替换了所有 10 个加法、减法和乘法,前提是旋转因子的虚部除以实部。部分文本为“注意 cr1 != 0”。简而言之,这本质上是我的问题。数学似乎与宣传的所有旋转因子一样有效,除非真正的旋转为零,在这种情况下,我们最终除以零。在这里效率绝对至关重要,当 cr1 == 0 时将代码分支到不同的蝴蝶不是一个好的选择,特别是当我们使用 SIMD 一次处理多个 twiddles 和蝴蝶时,其中可能只有一个 cr1 == 的元素0. 我的直觉告诉我应该是这样,当 cr1 == 0 时,cr1 和 ci1 应该完全是其他一些值,并且 FMA 代码仍然会产生正确的答案,但我似乎无法弄清楚这一点。如果我能弄清楚,修改 FMA 蝴蝶的预先计算的旋转因子将是一件相对简单的事情,当然,我们也可以避免蝴蝶开始时的除法运算。