我有两个8个无符号字节的序列,我需要计算它们的循环卷积,它产生8个无符号19位整数。当我重复这一百万次时,我想进行优化。
直接的方式需要64次MAC操作。我已经知道如何通过SSE / AVX指令加速这一点,这不是我想要的。
还有另一种方法,可能是基于FFT或数论变换来减少操作次数或其他技术以获得一些加速?
实际上,我不需要8个值:最大值和相应的移位就足够了。
答案 0 :(得分:1)
循环卷积可以通过对每个输入进行离散傅立叶变换(DFT),乘以变换,并采用逆DFT来计算。使用快速傅里叶变换算法,可以在N*log(N)
运算中计算DFT及其逆,然后另一个N运算来乘以变换。粗略地说,您需要3N*log(N)+N
个操作,对于您的输入大小为8,其操作为80.而且,FFT方法中的操作是复数操作,而不仅仅是MAC。
然而,还有一个优化:由于输入数据是真实的,您可以在不丢失信息的情况下表示N / 2 + 1复杂点的变换。存在利用此属性的实值变换(和逆变换)。根据经验,这相当于做一半长的变换。因此,如果我们将4插入3N*log(N)+N
,我们得到28.现在我们需要考虑复数问题:复数乘法是两个乘法,并且每个实数和虚数组件都有一个加法。所以每个复杂的op大约相当于3个MAC,我们发现它仍然比直接卷积慢。
随着数据规模的扩大,FFT方法确实开始取得成效。如果您使用的是2048长输入,则操作次数为3 * 10240 + 1024 = 34k操作。对于复数开销,即使乘以3也是如此,这与直接实现的~4M操作相比非常有利。
FFT方法值得考虑的另一种情况是,如果您需要将一个阵列与其他阵列进行卷积,或者全部针对所有阵列进行卷积。在这种情况下,您可以计算输入转换一次并重复使用它们。对于K序列,如果需要进行所有K ^ 2交叉卷积,则可以执行K变换,K ^ 2复数阵列乘法和K ^ 2逆变换。对于10个输入大小为8的数组,这小于1500次复数运算(10*4*log(4) + 500 + 100*4*log(4)
用于输入,变换乘法和输出)。采用直接方法需要100*64
个MAC,因此FFT方法胜出。
但是对于你的情况来说,一个好的直接实现似乎将成为赢家。
答案 1 :(得分:1)
在“快速傅立叶变换和卷积算法”中,Nussbaumer报告了一种优化的方法,使用14次乘法和46次加法(在实数上)计算8个项的卷积。我怀疑使用标准算法可以做得更好。
我觉得费马/欧拉数变换是相关的,但我无法填写细节。