编写以下Matlab代码时:
for ii=1:n
x(ii) = foo( ii ); % foo is some function of ii that cannot be vectorized.
end
我收到以下m-lint警告:
变量
x
似乎会在每次循环迭代时改变大小
我的问题:
这个问题与this one不重复,因为它涉及预分配的更一般方面,而不是它的特定实例。
答案 0 :(得分:16)
嗯,首先是第一件事。
此代码在语法方面是正确的,并且会正确执行返回预期结果:ii
的{{1}}元素将包含值x
。
但是,在运行这一小段代码之前,未定义变量foo( ii )
。现在,当循环开始时,x
被赋值x(1)
,因此Matlab创建foo( 1 )
作为长度为1的数组。在第二次迭代时,x
被赋值为x(2)
,因此Matlab需要将foo( 2 )
更改为长度为2,依此类推:x
更改其长度/大小每次迭代。
考虑当x
每次迭代改变其大小时后台(内存分配方面)会发生什么:在每次迭代时,Matlab需要找到一个空闲的内存空间来托管x
的新大小。如果幸运的话,x
之后就有足够的可用空间,所以所有这一切都会改变分配给x
并在正确的位置写入新值的内存量。
但是,如果x
之后没有足够的可用空间,则Matlab必须为x
中已有的{strong>所有 ii-1
元素找到新位置, x
的新空间,复制 x
已存在ii-1
的所有x
值到新地点,并释放使用的旧地点x
。在后台发生的这种无分配副本的操作可能非常耗时,尤其是当x
很大时。
最简单的解决方案是在循环之前预先分配所有空间x
需要:
x = zeros(1,n);
for ii=1:n
x( ii ) = foo( ii );
end
通过预分配,我们确定x
预先分配了它需要的所有内存,因此在循环执行时不需要昂贵的内存分配/复制。
如果你太懒(和我一样)并且不想预先分配,你可以简单地说:
for ii=n:-1:1
x( ii ) = foo( ii );
end
这样,第一次为x
分配了一个值,它被分配给第n
个元素(最后一个),因此Matlab 立即分配空间n
的所有x
个元素
酷!
答案 1 :(得分:13)
我的回答有点晚了,但我在MATLAB中提到了一些关于数组增长和预分配的事情。
首先要注意的是,MATLAB在最近的版本中已经大大提高了自动阵列增长性能,因此警告所暗示的性能影响可能不会太差如果你做得对(参见)下面)。不过,最佳做法是预先分配您的数组(例如使用zeros
)。
自R2014a起,警告的详细说明如下:
指示的变量或数组的大小似乎随着每次循环迭代而变化。通常,出现此消息是因为数组通过赋值或连接而增长。通过分配或连接来增长阵列可能很昂贵。对于大型数组, MATLAB必须分配一个新的内存块,并在进行每次分配时将较旧的数组内容复制到新数组。
以这种方式更改变量大小的程序可能会将大部分运行时间花在这种低效的活动上。 ...
从这段摘录中可以清楚地看出,如果你对表现感兴趣,预分配是一个明智的想法。
旁注:有关在阵列增长期间用于重新分配的算法的信息有限,但有些信息是由Steve Eddins在同一篇博客文章中提供的,我在this previous answer中总结了这些信息。
如果要通过沿维度增长(不预先分配)来使用动态数组大小调整,可以采用正确的方法。见MathWorks blog post by Steve Eddins。最重要的一点是,您应该沿最后一个维度增长以获得最佳性能。在您的情况下这不是问题,因为数组是1D。因此,如果您决定让它骑行,请在罪魁祸首代码之后将%#ok<SAGROW>
放在与警告相同的行上,以使警告静音。
Yair讨论了another post on his blog中动态数组的大小调整。此外,有一些方法可以在不使用一些毛茸茸的MEX API杂技进行初始化的情况下分配数组,但就是这样。
建议预先分配。养成习惯,学会爱zeros
。如果你决心从MATLAB中挤出一点性能,Yair Altman就内存预分配这个主题有几篇很好的文章:
答案 2 :(得分:7)
这个主题有很多材料。以下是一些选定的链接以获取更多信息:
官方文档和技术解决方案:
MathWorks博客:
社区博客:
相关Stack Overflow问题/答案:
答案 3 :(得分:1)
这个答案简化了一些细节。
当M-lint发现预先分配内存的机会时,它会警告程序员。预分配内存可缩短处理时间,因为它简化了MATLAB程序员与内存硬件管理相关的直接控制之外的任务。
变化的变量大小并不总是坏的;但是,编写更快的处理速度是一种很好的编程习惯。当代码使用许多迭代或更多内存时,更快的处理速度更加明显。
推荐的其他MATLAB预分配方法可行;但是,以下方法可能更直观。
为预先分配的数组分配值。
x(1:n) = NaN;
for ii=1:n
x(ii) = foo(ii);
end
注意:
为预先分配的多维数组赋值:
x(1:n_i,1:n_j) = NaN; % or NaN(n_i, N_j);
for ii=1:n_i
for jj=1:n_j
x(ii,jj) = foo(ii,jj);
end
end
预分配的一种懒惰方式是仅将零分配给数组中的最后一个元素。
x(n)= 0;
for ii=1:n
x(ii) = foo(ii);
end