簡體   English   中英

FFTW與Matlab FFT

[英]FFTW vs Matlab FFT

我在matlab中心發布了這個,但沒有得到任何回復所以我想我會轉發到這里。

我最近在Matlab中編寫了一個簡單的例程,它在for循環中使用FFT; FFT主導了計算。 我在mex中編寫了相同的例程僅用於實驗目的,它調用了FFTW 3.3庫。 事實證明,對於非常大的數組,matlab例程比mex例程運行得更快(大約快兩倍)。 mex例程使用智慧並執行相同的FFT計算。 我也知道matlab使用FFTW,但它們的版本是否可能稍微優化一下? 我甚至使用了FFTW_EXHAUSTIVE標志,它對大型數組的速度仍然是MATLAB的兩倍。 此外,我確保我使用的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實現的任何細節,所以有一些觀察而不是確定的答案:

  • 根據您的代碼,我可以看到兩種速度差異的解釋:
    • 速度差異由FFT優化級別的差異來解釋
    • MATLAB中的while循環執行次數要少得多

我將假設你已經研究過第二個問題,並且迭代次數是可比的。 (如果不是,這很可能是一些准確性問題,值得進一步調查。)

現在,關於FFT速度比較:

  • 是的,理論上說FFTW比其他高級FFT實現更快,但只要你比較蘋果和蘋果,它就是相關的:在這里你要比較下一級的實現,在匯編級別,不僅是選擇算法但其對特定處理器的實際優化以及具有不同技能的軟件開發人員正在發揮作用
  • 我在一年中在許多處理器上組裝優化或審查優化的FFT(我在基准測試行業),優秀的算法只是故事的一部分。 有些注意事項對您編碼的體系結構非常具體(考慮延遲,指令調度,寄存器使用優化,內存中數據排列,分支采用/不采用延遲等)以及產生差異的因素與選擇算法同樣重要。
  • 在N = 500000的情況下,我們還討論了大型內存緩沖區:更多優化的另一扇門,它可以快速地為您運行代碼的平台提供特定的功能:您如何設法避免緩存未命中將不會受到算法,以及數據流如何以及軟件開發人員可能使用哪些優化來有效地將數據輸入和輸出內存。
  • 雖然我不知道MATLAB FFT實現的細節,但我很確定一大批DSP工程師已經(並且仍然)在優化它,因為它是這么多設計的關鍵。 這很可能意味着MATLAB擁有正確的開發人員組合,可以產生更快的FFT。

由於低級和體系結構特定的優化,這是經典的性能提升。

Matlab使用來自英特爾MKL (數學核心庫)二進制文件(mkl.dll)的FFT。 這些是英特爾針對英特爾處理器優化(在匯編級別)的例程。 即便在AMD上,它似乎也提供了不錯的性能提升。

FFTW看起來像普通的c庫,沒有經過優化。 因此使用MKL的性能提升。

我在MathWorks網站上發現了以下評論[1]:

關於2的大功率的注意事項:對於功率為2的FFT維度,在2 ^ 14和2 ^ 22之間,MATLAB軟件在其內部數據庫中使用特殊的預加載信息來優化FFT計算。 當FTT的維數為2的冪時,不執行調整,除非使用命令fftw('wisdom',[])清除數據庫。

雖然它與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中,實部和虛部默認交錯為雙[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];
}

因此,這需要2x內存分配+ 4x讀取+ 4x寫入 - 全部大小為N.這對大問題確實需要加速。

我有一種預感,Mathworks可能已經破解了FFTW3代碼,使其能夠直接以Matlab格式讀取輸入向量,從而避免了上述所有情況。

在這種情況下,只能分配X並使用X作為Y來就地運行FFTW(如fftw_plan_*(N, X, X, ...)而不是fftw_plan_*(N, X, Y, ...) ),因為它將被復制到Yre和Yim Matlab向量,除非應用程序需要/受益於保持X和Y分開。

編輯 :在運行Matlab的fft2()和基於fftw3庫的代碼時實時查看內存消耗,它表明Matlab只分配一個額外的復雜數組(輸出),而我的代碼需要兩個這樣的數組( *fftw_complex緩沖區加上Matlab輸出)。 Matlab和fftw格式之間的就地轉換是不可能的,因為Matlab的實數和虛數陣列在內存中不是連續的。 這表明Mathworks攻擊了fftw3庫,使用Matlab格式讀/寫數據。

多次調用的另一個優化是持久分配(使用mexMakeMemoryPersistent() )。 我不確定Matlab實現是否也能做到這一點。

干杯。

ps作為旁注,Matlab復雜數據存儲格式對於分別對實數或虛數向量進行操作更有效。 在FFTW格式上,您必須進行++ 2內存讀取。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM