C/C++ 中固定长度实际输入数据的高效 2D FFT
Posted
技术标签:
【中文标题】C/C++ 中固定长度实际输入数据的高效 2D FFT【英文标题】:Efficient 2D FFT of fixed length real input data in C/C++ 【发布时间】:2012-12-04 12:15:59 【问题描述】:我正在开发一种算法,可以多次调用 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;
【问题讨论】:
我尝试自己实现 fft,使用 sin\cos 表和其他优化。我真的认为自己提高fft速度并使其比fftw之类的库更快的唯一方法是在硬件中执行它。他们真的知道自己在做什么。 您可以进行内存管理,如果尺寸固定,您可以重复使用相同的内存块,而无需在每次迭代时执行分配(假设您不需要存储旧图像)跨度> 不要指望能够如此轻松地击败 FFTW。虽然这当然是可能的(我以前做过,因为这是我所做的),除非您对现代硬件有深入的了解以及高性能计算方面的经验,否则您不应该尝试。 数据/内存管理步骤中的其他 6 毫秒你在做什么?是否可以改进(更少的数据副本、矢量化操作等)? 我在数据/内存管理方面尽了最大的努力来优化这6ms,但我不是这方面的专家,所以肯定可以改进。我将在问题中放置我的代码示例。 【参考方案1】:您使用 FFTW 的 FFT 时间似乎非常长。为了充分利用具有固定大小 FFT 的 FFTW,您应该使用FFTW_PATIENT
标志生成一个计划,然后理想地保存生成的“智慧”以供后续重用。您可以从自己的代码或使用fftw-wisdom 工具生成智慧。
【讨论】:
使用 FFTW_PATIENT 在 Windows 7、Visual Studio 10.0 和 FFTW 3.3.3 中,Intel Xeon 5130 2.0GHz CPU 的平均时间为 7 毫秒(按照站点中的说明编译)。你觉得还高吗? 是的,这似乎有点高 - 但你正在做复杂到复杂的非现场操作,所以这可能解释了它。 如果您需要更好的性能,请尝试使用实数到复数的转换(并尽可能将其原地转换)。 你的意思是不使用浮点数据类型作为输入? 否 - 使用浮点数,但使用实数到复数 FFT (r2c),即纯实数输入,不复数 - 目前您所有的虚输入都是 0,因此您浪费了大约 50% FFT 计算。就地意味着您对输入和输出使用相同的缓冲区,这也有助于提高性能。【参考方案2】:来自Intel Math Kernel Library(独立于英特尔编译器)的 FFT 在大多数情况下都比 FFTW 快。我不知道这是否足以改善您的情况以证明价格合理。
我同意其他人的观点,即滚动您自己的 FFT 可能不会很好地利用您的时间(除非您想学习如何去做)。多年来,可用的 FFT 实现(FFTW、MKL)已经过微调。我并不是说你不能做得更好,但这可能需要大量的工作和时间来获得边际收益。
【讨论】:
我在基准测试时发现完全相反,至少对于现代 Intel CPU(Core i7 等) - FFTW 轻松击败了英特尔库,尤其是当您花时间制定最佳计划时。 好吧,我的大部分经验是使用相对较长 (>32K) 的 1D FFT,而 MKL FFT 似乎更快。我没有尝试过 2D FFT,所以我想我错误地假设结果适用于 2D 案例。【参考方案3】:相信我 fftw 真的非常优化,你可以做得更好的机会非常小。
你用哪个编译器来编译 fftw?有时来自 Intel 的编译器提供比 gcc 更好的性能
【讨论】:
我同意你对 FFTW 的性能的看法,而且对于普通代码,ICC 确实比 gcc 提供更好的结果,但对于 FFTW,蝴蝶已经高度优化,根据我的经验,编译器的选择已经影响不大。以上是关于C/C++ 中固定长度实际输入数据的高效 2D FFT的主要内容,如果未能解决你的问题,请参考以下文章