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

Pat*_*ick 5 performance matlab

这个错误是由于Matlab太聪明了.

我有类似的东西

for k=1:N
    stats = subfun(E,k,stats);
end
Run Code Online (Sandbox Code Playgroud)

其中stats是一个1xN阵列,N=5000比方说,和subfun计算stats(k)E,并且它填充到统计

function stats = subfun(E,k,stats)
    s = mean(E);
    stats(k) = s;
end
Run Code Online (Sandbox Code Playgroud)

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

for k=1:N
    s = subfun(E,k);
    stats(k) = s;
end
Run Code Online (Sandbox Code Playgroud)

我的偏好是因为我实际上有更多的任务而不仅仅是stats.此外,一些任务实际上更复杂.

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

for k=1:N
    i = k;
    if i>=1
        stats = subfun(E,i,stats);
    end
end
Run Code Online (Sandbox Code Playgroud)

然后,在subfun内发生的任务突然变为"永远"(它比N线性地增加得快得多).这是任务,而不是永远的计算.事实上,它甚至比以下荒谬更糟糕subfun

function stats = subfun(E,k,stats)
    s = calculation_on_E(E);
    clear stats
    stats(k) = s;
end
Run Code Online (Sandbox Code Playgroud)

这需要每次重新分配统计数据.

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

Bas*_*els 5

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

function x = modify_big_matrix(x, i, j)
x(i, j) = 123;
Run Code Online (Sandbox Code Playgroud)

而不是

function x_out = modify_big_matrix(x_in, i, j)
x_out = x_in;
x_out(i, j) = 123;
Run Code Online (Sandbox Code Playgroud)

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

如果您确实需要加速算法,可以使用自己的mex函数就地修改数组.我已经成功地使用这个技巧在一些中等大小的阵列上获得了4倍的加速(订购100x100x100 IIRC).但是不建议这样做,如果你不小心可能会在某些版本中停止工作.


Pat*_*ick 0

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

如前所述,我确实更喜欢函数调用和赋值的第一种形式,尽管已经建议了其他可行的解决方案。在不依赖 JIT 的情况下,唯一有效的方法(据我所知)是某种形式的引用传递。

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

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