FFTW VS Matlab的FFT(FFTW vs Matlab FFT)

2019-07-21 05:14发布

我张贴这种MATLAB的中央,但是,所以我想我会在这里转贴没有得到任何回应。

我最近写在Matlab一个简单的步骤,使用在FFT for循环; 在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秒,4.4秒用MATLAB。 我使用R2011b。 谢谢

Answer 1:

一些意见,而不是一个确切的答案,因为我不知道任何MATLAB的FFT实施的具体情况:

  • 根据你的代码,我可以看到的速度差两种解释:
    • 速度差异是由不同的FFT优化的水平来解释
    • 在MATLAB中而循环执行的次数显著较小数量

我会假设你已经调查了第二个问题,而且迭代的次数是可比的。 (如果不是,这是最有可能的一些精确性的问题,值得进一步研究。)

现在,关于FFT速度对比:

  • 是的,这个理论是说FFTW比其他高层次的FFT实现速度更快,但因为你比较苹果,苹果是唯一的,只要相关的:在一定的水平在这里你是比较实现进一步下跌,在组件级别,这里不仅有该算法,但对于一个特定的处理器和软件开发人员具有不同技能的实际优化的选择来在作怪
  • 我已经优化或审查了过去一年多处理器的汇编优化的FFT(我是在基准工业)和伟大的算法是故事的一部分。 有考虑,是非常具体的,你是编码(占潜伏期,指令调度,寄存器使用的优化,内存中的数据排列,占分行采取/不采取延迟等)的结构和构成的差异作为算法的选择同样重要。
  • 随着N = 500000我们也在谈论大内存缓冲区:另一个门更多的优化,能够迅速得到相当具体到你运行你的代码的平台:您管理如何避免高速缓存未命中将不会被来决定算法这么多的数据如何流动和优化什么软件开发人员可能用于进出内存带来的高效数据。
  • 虽然我不知道MATLAB FFT实现的细节,我敢肯定,DSP工程师的军队已经(和仍然是)在其优化的磨练,因为它是关键,这么多的设计。 这很可能意味着,MATLAB有开发商的正确组合,以产生更快的FFT。


Answer 2:

这是经典的性能增益得益于低级别和具体的体系结构进行优化。

MATLAB的使用FFT从Intel MKL (数学核心函数库)二进制(mkl.dll)。 这些是由英特尔英特尔处理器优化(在组装电平)例程。 即使在AMD的它似乎给了不错的性能提升。

FFTW,似乎是没有优化的一个正常的C库。 因此,性能增益使用MKL。



Answer 3:

我已经找到了MathWorks公司的网站[1]以下评论:

注意上的2个大的权力:对于FFT尺寸是2的幂,在2 ^ 14和2 ^ 22,MATLAB软件在其内部数据库中使用特殊的预载的信息来优化FFT运算。 当FTT的尺寸是2的幂,除非使用命令FFTW清除数据库不执行调谐,(“智慧”,[])。

虽然涉及的2的幂,它可能暗示使用特定(大)阵列尺寸FFTW当在该MATLAB使用它自己的“特殊智慧”。 考虑:2 ^ 16 = 65536。

[1] R2013b文档可从http://www.mathworks.de/de/help/matlab/ref/fftw.html (29访问2013年10月)



Answer 4:

编辑:@wakjah的回答这个答案是正确的:FFTW不支持通过其大师接口拼合实部和虚存储器。 我对黑客要求,因此不能准确的,但可以很好地适用,如果FFTW的大师界面不使用 - 在默认情况下是这样的,所以要小心依旧!

首先,遗憾的是晚了一年。 我不相信,你看到的速度增长来自MKL或其他优化。 也有一些是相当FFTW和Matlab之间有着根本的不同,那就是数据多么复杂存储在内存中。

在Matlab中,一个复向量X的实部和虚部是独立的数组XRE [i]和XIM [I](关于它们中的单独操作时,在存储器中的线性,高效率)。

在FFTW,实部和虚部是交错为双[2]通过默认,即,X [I] [0]是实部,和X [I] [1]是虚部。

因此,为了使用在MEX文件中的一个不能直接使用Matlab的阵列FFTW库,但是必须首先分配新的内存,再包从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];
}

因此,这需要两倍的内存分配+ 4X读取+ 4X写入 - 所有大小N.这确实需要收费速度明智的大问题。

我有预感,Mathworks公司可能侵入的FFTW3代码,以允许其直接在Matlab格式,这避免了所有上述的读取输入向量。

在这种情况下,人们只能分配X和使用用于Y的X以就地FFTW运行(如fftw_plan_*(N, X, X, ...)而不是fftw_plan_*(N, X, Y, ...) ),因为它会被复制到YRE及盐田Matlab的矢量,除非应用需要从保持X和Y独立/益处。

编辑 :运行Matlab的FFT2()以及基于fftw3库我的代码时,综观实时的内存消耗,它表明Matlab的只分配唯一的一个额外的复杂阵列(输出),而我的代码需要两个这样的阵列(的*fftw_complex缓冲液加Matlab的输出)。 在Matlab和FFTW格式之间的就地转换是不可能的,因为Matlab的实部和虚数组不是在内存中连续的。 这表明,Mathworks公司砍死fftw3图书馆阅读/使用MATLAB格式写入数据。

另外一个优化多次调用,就是要坚持分配(使用mexMakeMemoryPersistent() 我不知道如果这个Matlab实现做这一点。

干杯。

PS作为边注,Matlab的复杂的数据存储格式是用于在实部或虚矢量分别工作更有效。 在FFTW的格式,你必须做++ 2内存读取。



文章来源: FFTW vs Matlab FFT