37

これを matlab central に投稿しましたが、応答がなかったため、ここに再投稿することにしました。

最近、for ループで FFT を使用する簡単なルーチンを Matlab で作成しました。FFT が計算を支配します。実験目的で同じルーチンを mex で作成し、FFTW 3.3 ライブラリを呼び出します。非常に大きな配列の場合、matlab ルーチンは mex ルーチンよりも高速に実行されることがわかります (約 2 倍の速さ)。mex ルーチンは知恵を使い、同じ FFT 計算を実行します。また、matlab が FFTW を使用していることも知っていますが、そのバージョンがもう少し最適化されている可能性はありますか? FFTW_EXHAUSTIVE フラグも使用しましたが、大規模な配列では MATLAB の対応するフラグよりも約 2 倍遅くなりました。さらに、使用した matlab が「-singleCompThread」フラグを使用してシングル スレッドであり、使用した mex ファイルがデバッグ モードではないことを確認しました。これが事実であったかどうか、または私が知らないフードの下でmatlabが使用しているいくつかの最適化があるかどうかに興味があります。ありがとう。

これがメックス部分です:

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を使用しています。ありがとう

4

4 に答える 4

15

私は MATLAB FFT 実装の詳細を知らないため、明確な答えではなくいくつかの観察を行います。

  • あなたが持っているコードに基づいて、速度の違いについて2つの説明を見ることができます:
    • 速度の違いは、FFT の最適化レベルの違いによって説明されます。
    • MATLAB の while ループの実行回数が大幅に減少

すでに 2 番目の問題を調べており、反復回数は同程度であると仮定します。(そうでない場合は、精度の問題が発生している可能性が高く、さらに調査する価値があります。)

さて、FFT速度比較について:

  • はい、FFTW は他の高レベルの FFT 実装よりも高速であるという理論がありますが、リンゴとリンゴを比較する場合にのみ関係があります。ここでは、実装をさらに下のレベル、アセンブリ レベルで比較しています。アルゴリズムの選択ではなく、特定のプロセッサ向けの実際の最適化と、さまざまなスキルを持つソフトウェア開発者による最適化が行われます
  • 私は 1 年以上にわたって多くのプロセッサのアセンブリで最適化された FFT を最適化またはレビューしてきました (私はベンチマーク業界にいました)。優れたアルゴリズムは話の一部にすぎません。コーディングしているアーキテクチャに非常に固有の考慮事項 (レイテンシの考慮、命令のスケジューリング、レジスタ使用の最適化、メモリ内のデータの配置、分岐の発生/不発生の待機時間の説明など) があり、それによって違いが生じます。アルゴリズムの選択と同じくらい重要です。
  • N=500000 では、大きなメモリ バッファについても話しています。コードを実行するプラットフォームにすぐに固有のものになる可能性がある、より多くの最適化へのもう 1 つの扉: キャッシュ ミスをどれだけうまく回避できるかは、データ フローやソフトウェア開発者がデータをメモリに効率的に出し入れするためにどのような最適化を使用したかということだけでなく、アルゴリズムも同様です。
  • 私は MATLAB FFT の実装の詳細を知りませんが、非常に多くの設計の鍵となるため、多くの DSP エンジニアがその最適化に磨きをかけてきたこと (そして今もなお) を確信しています。これは、MATLAB がはるかに高速な FFT を生成するための適切な開発者の組み合わせを持っていたことを意味する可能性があります。
于 2013-03-13T02:09:04.333 に答える