Arrays 计算矢量和的栅格点时,避免循环
我想加速我的Matlab代码。 通常我会找到一些方法来避免循环以获得计算时间,但在这种情况下,我遇到了麻烦。 我必须计算点网格中的值,但计算值需要逻辑运算和向量求和,这会使实现复杂化。 此代码在我的计算机上运行约8秒钟:Arrays 计算矢量和的栅格点时,避免循环,arrays,matlab,for-loop,matrix,nested-for-loop,Arrays,Matlab,For Loop,Matrix,Nested For Loop,我想加速我的Matlab代码。 通常我会找到一些方法来避免循环以获得计算时间,但在这种情况下,我遇到了麻烦。 我必须计算点网格中的值,但计算值需要逻辑运算和向量求和,这会使实现复杂化。 此代码在我的计算机上运行约8秒钟: clear all % Grid dLimsX=[-100 +100]; dLimsY=[-100 +100]; dStep=1; [x_map, y_map]=meshgrid((dLimsX(1):dStep:dLimsX(2)),(dLimsY(1):dStep:dLim
clear all
% Grid
dLimsX=[-100 +100];
dLimsY=[-100 +100];
dStep=1;
[x_map, y_map]=meshgrid((dLimsX(1):dStep:dLimsX(2)),(dLimsY(1):dStep:dLimsY(2)));
nPoints_map=numel(x_map);
% Inputs
smallDistance=1e-3;
N=10e3;
scaleFactor=10;
x_input = sin(linspace(0,1,N));
y_input = cos(linspace(0,1,N));
z_input = linspace(0,1,N);
tic
A=zeros(size(x_map));
for r=1:size(x_map,1)
y0=y_map(r,1);
for c=1:size(x_map,2)
x0=x_map(1,c);
idxTemp = find((x0-x_input).^2+(y0-y_input).^2>smallDistance); % do not consider in the calculation the inputs too close to the point
A(r,c) = sum( scaleFactor * z_input(idxTemp) .* (y0-y_input(idxTemp)) ./ ((x0-x_input(idxTemp)).^2 +(y0-y_input(idxTemp)).^2+eps) );
end
end
toc
加速代码并不是删除for循环。我发现很多情况下,矢量化代码比循环等效代码慢。在过去的20年里,MATLAB的循环速度越来越快,它们不再是经济放缓的重要原因。例如,对于100万个元素的求和,以下仅是比求和(x)慢4倍的系数:
y = 0;
for ii = 1:numel(x)
y = y+x(ii);
end
如果循环内的计算更昂贵,则循环开销将完全消失
您仍然可以从矢量化中获益的原因是,在循环代码中,经常提取矩阵的行或列。这涉及到复制数据,这很昂贵。另一方面,如果矢量化代码需要一个大的中间矩阵,那么将该矩阵存储在内存中将是使矢量化代码显著变慢的瓶颈。内存访问通常是个问题
为了使代码更快,您应该首先关注避免重复计算。例如,
(y0-y\u输入)。^2
计算3*大小(x\u映射,2)
次!(数据子集的1/3时间,但通过索引删除的点的数量很小)
此外,还应使用逻辑索引,并避免使用find
A(查找(条件))
与A(条件)
相同,但速度较慢
您的循环在我的机器上运行约10.5秒,此版本运行约5.1秒:
tic
A=零(大小(x_图));
对于r=1:大小(x_图,1)
y0=y_图(r,1);
dy2=(y0-y_输入)。^2;
对于c=1:大小(x_图,2)
x0=x_图(1,c);
dx2=(x0-x_输入)。^2;
idxTemp=dx2+dy2>小距离;%在计算中不要考虑输入太靠近点。
A(r,c)=和(scaleFactor*z_输入(idxTemp)。*(y0-y_输入(idxTemp))/(dx2(idxTemp)+dy2(idxTemp)+eps);
结束
结束
toc
还有进一步的改进,例如避免在内部循环中重复计算
y0-y_输入
。Cris Luengo的回答给了我一个巨大的提示,让我思考哪些计算可以避免重复。
Cris建议避免重新计算x0-x_输入
和y0-y_输入
,已经将计算时间缩短了50-60%
此外,当在另一个循环中使用代码时,它帮助我区分哪些更改和哪些更改只能计算一次。在我的例子中,x0
,x\u输入
,y0
,y\u输入
始终保持不变<代码>z_输入随每次新迭代而变化。
所以我把计算分为两部分:首先,计算一次所有不变的东西;其次,计算所需的值
为了能够使用简单的矩阵乘法执行第二次计算,我将x-y值从矩阵(网格)重新排列为向量。
这就是我所做的:
% Arrange x-y values in two vectors
x = x_map(:);
y = y_map(:);
nPoints = numel(x);
z_input = linspace(0,1,N)';
tic
a = zeros(nPoints,N);
for p = 1:nPoints
x0 = x(p);
y0 = y(p);
dx2 = (x0-x_input).^2;
dy2 = (y0-y_input).^2;
idxTemp = dx2 + dy2 > smallDistance; % do not consider in the calculation the inputs too close to the point
a(p,:) = (scaleFactor * 1 .* dy2 ./ (dx2 + dy2 + eps)) .*idxTemp;
end
toc
tic
A2 = a*z_input;
toc
% Check that the values calculated with the alternative method are correct
mean(mean(abs((A(:)-A2)./A(:))))
对结果的一些评论:
- 原始代码:~122秒在我的家用电脑上(比我的原始帖子的办公电脑慢得多)
- Cris提出的代码:~50秒
- 以上代码:~75秒为第一部分~第二部分为0.6秒
- 替代计算的相对误差约为1E-17
a
需要更大的内存使用量
非常感谢您的反馈。加快代码速度并不是为了删除for循环。我发现很多情况下,矢量化代码比循环等效代码慢。在过去的20年里,MATLAB的循环速度越来越快,它们不再是经济放缓的重要原因。通常避免对整列或整行进行索引,这会加快矢量化解决方案的速度。在您的情况下,我将从删除重复计算开始。例如,
(y0-y_输入)。^2
可以在内部循环之外计算。您还可以省去find
,以获得良好的加速效果(使用逻辑矩阵索引)。此外,clear all
是不必要的,它会使代码变慢。如果要删除所有变量,请使用clear
清除所有
从内存中卸载所有解析的函数,这意味着MATLAB在使用它们时将需要再次加载和解析它们。感谢您花时间给出这样的信息性回复。您提出的代码确实将计算时间减少了50-60%。此外,删除无用的重复计算的提示也让人大开眼界!我非常感谢您对我提出的解决方案的反馈。