如何编写C代码以实现长信号和长内核卷积

时间:2019-01-08 15:40:20

标签: c r fft convolution dft

我想对一个内核长度为16000的长度为4000 * 270的信号进行线性卷积。该信号不是固定的,而内核是固定的。为了我的目的,需要多次重复此操作,因此我想尽快提高速度。我可以用R或C来实现这种卷积。

起初,我尝试在R中进行卷积,但是速度不能满足我的需要。我尝试通过迭代来完成,但是速度太慢。我也尝试过使用FFT进行此操作,但是由于信号和内核都很长,因此FFT并没有提高很多速度。

然后,我决定在C中进行迭代卷积。但是C似乎无法处理如此大量的计算,并且经常报告错误。即使工作正常,它仍然非常缓慢。我也尝试在C中进行fft卷积,但是程序始终关闭。

我从我的一个朋友那里找到了这段代码,不确定原始来源。如果有版权问题,我将其删除。这是我在C中执行fft所使用的C代码,但程序无法处理长度为2097152的长向量(最小幂2大于或等于信号向量长度) )。

#define q    3        /* for 2^3 points */
#define N    2097152        /* N-point FFT, iFFT */

typedef float real;
typedef struct{real Re; real Im;} complex;

#ifndef PI
# define PI    3.14159265358979323846264338327950288
#endif


void fft( complex *v, int n, complex *tmp )
                   {
if(n>1) {            /* otherwise, do nothing and return */
  int k,m;    
  complex z, w, *vo, *ve;
  ve = tmp; 
  vo = tmp+n/2;

  for(k=0; k<n/2; k++) {
          ve[k] = v[2*k];
          vo[k] = v[2*k+1];
      }
  fft( ve, n/2, v );        /* FFT on even-indexed elements of v[] */
  fft( vo, n/2, v );        /* FFT on odd-indexed elements of v[] */
  for(m=0; m<n/2; m++) {
      w.Re = cos(2*PI*m/(double)n);
      w.Im = -sin(2*PI*m/(double)n);
      z.Re = w.Re*vo[m].Re - w.Im*vo[m].Im;    /* Re(w*vo[m]) */
      z.Im = w.Re*vo[m].Im + w.Im*vo[m].Re;    /* Im(w*vo[m]) */
      v[  m  ].Re = ve[m].Re + z.Re;
      v[  m  ].Im = ve[m].Im + z.Im;
      v[m+n/2].Re = ve[m].Re - z.Re;
      v[m+n/2].Im = ve[m].Im - z.Im;
      }
      }
  return;
       }

  void ifft( complex *v, int n, complex *tmp )
          {
    if(n>1) {            /* otherwise, do nothing and return */
           int k,m;    
           complex z, w, *vo, *ve;
           ve = tmp; 
           vo = tmp+n/2;
    for(k=0; k<n/2; k++) {
          ve[k] = v[2*k];
          vo[k] = v[2*k+1];
          }
    ifft( ve, n/2, v );        /* FFT on even-indexed elements of v[] */
    ifft( vo, n/2, v );        /* FFT on odd-indexed elements of v[] */
    for(m=0; m<n/2; m++) {
            w.Re = cos(2*PI*m/(double)n);
            w.Im = sin(2*PI*m/(double)n);
            z.Re = w.Re*vo[m].Re - w.Im*vo[m].Im;    /* Re(w*vo[m]) */
            z.Im = w.Re*vo[m].Im + w.Im*vo[m].Re;    /* Im(w*vo[m]) */
            v[  m  ].Re = ve[m].Re + z.Re;
            v[  m  ].Im = ve[m].Im + z.Im;
            v[m+n/2].Re = ve[m].Re - z.Re;
            v[m+n/2].Im = ve[m].Im - z.Im;
            }
            }
        return;
        }

我发现此页面谈论长信号卷积https://ccrma.stanford.edu/~jos/sasp/Convolving_Long_Signals.html 但是我不确定如何在其中使用这个想法。任何想法都会受到感激,我准备提供有关我的问题的更多信息。

1 个答案:

答案 0 :(得分:0)

根据您引用的CCRMA论文,最常见的高效长FIR滤波器方法是使用FFT / IFFT重叠相加(或重叠保存)快速卷积。只需将数据切成更适合FFT库和处理器数据高速缓存大小的较短块,至少填充滤波器内核长度,FFT滤波器零填充,然后在每次IFFT之后依次叠加-添加余数/尾数。

巨大的长FFT很可能会破坏处理器的缓存,这很可能在任何算法O(NlogN)加速中都占主导地位。