2011-01-25 28 views
2

私は以下のコードを見直して、チックとトックの間のセクションをスピードアップするヒントを提供したいと考えています。以下の関数は、(1)fft係数ビンの殆どがゼロである(すなわち、,〜300M個のビンが0でない〜1000個のビン)、(2)ビンが0であるため、Matlabのビルトイン関数より高速にIFFTを実行しようとします。 IFFT結果の中央3分の1だけが保持されます(最初と最後の3分の1は破棄されるため、最初に計算する必要はありません)。スパースFFT計算の高速化

入力変数は以下のとおりです。

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)) 

現在、このコードが長く、スペクトル全体がそれの2/3年代を破棄計算のMatlabのifft関数アプローチよりも数桁をとります。例えば、fftcoefとビンのための入力データが9x1配列である場合、(すなわち2^25)(側波帯あたりすなわちのみ9複雑なFFT係数の両方の側波帯を考慮した場合18 PTS)、及びDATAn=32781534FFTn=33554432、次にIFFTアプローチは、一方1.6秒かかり下のループは700秒を引き継ぎます。

fftcoefとbinsの配列サイズが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 
+0

潜在的な節約の分析については、http://www.fftw.org/pruned.htmlを参照してください。それは価値がないかもしれません。 – mtrw

+0

'2 * length(bins)/ 3> lg(DAtan(2 * DATAn/3)')の場合、FFTのアプローチでは 'DATAn * ) '(FFTWは非2の変換サイズを扱うので、私はゼロパディングを無視している)。 10個のビンと2^25の出力ポイントの場合は、'20/3> 25 'であり、潜在的な3倍の改善要因です。あなたが75のFFT coeffになるとすぐに、あなたは優位性を失いました。そして、アルゴリズムをCでコーディングし、それを維持する必要があります。 – mtrw

+0

ありがとうmtrw、私は数日前にリンクを見直しました。 「このため、出力の1%以下を望んでいないかぎり(および/または入力の1%以下がゼロ以外の場合)、刈り込まれた1次元FFTを検討するのはお勧めできません。 "私の場合、入力の0.00001%(IDFTに対する係数)はゼロではありません。私はこれがあなたが上に引用した3つの改善の要因ではなく、スピードの増加の支配的理由であるべきだと思います。 – ggkmath

答えて

3

あなたは、MATLABがMATLABスクリプトその後、はるかに高速に動作させる以外にも、それは、多くのユースケース用に最適化され、そのFFT関数、用にコンパイルFFTライブラリ(http://www.fftw.org/)を使用して心に留めておく必要があります。だから最初のステップはあなたのコードをc/C++で書いて、それをMatlab内で使うことができるmexファイルとしてコンパイルすることです。それは確かに少なくともあなたのコードのスピードを上げるでしょう(もっと多分)。

  1. あなたは、あなたは、FFT coeffsの対称性を利用することができますので、お時間のシリーズは、本物の、評価されていると仮定します。そのほかに

    は、あなたが行うことができます1つの単純な最適化は、2つのことを考慮しています。

  2. あなたの時系列は通常、あなたのfft coeffsベクトルよりもはるかに長いので、時間点の代わりにビンを反復する方が良い(長いベクトルをベクトル化する方がよい)。

これら二点は、次のループに翻訳されています

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秒かかりますが、あなたのコードを実行するには141.3秒かかります。

+0

こんにちはItamar Katz、素晴らしい提案。上記の#2の提案とコードを使用して、改善のために上に示したものと同様の数字が得られます。私はあなたの提案#1を上記のように(対称性を利用するために)コードする方法は確かではありません。もっと説明できますか? – ggkmath

+0

ああ、あなたは既に上記の "2 *本当の"コードを含めることで提案1を含んでいます。そして、fftcoef = [...]とbins = [...の2行をコメントアウトします]。ニースのトリック、それは半分の時間を削減します。そして、ビンとfftcoefを拡張する理由はありません。 – ggkmath

+0

はい、正確です。対称性は、実数値データの場合、k'th coeffは(Nk) 'th coeffの複素共役であるため、各項とその共役を2 *実数(...)に合計することができます。 –