变量似乎在每次循环迭代时改变大小 - 什么?

Sha*_*hai 9 iteration matlab memory-management

在编写以下Matlab代码时:

for ii=1:n
    x(ii) = foo( ii ); % foo is some function of ii that cannot be vectorized.
end
Run Code Online (Sandbox Code Playgroud)

我得到以下m-lint警告:

变量x似乎在每次循环迭代时改变大小

我的问题:

  1. 这个警告意味着什么?
  2. 为什么每次迭代都会改变变量大小是件坏事?
  3. 怎样才能解决这个问题?

这个问题是不能重复的这一个,因为它与预分配的更一般的问题,而是它的一个特定实例涉及.

Sha*_*hai 18

嗯,首先要做的事情.

1.这个警告意味着什么?

此代码在语法方面是正确的,它将正确执行返回预期结果:ii-th元素x将包含值foo( ii ).
但是,在运行这一小段代码之前,x未定义变量.现在,当循环开始时,为其x(1)分配值foo( 1 ),因此Matlab创建x为长度为1的数组.在第二次迭代时x(2)分配值foo( 2 ),因此Matlab需要更改x为长度为2,依此类推:x在每次迭代时更改其长度/大小.

2.为什么每次迭代改变变量都是一件坏事?

考虑在x每次迭代时改变其大小时后台(在内存分配方面)会发生什么:在每次迭代时,Matlab需要找到一个空闲的内存空间来托管新的大小x.如果幸运的话,之后就有足够的可用空间,x所以所发生的一切都是分配给x新的值并在正确位置写入新值的内存量的变化.
但是,如果只是后没有足够的自由空间x,MATLAB必须找到一个新的点所有ii-1已经元素x,分配这个新的空间x,复制所有ii-1已值x到新的地点,并释放老地方x使用.在后台发生的这种无分配副本的操作可能非常耗时,尤其是在x很大的时候.

3.如何解决这个问题?

最简单的解决方案是在循环之前预先分配所有空间x需求:

x = zeros(1,n); 
for ii=1:n
    x( ii ) = foo( ii );
end
Run Code Online (Sandbox Code Playgroud)

通过预分配,我们确定预先x分配了它需要的所有内存,因此在循环执行时不需要昂贵的内存分配/复制.

解决问题的另一种很酷的解决方案

如果你太懒(和我一样)并且不想预先分配,你可以简单地:

for ii=n:-1:1
    x( ii ) = foo( ii );
end
Run Code Online (Sandbox Code Playgroud)

这样,第一次x被分配了一个值,它被分配给它的n第 - 个元素(最后一个),因此Matlab 立即为所有n元素分配空间x.
凉!

  • 另一种解决方案是右键单击突出显示的代码部分,然后从菜单中选择"抑制..."等以停止警告.在某些情况下,动态内存分配不能轻易避免,或者只是不值得进行简单的廉价计算.在当前版本中,本机动态内存分配可以非常快,甚至比其他一些动态分配方法更快. (4认同)

cha*_*pjc 14

我的回答有点迟了,但我在MATLAB中有关于数组增长和预分配的一些事情.

首先要注意的是,MATLAB在最近的版本中已经大大提高了自动阵列增长性能,因此如果你做得正确,警告所暗示的性能可能不会太差(见下文).不过,最佳做法是预先分配您的数组(例如zeros).

警告说明

自R2014a起,警告的详细说明如下:

指示的变量或数组的大小似乎随着每次循环迭代而改变.通常,出现此消息是因为阵列通过分配或连接而增长.通过分配或连接来增长阵列可能很昂贵.对于大型数组,MATLAB必须分配一个新的内存块,并在进行每次赋值时将较旧的数组内容复制到新数组中.

以这种方式更改变量大小的程序可能会将大部分运行时间花在这种低效的活动上....

从这个摘录中可以清楚地看出,如果你完全关注性能,预分配是一个聪明的想法.

旁注:关于在阵列增长期间用于重新分配的算法的信息有限,但是一些信息是由Steve Eddins在同一篇博客文章中提供的,我在前面的答案对此进行了总结.

自动阵列增长优化

如果要通过沿维度增长(不预先分配)来使用动态数组大小调整,可以采用正确的方法.请参阅Steve Eddins撰写的MathWorks博客文章.最重要的是要注意你应该沿着最后一个维度增长以获得最佳性能.在您的情况下这不是问题,因为数组是1D.因此,如果您决定让它骑行,请%#ok<SAGROW>在罪魁祸首代码之后与警告放在同一行,以使警告静音.

Yair 在他博客的另一篇文章中讨论了动态数组调整大小.此外,有一些方法可以在不使用一些毛茸茸的MEX API杂技进行初始化的情况下分配数组,但就是这样.

预分配

建议进行预分配.养成习惯,学会爱zeros.如果你决心从MATLAB中挤出一点性能,Yair Altman就内存预分配这个主题提出了几篇很好的文章:

  • 这里有很多知识 (2认同)