私はこれをMatlab Centralに投稿しましたが、応答がなかったので、ここに再投稿すると思いました。
最近、MatlabでforループでFFTを使用する簡単なルーチンを書きました。 FFTが計算を支配します。実験の目的でmexで同じルーチンを作成し、FFTW 3.3ライブラリを呼び出します。非常に大きな配列の場合、matlabルーチンはmexルーチンよりも高速に実行されます(約2倍の速度)。 mexルーチンは知恵を使用し、同じFFT計算を実行します。 matlabがFFTWを使用していることも知っていますが、バージョンが少し最適化されている可能性はありますか? FFTW_EXHAUSTIVEフラグを使用しましたが、大規模な配列の場合、MATLABのフラグよりも約2倍遅くなります。さらに、使用したmatlabが「-singleCompThread」フラグを使用してシングルスレッドであり、使用したmexファイルがデバッグモードではないことを確認しました。これが事実であるか、または私が知らない内部でMATLABが使用している最適化があるかどうかに興味があります。ありがとう。
Mexの部分は次のとおりです。
void class_cg_toeplitz::analysis() {
// This method computes CG iterations using FFTs
// Check for wisdom
if(fftw_import_wisdom_from_filename("cd.wis") == 0) {
mexPrintf("wisdom not loaded.\n");
} else {
mexPrintf("wisdom loaded.\n");
}
// Set FFTW Plan - use interleaved FFTW
fftw_plan plan_forward_d_buffer;
fftw_plan plan_forward_A_vec;
fftw_plan plan_backward_Ad_buffer;
fftw_complex *A_vec_fft;
fftw_complex *d_buffer_fft;
A_vec_fft = fftw_alloc_complex(n);
d_buffer_fft = fftw_alloc_complex(n);
// CREATE MASTER PLAN - Do this on an empty vector as creating a plane
// with FFTW_MEASURE will erase the contents;
// Use d_buffer
// This is somewhat dangerous because Ad_buffer is a vector; but it does not
// get resized so &Ad_buffer[0] should work
plan_forward_d_buffer = fftw_plan_dft_r2c_1d(d_buffer.size(),&d_buffer[0],d_buffer_fft,FFTW_EXHAUSTIVE);
plan_forward_A_vec = fftw_plan_dft_r2c_1d(A_vec.height,A_vec.value,A_vec_fft,FFTW_WISDOM_ONLY);
// A_vec_fft.*d_buffer_fft will overwrite d_buffer_fft
plan_backward_Ad_buffer = fftw_plan_dft_c2r_1d(Ad_buffer.size(),d_buffer_fft,&Ad_buffer[0],FFTW_EXHAUSTIVE);
// Get A_vec_fft
fftw_execute(plan_forward_A_vec);
// Find initial direction - this is the initial residual
for (int i=0;i<n;i++) {
d_buffer[i] = b.value[i];
r_buffer[i] = b.value[i];
}
// Start CG iterations
norm_ro = norm(r_buffer);
double fft_reduction = (double)Ad_buffer.size(); // Must divide by size of vector because inverse FFT does not do this
while (norm(r_buffer)/norm_ro > relativeresidual_cutoff) {
// Find Ad - use fft
fftw_execute(plan_forward_d_buffer);
// Get A_vec_fft.*fft(d) - A_vec_fft is only real, but d_buffer_fft
// has complex elements; Overwrite d_buffer_fft
for (int i=0;i<n;i++) {
d_buffer_fft[i][0] = d_buffer_fft[i][0]*A_vec_fft[i][0]/fft_reduction;
d_buffer_fft[i][1] = d_buffer_fft[i][1]*A_vec_fft[i][0]/fft_reduction;
}
fftw_execute(plan_backward_Ad_buffer);
// Calculate r'*r
rtr_buffer = 0;
for (int i=0;i<n;i++) {
rtr_buffer = rtr_buffer + r_buffer[i]*r_buffer[i];
}
// Calculate alpha
alpha = 0;
for (int i=0;i<n;i++) {
alpha = alpha + d_buffer[i]*Ad_buffer[i];
}
alpha = rtr_buffer/alpha;
// Calculate new x
for (int i=0;i<n;i++) {
x[i] = x[i] + alpha*d_buffer[i];
}
// Calculate new residual
for (int i=0;i<n;i++) {
r_buffer[i] = r_buffer[i] - alpha*Ad_buffer[i];
}
// Calculate beta
beta = 0;
for (int i=0;i<n;i++) {
beta = beta + r_buffer[i]*r_buffer[i];
}
beta = beta/rtr_buffer;
// Calculate new direction vector
for (int i=0;i<n;i++) {
d_buffer[i] = r_buffer[i] + beta*d_buffer[i];
}
*total_counter = *total_counter+1;
if(*total_counter >= iteration_cutoff) {
// Set total_counter to -1, this indicates failure
*total_counter = -1;
break;
}
}
// Store Wisdom
fftw_export_wisdom_to_filename("cd.wis");
// Free fft alloc'd memory and plans
fftw_destroy_plan(plan_forward_d_buffer);
fftw_destroy_plan(plan_forward_A_vec);
fftw_destroy_plan(plan_backward_Ad_buffer);
fftw_free(A_vec_fft);
fftw_free(d_buffer_fft);
};
これがMATLABの部分です:
% Take FFT of A_vec.
A_vec_fft = fft(A_vec); % Take fft once
% Find initial direction - this is the initial residual
x = zeros(n,1); % search direction
r = zeros(n,1); % residual
d = zeros(n+(n-2),1); % search direction; pad to allow FFT
for i = 1:n
d(i) = b(i);
r(i) = b(i);
end
% Enter CG iterations
total_counter = 0;
rtr_buffer = 0;
alpha = 0;
beta = 0;
Ad_buffer = zeros(n+(n-2),1); % This holds the product of A*d - calculate this once per iteration and using FFT; only 1:n is used
norm_ro = norm(r);
while(norm(r)/norm_ro > 10^-6)
% Find Ad - use fft
Ad_buffer = ifft(A_vec_fft.*fft(d));
% Calculate rtr_buffer
rtr_buffer = r'*r;
% Calculate alpha
alpha = rtr_buffer/(d(1:n)'*Ad_buffer(1:n));
% Calculate new x
x = x + alpha*d(1:n);
% Calculate new residual
r = r - alpha*Ad_buffer(1:n);
% Calculate beta
beta = r'*r/(rtr_buffer);
% Calculate new direction vector
d(1:n) = r + beta*d(1:n);
% Update counter
total_counter = total_counter+1;
end
時間に関しては、N = 50000およびb = 1:nの場合、mexでは約10.5秒、Matlabでは4.4秒かかります。 R2011bを使用しています。ありがとう
MATLAB FFT実装の詳細がわからないので、明確な答えではなくいくつかの観察:
すでに2番目の問題を検討しており、反復回数は同等であると想定します。 (そうでない場合、これはいくつかの精度の問題である可能性が最も高く、さらに調査する価値があります。)
ここで、FFT速度の比較について:
これは、低レベルのアーキテクチャ固有の最適化により、従来のパフォーマンスが向上しています。
MatlabはIntelのFFTを使用します [〜#〜] mkl [〜#〜] (Math Kernel Library)バイナリ(mkl.dll)。これらは、インテルによってインテルプロセッサー用に(アセンブリーレベルで)最適化されたルーチンです。 AMDでも、Niceパフォーマンスが向上するようです。
FFTWは、最適化されていない通常のCライブラリのようです。したがって、MKLを使用するとパフォーマンスが向上します。
MathWorks Webサイト[1]で次のコメントを見つけました。
2のべき乗に注意:2のべき乗であるFFT次元が2 ^ 14から2 ^ 22の場合、MATLABソフトウェアは内部データベースに事前に読み込まれた特別な情報を使用してFFT計算を最適化します。コマンドfftw( 'wisdom'、[])を使用してデータベースをクリアしない限り、FTTの次元が2の累乗である場合、チューニングは実行されません。
これは2の累乗に関係しますが、特定の(大きな)配列サイズにFFTWを使用する場合、MATLABは独自の「特別な知識」を使用することを示唆している場合があります。考慮してください:2 ^ 16 = 65536。
[1] R2013bドキュメントは http://www.mathworks.de/de/help/matlab/ref/fftw.html から入手できます(2013年10月29日にアクセス)
編集:この回答に対する@wakjahの回答は正確です。FFTWは、Guruインターフェースを介して実メモリと虚メモリの分割をサポートしています。したがって、ハッキングについての私の主張は正確ではありませんが、FFTWのGuruインターフェースが使用されていない場合には非常によく当てはまります。これはデフォルトのケースですので、注意してください。
まず、一年遅れてすみません。表示される速度の向上がMKLまたはその他の最適化によるものであるとは思いません。 FFTWとMatlabの間には根本的にかなり異なる点があり、それがメモリに複雑なデータが格納される方法です。
Matlabでは、複素数ベクトルXの実数部と虚数部は別々の配列Xre [i]とXim [i]です(メモリ内で線形、これらのいずれかを別々に操作する場合に効率的)。
FFTWでは、実数部と虚数部はデフォルトでdouble [2]としてインターレースされます。つまり、X [i] [0]は実数部であり、X [i] [1]は虚数部です。
したがって、mexファイルでFFTWライブラリを使用するには、Matlab配列を直接使用することはできませんが、最初に新しいメモリを割り当ててから、Matlabからの入力をFFTW形式にパックし、次にFFTWからの出力をMatlab形式にアンパックする必要があります。つまり.
_X = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
Y = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
_
その後
_for (size_t i=0; i<N; ++i) {
X[i][0] = Xre[i];
X[i][1] = Xim[i];
}
_
その後
_for (size_t i=0; i<N; ++i) {
Yre[i] = Y[i][0];
Yim[i] = Y[i][1];
}
_
したがって、これには2倍のメモリ割り当て+ 4倍の読み取り+ 4倍の書き込み-サイズNのすべてが必要です。これは、大規模な問題では速度的に非常に時間がかかります。
MathworksがFFTW3コードをハッキングして、Matlab形式で入力ベクトルを直接読み取れるようにして、上記のすべてを回避しているのではないかと思います。
このシナリオでは、Xを割り当て、YにXを使用してFFTWをインプレースで実行できます(fftw_plan_*(N, X, X, ...)
ではなくfftw_plan_*(N, X, Y, ...)
として)。これは、Yreにコピーされ、 Yim Matlabベクトル。ただし、アプリケーションがXとYを別々にしておく必要がある、またはメリットがない場合。
[〜#〜] edit [〜#〜]:Matlabのfft2()およびfftw3ライブラリに基づいた私のコードを実行しているときにリアルタイムでメモリ消費量を確認すると、Matlabは割り当てのみ1つの追加の複雑な配列(出力)ですが、私のコードには2つの配列(_*fftw_complex
_バッファーとMatlab出力)が必要です。 Matlabの実数配列と虚数配列はメモリ内で連続していないため、Matlab形式とfftw形式間のインプレース変換は不可能です。これは、Mathworksがfftw3ライブラリをハッキングして、Matlab形式を使用してデータを読み書きしたことを示唆しています。
複数の呼び出しのもう1つの最適化は、永続的に割り当てることです(mexMakeMemoryPersistent()
を使用)。 Matlabの実装でもこれが可能かどうかはわかりません。
乾杯。
pS補足として、Matlabの複雑なデータストレージ形式は、実数ベクトルまたは虚数ベクトルを個別に操作する場合により効率的です。 FFTWのフォーマットでは、++ 2のメモリ読み取りを行う必要があります。