Performance 修改阵列的函数意外减速

Performance 修改阵列的函数意外减速,performance,matlab,Performance,Matlab,这个错误是由于Matlab太聪明了 我有点像 for k=1:N stats = subfun(E,k,stats); end 其中,stats是一个1xN数组,N=5000假设,subfun从E计算stats(k),并将其填充到stats中 function stats = subfun(E,k,stats) s = mean(E); stats(k) = s; end 当然,在来回传递一个大数组时,只需填充其中一个元素,会有一些开销。然而,在我的例子中,开销是可以

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

我有点像

for k=1:N
    stats = subfun(E,k,stats);
end
其中,
stats
是一个
1xN
数组,
N=5000
假设,
subfun
E
计算
stats(k)
,并将其填充到stats中

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
我的偏好是因为我实际上有比统计数据多得多的任务。 另外,有些作业实际上要复杂得多

如前所述,开销是可以忽略的。但是,如果我做了一些琐碎的事情,比如这个无关紧要的if语句

for k=1:N
    i = k;
    if i>=1
        stats = subfun(E,i,stats);
    end
end
子单元内部发生的赋值突然“永远”(其增长速度远远快于与N的线性关系)。这是一项任务,而不是永远需要的计算。事实上,它甚至比以下毫无意义的
子功能更糟糕

function stats = subfun(E,k,stats)
    s = calculation_on_E(E);
    clear stats
    stats(k) = s;
end
每次都需要重新分配统计数据


有人知道为什么会发生这种情况吗?

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

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认识到它是一个就地操作


如果您真的需要加快算法的速度,可以在适当的位置修改数组。我已经成功地使用这个技巧在一些中型阵列(订单100x100x100 IIRC)上获得了4倍的加速。但是,如果您不小心,这可能会破坏Matlab,并可能在未来版本中停止工作。

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

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

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


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

什么是
E
?只是一些常量数据?
E
可能是常量,也可能是随机的。它的大小不变。查看我的编辑。Matlab最新版本的JIT知道不创建新数组,而是进行修改。其中一个要求是将函数定义为
函数x=modify(x,y)
,而不是
函数x\u out=modify(x\u in,y)
。您的示例似乎遵循这条规则,但您是否检查了实际代码?正如@Bas所提到的,MATLAB内置了一些启发式方法来确定是否可以修改数组。我猜通过在循环中添加分支,您已经混淆了JIT,它不再认为就地修改是可能的,从而导致每次迭代都重新分配。假设MATLAB没有函数的参考参数的概念,那么您真的应该重写函数,以便调用
stats(k)=subfun(E,k)在循环中。这将是更好的实践,而不是依赖JIT和它的模式匹配(或任何它做的事情)。@Praetorian我考虑过这一点,但问题是我的统计数据实际上是一个包含多个数组的结构。我当然可以有一个结构数组,但我发现从这样一个对象中获取字段更难。你的意思是
function x=modify(x,y)
,而不仅仅是
x=modify(x,y)
,对吗?是的,我的真实代码也有所需的格式。虽然我认为是JIT搞糟了,但是if语句真的很奇怪,谢谢,修正了错误。