我在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。感谢
答案 0 :(得分:13)
一些观察而不是确定的答案,因为我不知道MATLAB FFT实现的任何细节:
我将假设你已经研究过第二个问题并且迭代次数是可比的。 (如果不是,那么这很可能是一些准确性问题,值得进一步调查。)
现在,关于FFT速度比较:
答案 1 :(得分:8)
由于低级和体系结构特定的优化,这是经典的性能提升。
Matlab使用来自英特尔MKL(数学核心库)二进制文件(mkl.dll)的FFT。这些是英特尔针对英特尔处理器优化(在汇编级别)的例程。即使在AMD上,它似乎也提供了不错的性能提升。
FFTW看起来像是一个没有优化的普通c库。因此使用MKL的性能提升。
答案 2 :(得分:3)
我在MathWorks网站上发现了以下评论[1]:
关于2的大功率的注释:对于作为幂的FFT维数 2,在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日访问)
答案 3 :(得分:2)
编辑: @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实现是否也这样做。
干杯。
P.S。作为旁注,Matlab复杂数据存储格式对于分别对实数或虚数向量进行操作更有效。在FFTW格式上,你必须进行++ 2内存读取。