在C/C++中高效地对固定长度的实数输入数据进行二维FFT

4
我正在开发一个调用FFT函数多次的算法。我有几个时间约束(需要实时执行),因此我需要尽量减少每个FFT调用所花费的时间。
我正在使用OpenCV库进行开发,并已经使用两种不同的方法实现了我的代码:
- 使用FFTW库。数据/内存管理+FFT(8ms)=14ms(平均值,使用FFT_MEASURE标志)。 - 使用OpenCV fft函数。数据/内存管理+FFT(21ms)=23ms(平均值)。
由于我的输入数据始终固定为512x512像素的实际图像,您认为如果我根据DFT的数学定义自己实现基于FFT的算法,并存储正弦/余弦表,我能否实现更好的性能,或者FFTW库真的非常优化?是否有更好的想法?
非常感谢您提供的所有想法和建议。暂时,我不考虑并行处理或GPU实现。
更新:
系统:Windows 7上的Intel Xeon 5130 2.0GHz CPU,Visual Studio 10.0以及FFTW 3.3.3(按照网站上的说明编译),OpenCV 2.4.3。
使用FFTW进行FFT调用的代码示例(输入:OpenCV Mat CV_32F(1通道,浮点类型),输出OpenCV Mat CV_32FC2(2通道,浮点类型):
float           *im_data;

fftwf_complex    *data_in;
fftwf_complex    *fft;      

fftwf_plan       plan_f;

int             i, j, k;

int height=I.rows;
int width=I.cols;
int N=height*width;


float* outdata = new float[2*N];
im_data = ( float* ) I.data;

data_in = ( fftwf_complex* )fftwf_malloc( sizeof( fftwf_complex ) * N );
fft     = ( fftwf_complex* )fftwf_malloc( sizeof( fftwf_complex ) * N );

plan_f = fftwf_plan_dft_2d( height , width , data_in , fft ,  FFTW_FORWARD ,  FFTW_MEASURE );

for(int i = 0,k=0; i < height; ++i) {
    float* row = I.ptr<float>(i);
    for(int j = 0; j < width; j++) {
        data_in[k][0]=(float)row[j];
        data_in[k][1] =(float)0.0;
        k++;
    }
} 

fftwf_execute( plan_f );

int width2=2*width;
// writing output matrix: RealFFT[0],ImaginaryFFT[0],RealFFT[1],ImaginaryFFT[1],...
for( i = 0, k = 0 ; i < height ; i++ ) {
    for( j = 0 ; j < width2 ; j++ ) {

        outdata[i * width2 + j] = ( float )fft[k][0];
        outdata[i * width2 + j+1] = ( float )fft[k][1];
        j++;
        k++;
    }
}

Mat fft_I(height,width,CV_32FC2,outdata);

fftwf_destroy_plan( plan_f );
fftwf_free( data_in );
fftwf_free( fft );


return fft_I;

1
我尝试自己实现FFT,使用正弦/余弦表和其他优化。我真的认为,要想在自己的计算机上提高FFT速度并使其比FFTW等库更快,唯一的方法就是在硬件上执行它。他们确实知道他们在做什么。 - Arsenii Fomin
1
如果维度是固定的,您可以在内存管理方面进行工作,而无需在每次迭代中执行分配,可以重复使用相同的内存块(假设您不需要存储旧图像)。 - Alessandro Teruzzi
5
不要指望能够轻易地打败FFTW。虽然这是有可能的(而且我以前做过,因为这是我的工作),但除非你对现代硬件有着深入了解并且具有高性能计算方面的经验,否则不应尝试。 - Mysticial
在数据/内存管理步骤的其余6毫秒中,你在做什么?这能否得到改进(减少数据复制、向量化操作等)? - Jason B
我尽力在数据/内存管理方面优化了这6毫秒,但我不是这个领域的专家,所以肯定还有改进的空间。我会在问题中放一个代码示例。 - gui
3个回答

3
你使用FFTW进行FFT计算的时间非常长。为了充分利用FFTW进行固定大小的FFT计算,你应该使用FFTW_PATIENT标志生成一个计划,并最好保存生成的“智能”以供后续重复使用。你可以从自己的代码或使用fftw-wisdom工具生成智能。

使用 FFTW_PATIENT,在 Windows 7、Visual Studio 10.0 和 FFTW 3.3.3 下编译后,我在 Intel Xeon 5130 2.0GHz CPU 上平均获得了 7 毫秒。您认为这仍然太高吗? - gui
是的,看起来有点高 - 但你正在进行复杂到复杂的非就地操作,这可能可以解释它。 - Paul R
如果您需要更好的性能,请尝试使用实际到复杂的转换(如果可能,请进行原地转换)。 - Paul R
你的意思是不要使用浮点数作为输入数据类型? - gui
不要使用float,而是使用实数到复数FFT(r2c),即纯实数输入,而不是复数。目前你所有的虚数输入都是0,因此你浪费了约50%的FFT计算。原地意味着你使用相同的缓冲区作为输入和输出,这也可以提高性能。 - Paul R
我按照你的建议编写了我的代码的r2c(out-place)版本。我对结果感到满意(平均FFT在4ms内完成,带有内存和数据管理的情况下为9ms)。我会接受这个答案,谢谢。 - gui

1

Intel Math Kernel Library中的FFT(与Intel编译器分开)大多数时候比FFTW更快。但我不知道在您的情况下是否足以证明其价格的合理性。

我同意其他人的观点,自己编写FFT可能不是您时间的好用途(除非您想学习如何做)。现有的FFT实现(FFTW、MKL)已经经过多年的精细调整。我并不是说您不能做得更好,但这可能需要大量的工作和时间才能获得微小的收益。


在基准测试时,我发现完全相反的结果,至少对于在512x512到2048x2048范围内的图像大小进行的2D实际复杂FFT和现代英特尔CPU(Core i7 *等)而言 - FFTW相比英特尔库具有更高的性能,特别是如果您花时间生成最佳计划。 - Paul R
好的,我的大部分经验都是与相对较长(>32K)的一维FFT相关的,其中MKL FFT似乎更快。我还没有尝试过二维FFT,所以我猜想结果在二维情况下可能不成立。 - Jason B

0

相信我,fftw真的非常优化,你很难做得更好。

你用哪个编译器编译fftw?有时候Intel的编译器比gcc性能更好。


我同意你关于FFTW性能的看法,一般情况下ICC比gcc在普通代码方面表现更好,但对于FFTW,蝴蝶已经高度优化,在我的经验中编译器的选择几乎没有影响。 - Paul R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接