Optimization 快速FFT位反转,我可以倒计时位反转吗?

Optimization 快速FFT位反转,我可以倒计时位反转吗?,optimization,fft,reverse,bit,Optimization,Fft,Reverse,Bit,我正在使用FFT进行音频处理,我已经想出了一些可能非常快速的方法来进行所需的位反转,这可能对其他人有用,但由于我的FFT(8192)的大小,我正在尝试减少内存使用/缓存刷新,以减小查找表或代码的大小,并提高性能。我见过很多聪明的位反转程序;它们都允许您向它们提供任意值,并获得稍微反转的输出,但FFT不需要这种灵活性,因为它们是按可预测的顺序进行的。首先,让我说明我已经尝试和/或想出了什么,因为它可能是迄今为止最快的,而且你可以看到问题,然后我会问这个问题 1) 我编写了一个程序来生成直接的、未加

我正在使用FFT进行音频处理,我已经想出了一些可能非常快速的方法来进行所需的位反转,这可能对其他人有用,但由于我的FFT(8192)的大小,我正在尝试减少内存使用/缓存刷新,以减小查找表或代码的大小,并提高性能。我见过很多聪明的位反转程序;它们都允许您向它们提供任意值,并获得稍微反转的输出,但FFT不需要这种灵活性,因为它们是按可预测的顺序进行的。首先,让我说明我已经尝试和/或想出了什么,因为它可能是迄今为止最快的,而且你可以看到问题,然后我会问这个问题

1) 我编写了一个程序来生成直接的、未加载的x86源代码,可以粘贴到我的FFT代码中,该代码读取音频样本,将其乘以一个窗口值(这是一个查找表本身)然后在x86寻址模式(如:movlps[edi+1876]、xmm0)中,将结果值按绝对值放入正确的位反转排序位置。对于较小的FFT大小,这是绝对最快的方法。问题是,当我直接编写代码来处理8192个值时,代码会超出一级指令缓存的大小,性能会下降。当然,与此相反,32K位反转查找表与32K窗口表混合,再加上其他内容,也太大,无法容纳一级数据缓存,性能会下降,但我目前正在这样做

2) 我在位反转序列中发现了可以用来减少查找表大小的模式,例如使用4位数字(0..15)作为示例,位反转序列看起来像:0,8,4,12,2,10,6,14 | 1,5,9,13,3,11,7,15。首先可以看到的是,最后8个数字与前8+1相同,因此我可以将LUT切掉一半。如果我看两个数字之间的差异,会有更多的冗余,因此如果我从寄存器中的零开始,并想向其中添加值,以得到下一个位反转的数字,它们将是:+0、+8、-4、+8、-10、+8、-4、+8,下半部分相同。可以看出,我的查找表可能只有0和-10,因为+8和-4总是以可预测的方式显示。代码将展开以处理每个循环的4个值:一个是读取的查找表,另3个是+8、-4、+8的直接代码,然后再次循环。然后第二个循环可以处理1,5,9,13,3,11,7,15序列。这很好,因为我现在可以将我的查找表再缩减4倍。这与8192大小的FFT的放大方式相同。我现在可以使用4K大小的LUT而不是32K。我可以利用相同的模式,将代码的大小增加一倍,再将LUT减少一半,不管我想走多远。但是为了完全消除LUT,我回到了禁止的代码大小

对于较大的FFT大小,我相信这个#2解决方案是迄今为止绝对最快的,因为需要进行相对较小百分比的查找表读取,并且我目前在web上找到的每个算法都需要太多无法矢量化的串行/依赖计算


问题是,是否有一种算法可以增加数字,使MSB像LSB一样工作,等等?换句话说(二进制):0000、1000、0100、1100、0010等等……我试着想出了一些方法,到目前为止,由于没有一堆嵌套循环,我似乎找不到一种快速简单的算法,即简单地将1添加到数字的LSB的镜像。然而,似乎应该有一种方法。

另一种方法需要考虑:采用一种众所周知的位反转算法——通常是一些掩码、移位和OR——然后用SSE实现这一点,因此您可以以一个的价格获得8 x 16位的反转。对于16位,您需要5*log2(N)=20条指令,因此聚合吞吐量为每位反转2.5条指令。

这是最简单、最直接的解决方案(在C中):

无效位反转增量(无符号*var,整数位)
{
无符号c,1=1u>=1;
}while(one&c);
}
条件分支的主要问题是,在现代CPU上,条件分支通常代价高昂。每一位有一个条件分支

您可以通过一次处理多个位来执行反向增量,例如,如果整数为32位,则为3:

void BitReversedIncrement2(unsigned *var, int bit)
{
  unsigned r = *var, t = 0;

  while (bit >= 2 && !t)
  {
    unsigned tt = (r >> (bit - 2)) & 7;
    t = (07351624 >> (tt * 3)) & 7;
    r ^= ((tt ^ t) << (bit - 2));
    bit -= 3;
  }

  if (bit >= 0 && !t)
  {
    t = r & ((1 << (bit + 1)) - 1);
    r ^= t;
    t <<= 2 - bit;
    t = (07351624 >> (t * 3)) & 7;
    t >>= 2 - bit;
    r |= t;
  }

  *var = r;
}
无效位反转增量2(无符号*var,整数位)
{
无符号r=*var,t=0;
while(位>=2&&!t)
{
无符号tt=(r>>(位-2))&7;
t=(07351624>(tt*3))&7;
r^=((tt^t)=0&&!t)
{
t=r&(1>=2位;
r |=t;
}
*var=r;
}
这样更好,每3位只有1个条件分支

如果您的CPU支持64位整数,则一次可以使用4位:

void BitReversedIncrement3(unsigned *var, int bit)
{
  unsigned r = *var, t = 0;

  while (bit >= 3 && !t)
  {
    unsigned tt = (r >> (bit - 3)) & 0xF;
    t = (0xF7B3D591E6A2C48ULL >> (tt * 4)) & 0xF;
    r ^= ((tt ^ t) << (bit - 3));
    bit -= 4;
  }

  if (bit >= 0 && !t)
  {
    t = r & ((1 << (bit + 1)) - 1);
    r ^= t;
    t <<= 3 - bit;
    t = (0xF7B3D591E6A2C48ULL >> (t * 4)) & 0xF;
    t >>= 3 - bit;
    r |= t;
  }

  *var = r;
}
无效位反转增量3(无符号*var,整数位)
{
无符号r=*var,t=0;
而(位>=3&&!t)
{
无符号tt=(r>>(位-3))&0xF;
t=(0xF7B3D591E6A2C48ULL>>(tt*4))&0xF;
r^=((tt^t)=0&&!t)
{
t=r&(1>=3位;
r |=t;
}
*var=r;
}
唯一的查找表(07351624或0xF7B3D591E6A2C48)很小,很可能编码为立即指令操作数


如果反转“1”的位位置是已知常量,则可以进一步改进代码。只需将while循环展开为嵌套的ifs,替换反转的一位位置常量。

对于较大的FFT,请注意缓存阻塞(最小化未覆盖的缓存未命中循环总数)对性能的影响可能远远大于通过索引位反转进行的循环计数优化。在优化较小的影响时,确保不要通过较大的循环计数对较大的影响进行反优化。对于小型FFT,所有内容都适合缓存,LUT可以是一个很好的解决方案,只要您注意任何负载使用危险通过使表面光滑
void BitReversedIncrement3(unsigned *var, int bit)
{
  unsigned r = *var, t = 0;

  while (bit >= 3 && !t)
  {
    unsigned tt = (r >> (bit - 3)) & 0xF;
    t = (0xF7B3D591E6A2C48ULL >> (tt * 4)) & 0xF;
    r ^= ((tt ^ t) << (bit - 3));
    bit -= 4;
  }

  if (bit >= 0 && !t)
  {
    t = r & ((1 << (bit + 1)) - 1);
    r ^= t;
    t <<= 3 - bit;
    t = (0xF7B3D591E6A2C48ULL >> (t * 4)) & 0xF;
    t >>= 3 - bit;
    r |= t;
  }

  *var = r;
}