加速稀疏FFT计算

时间:2011-01-25 01:52:29

标签: matlab fft

我希望有人可以查看下面的代码并提供提示如何加快tic和toc之间的部分。下面的函数试图比Matlab的内置函数更快地执行IFFT,因为(1)几乎所有的fft系数区间都是零(即101000区间10M to 300M bin是非零的),以及(2)只保留IFFT结果的中心三分之一(丢弃第一个和后三个 - 所以不需要首先计算它们)。

输入变量是:

fftcoef = complex fft-coef 1D array (10 to 1000 pts long)
bins = index of fft coefficients corresponding to fftcoef (10 to 1000 pts long)
DATAn = # of pts in data before zero padding and fft (in range of 10M to 260M)
FFTn = DATAn + # of pts used to zero pad before taking fft (in range of 16M to 268M) (e.g. FFTn = 2^nextpow2(DATAn))

目前,此代码比Matlab的ifft函数方法要长几个数量级,后者计算整个频谱然后丢弃它的2/3。例如,如果fftcoef和bin的输入数据是9x1数组(即每个边带只有9个复数fft系数;考虑两个边带时18 pts),DATAn=32781534FFTn=33554432(即2^25),然后ifft方法需要1.6秒,而下面的循环需要700秒。

我避免使用矩阵来矢量化nn循环,因为有时fftcoef和bin的数组大小可能长达1000个pts,而260Mx1K矩阵对于内存来说太大了除非它能以某种方式被打破。

非常感谢任何建议!提前谢谢。

function fn_fft_v1p0(fftcoef, bins, DATAn, FFTn)

fftcoef = [fftcoef; (conj(flipud(fftcoef)))];     % fft coefficients
bins = [bins; (FFTn - flipud(bins) +2)];          % corresponding fft indices for fftcoef array

ttrend = zeros( (round(2*DATAn/3) - round(DATAn/3) + 1), 1); % preallocate

start = round(DATAn/3)-1;

tic;
for nn = start+1 : round(2*DATAn/3)  % loop over desired time indices
  % sum over all fft indices having non-zero coefficients
  arg = 2*pi*(bins-1)*(nn-1)/FFTn;
  ttrend(nn-start) = sum( fftcoef.*( cos(arg) + 1j*sin(arg)); 
end
toc;

end

2 个答案:

答案 0 :(得分:3)

你必须记住,Matlab使用编译的fft库(http://www.fftw.org/)来实现其fft函数,除了比Matlab脚本运行速度快得多之外,它还针对许多用例进行了优化。因此,第一步可能是用c / c ++编写代码并将其编译为可在Matlab中使用的mex文件。这肯定会使你的代码加速至少一个数量级(可能更多)。

除此之外,您可以做的一个简单优化是考虑两件事:

  1. 您认为您的时间序列是真正有价值的,因此您可以使用fft系数的对称性。
  2. 你的时间序列通常比你的fft coeffs矢量长得多,所以最好迭代bin而不是时间点(因此矢量化更长的矢量)。
  3. 这两点转换为以下循环:

    nn=(start+1 : round(2*DATAn/3))';
    ttrend2 = zeros( (round(2*DATAn/3) - round(DATAn/3) + 1), 1);
    tic;
    for bn = 1:length(bins)
         arg = 2*pi*(bins(bn)-1)*(nn-1)/FFTn; 
         ttrend2 = ttrend2 +  2*real(fftcoef(bn) * exp(i*arg)); 
    end
    toc;
    

    请注意,在展开binsfftcoef之前,必须先使用 这个循环,因为已经考虑了对称性。使用您问题中的参数运行此循环需要8.3秒,而使用您的代码运行我的pc需要141.3秒。

答案 1 :(得分:0)

我在Accelerating FFTW pruning to avoid massive zero padding发布了一个问题/答案,它解决了使用FFTW的C ++案例的问题。您可以通过利用mex - 文件来使用此解决方案。