Arrays 计算矢量和的栅格点时,避免循环

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

我想加速我的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: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%。此外,删除无用的重复计算的提示也让人大开眼界!我非常感谢您对我提出的解决方案的反馈。