意外减慢了就地修改数组的函数

时间:2013-09-26 18:00:39

标签: performance matlab

这个错误是由于Matlab过于聪明而不是为了自己的利益。

我有类似

的东西
for k=1:N
    stats = subfun(E,k,stats);
end

其中stats1xN数组,N=5000说,subfunstats(k)计算E,并将其填入统计数据< / p>

function stats = subfun(E,k,stats)
    s = mean(E);
    stats(k) = s;
end

当然,来回传递一个大数组会有一些开销,只是为了填充其中一个元素。然而,在我的情况下,开销是可以忽略的,我更喜欢这个代码而不是

for k=1:N
    s = subfun(E,k);
    stats(k) = s;
end

我的偏好是因为我实际上有比stats更多的作业。 另外一些作业实际上要复杂得多。

如上所述,开销可以忽略不计。但是,如果我做一些微不足道的事情,比如这个无关紧要的if语句

for k=1:N
    i = k;
    if i>=1
        stats = subfun(E,i,stats);
    end
end
然后,在subfun中发生的分配突然变得“永远”(它比N线性地增加得快得多)。这是任务,而不是永远的计算。事实上,它甚至比以下荒谬的subfun

更糟糕
function stats = subfun(E,k,stats)
    s = calculation_on_E(E);
    clear stats
    stats(k) = s;
end

每次都需要重新分配统计数据。

有没有人知道为什么会这样发生?

2 个答案:

答案 0 :(得分:5)

这可能是由于Matlab JIT的一些模糊细节造成的。最近版本的Matlab的JIT知道不创建新数组,而是在某些有限的情况下进行修改in-place。其中一个要求是函数定义为

function x = modify_big_matrix(x, i, j)
x(i, j) = 123;

而不是

function x_out = modify_big_matrix(x_in, i, j)
x_out = x_in;
x_out(i, j) = 123;

您的示例似乎遵循此规则,因此,正如Praetorian所提到的,您的if语句可能会阻止JIT识别它是一个就地操作。

如果您确实需要加速算法,可以就地修改数组using your own mex-functions。我已经成功地使用这个技巧在一些中等大小的阵列上获得了4倍的加速(订购100x100x100 IIRC)。但是not recommended,如果您不小心可能会在Matlab中出现段错误,并可能在将来的版本中停止工作。

答案 1 :(得分:0)

正如其他人所讨论的那样,问题几乎肯定在于JIT及其相对脆弱的修改能力。

如上所述,我真的更喜欢函数调用和赋值的第一种形式,尽管已经提出了其他可行的解决方案。如果不依赖于JIT,这种效率的唯一方法(据我所见)就是某种形式的参考传递。

因此,我创建了一个继承自handle的类Stats,它包含k=1:N的数据数组。然后通过引用传递。

为了将来参考,这似乎运行良好,性能良好,我目前正在使用它作为我的工作解决方案。