我有一些C++代码,随着时间的推移,它已成为一个相当有用的FFT库,并且已经使用SSE和AVX指令使其运行得相当快。当然,这全部只是基于radix-2算法,但仍然保持不错的效果。我的最新想法是让蝴蝶计算使用FMA指令。基本的radix-2蝴蝶包括4个乘法和6个加减法。简单的方法是将其中2个加减法和2个乘法替换为2个FMA指令,从而得到数学上相同的蝴蝶,但显然有更好的方法来实现这一点:
作者用6个FMA替换了所有的10个加、减和乘法,前提是扭转因子的虚部被实部除尽。其中一部分文字写道“注意cr1 != 0”。这基本上就是我的问题所在。对于所有的扭转因子,数学似乎都像广告中宣传的那样工作得很好,除了当实际扭转为零时,我们最终会除以零。在这里效率非常关键,当cr1 == 0时将代码分支到不同的蝴蝶形式并不是一个好的选择,特别是当我们使用SIMD同时处理多个扭转和蝴蝶形式时,也许只有cr1的一个元素等于0。我的直觉告诉我,当cr1 == 0时,cr1和ci1应该是完全不同的值,而FMA代码仍然会得出正确的答案,但我似乎无法弄清楚这一点。如果我能搞清楚,修改预计算的FMA蝴蝶形式的扭转因子将是一个相对简单的事情,当然,我们还可以避免在蝴蝶形式开始时进行除法运算。
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同时处理多个扭转和蝴蝶形式时,也许只有cr1的一个元素等于0。我的直觉告诉我,当cr1 == 0时,cr1和ci1应该是完全不同的值,而FMA代码仍然会得出正确的答案,但我似乎无法弄清楚这一点。如果我能搞清楚,修改预计算的FMA蝴蝶形式的扭转因子将是一个相对简单的事情,当然,我们还可以避免在蝴蝶形式开始时进行除法运算。