如何在Matlab中加速嵌套的for循环代码

时间:2016-01-09 20:02:56

标签: matlab loops for-loop optimization vectorization

我试图加速以下代码:

 $json[]=array(
            'value'=>  $fName,
            'label'=> $fName,
            'lName'=> $lName,
            'tel' => $telNo,
            'loc'=> $locs
         );

从上面的代码可以看出,我使用了tic toc,我发现每个i值的每个循环迭代如下:

xdim=5560;
ydim=6945;
zdim=7;
Nexi=270250785;
Neyi=270260480;

kne=1;
for i=1:xdim
    tic
    for j=1:ydim
        for k=1:zdim
            if j<ydim
                map_ey(j+(k-1)*(ydim-1)+(i-1)*(ydim-1)*zdim+Nexi)=kne;
                kne=kne+1;
            end
        end
    end
    for j=1:ydim
        for k=1:zdim
            if k<zdim
                map_ez(j+(k-1)*ydim+(i-1)*ydim*(zdim-1)+Nexi+Neyi)=kne;
                kne=kne+1;
            end
        end
    end
    for j=1:ydim
         for k=1:zdim
            if i<xdim
                map_ex(j+(k-1)*ydim+(i-1)*ydim*zdim)=kne;
                kne=kne+1;
            end
        end
    end
    toc
end

可以更快吗?请帮忙。提前谢谢。

2 个答案:

答案 0 :(得分:2)

正如您所知,for循环(实现它们的方式)在MATLAB中相对较慢。有时它们是不可避免的,但我们可以做一些简单的事情来尝试优化它们。

首先是查看mlint警告以获取帮助(MATLAB编辑器中的橙色下划线)。如果您注意到,每次进行循环时,您都会扩展map_exmap_eymap_ez的大小。这会导致MATLAB不断重新分配用于存储这些变量的内存,这会导致程序随着这些数组的大小增长而变得越来越慢。为了避免这个陷阱,预先分配数组(即在开头创建一个与数组一样大的空数组。)

在您的情况下,这似乎是以下

map_ex = zeros(1, (xdim - 1) * ydim * zdim);
map_ey = zeros(1, (xdim * (ydim - 1) * zdim) + Nexi);
map_ez = zeros(1, (xdim * ydim * (zdim - 1)) + Nexi + Neyi);

然后你可以像你现在写的那样执行你的for循环,你应该注意到一个立即的性能提升。

在查看代码时我注意到的另一件事是你在整个

中重复了这个主题
for k=1:zdim
    if k < zdim
        % Do something
    end
end

这里的问题是,您每次循环时都会将kzdim进行比较,而您可以真正只循环k

的唯一必要值
for k = 1:(zdim - 1)
    % Do something
end

这将删除每次循环评估的不必要的if语句。

我相信你知道,在MATLAB中做事的首选方法是通过矢量化而不是循环。如何以清晰简洁的方式对代码进行矢量化很困难,而不会看到您在此处尝试实现的内容。到目前为止,我可以对你所拥有的内容进行矢量化,但我觉得可能有更好的方法来实现它。如果您可以提供有关您尝试解决的问题的更多信息,也许我可以提供更多帮助。

此外,如果你真的需要性能并且无法避免使用for循环,MATLAB确实提供了mex library,因此你可以在C / C ++中实现这种类型的代码,并且仍然可以在MATLAB中调用它。

答案 1 :(得分:2)

幸运的是,我认为这是programming puzzle,并对您的代码进行了解卷积,尽管您不愿意明确地解释您希望实现的目标。这是一个简短的解决方案,测试尺寸较小:

xdim = 4;
ydim = 5;
zdim = 3;

% avoid magic numbers
Nexi = (xdim-1)*ydim*zdim;
Neyi = xdim*(ydim-1)*zdim;
Nezi = xdim*ydim*(zdim-1);

% preallocate
map_ex2 = zeros(1,Nexi);
map_ey2 = zeros(1,Nexi+Neyi);
map_ez2 = zeros(1,Nexi+Neyi+Nezi);


% index jump between blocks of the same variable
dN = ydim*zdim + (ydim-1)*zdim + (zdim-1)*ydim;

%construct matrices
map_ey2(Nexi+1:Nexi+Neyi) = ...
      permute(reshape(bsxfun(@plus,(1:xdim*zdim).',(0:ydim-2)*dN),...             %'
                      [zdim,xdim,ydim-1]),...
              [2 1 3]);
map_ez2(Nexi+Neyi+1:Nexi+Neyi+Nezi) = ...
      permute(reshape(bsxfun(@plus,(1:(zdim-1)*ydim).'+xdim*zdim,(0:xdim-1)*dN),...    %'
                      [zdim-1,ydim,xdim]),...
              [2 1 3]);
map_ex2(1:Nexi) = ...
      permute(reshape(bsxfun(@plus,(1:(xdim-1)*ydim).'+xdim*zdim+(zdim-1)*ydim,...    %'
                             (0:zdim-1)*dN),...
                      [xdim-1,ydim,zdim]),...
              [2 1 3]);

运行原始循环版本并检查

isequal(map_ex,map_ex2)
isequal(map_ey,map_ey2)
isequal(map_ez,map_ez2)

告诉我,这应该和你正在做的一样,只是没有任何循环。

如您所见,我定义了动态变量NexiNeyiNezi以避免使用幻数。我建议您采用这种行为。

上述解决方案确实很混乱,但原来的三重循环也是如此。 @Divakar有可能以这种方式出现并为您提供更优雅/更有效的解决方案;)