为什么Octave的函数调用开销比Matlab和Python的大得多?

时间:2015-07-14 10:04:12

标签: performance matlab function optimization octave

我在Python和Octave中有两段结构完全相同的代码。但是,使用numpy和scipy实现的Python版本速度提高了约5倍。我做了代码的配置文件,我发现Octave代码中的主要罪魁祸首是6个函数在循环中重复调用了数千次。这些函数仅计算数值表达式,例如cos,cosh,所以我很惊讶他们消耗了多少时间(作为参考,两个代码都在2秒内运行。)

我在网上研究了这个奇怪的现象并阅读了paper,它显示了Octave中的函数开销,即函数开始执行函数体内的实际函数代码所需的设置和清理之后,它比Matlab的大约30倍,比Python的大约100倍。

这种情况让我感到非常困惑 - 从Octave调用函数怎么可能这个比调用另外两种类似语言中的函数慢得多?此外,除了将函数本身复制并粘贴到循环体中之外,还有什么方法可以解决这种速度降低的问题吗?

编辑:我已经从我的代码中发布了主要的for循环。它是牛顿多方程方法的迭代实现,所以我不确定它是如何被矢量化的。

for k = 1:10    
  for l = 1:50
    % matrix of derivatives of equations with respect to variables
    a = [dEq1_dq1(p1, p2, q1, q2, i, j), dEq1_dq2(p1, p2, q1, q2, i, j); dEq2_dq1(p1, p2, q1, q2, i, j), dEq2_dq2(p1, p2, q1, q2, i, j)];

    % vector of equations
    b = [Eq1(p1, p2, q1, q2, i, j); Eq2(p1, p2, q1, q2, i, j)];

    % solution to ax=b
    x = a \ b;

    % iteratively update q
    q1 -= beta*x(1);
    q2 -= beta*x(2);
  endfor

  for l = 1:50
    a = [dEp1_dp1(p1, p2, q1, q2, i, j), dEp1_dp2(p1, p2, q1, q2, i, j); dEp2_dp1(p1, p2, q1, q2, i, j), dEp2_dp2(p1, p2, q1, q2, i, j)];
    b = [Ep1(p1, p2, q1, q2, i, j); Ep2(p1, p2, q1, q2, i, j)];
    x = a \ b;

    p1 -= beta*x(1);
    p2 -= beta*x(2);
  endfor
endfor

...

% derivatives of implicit equations with respect to variables
function val = dEp1_dp1(p1, p2, q1, q2, i, j)
  % symmetric
  if mod(i, 2) == 1
    val = p1/(2*cos(p1/2)**2)+tan(p1/2);
  % anti-symmetric
  else
    val = tan(p1/2)/(p1**2)-1/(2*p1*cos(p1/2)**2);
  endif
end

...

function val = Ep1(p1, p2, q1, q2, i, j)    
  if mod(i, 2) == 1
    val = p2*tanh(p2/2)+p1*tan(p1/2);
  else
    val = (1/p2)*tanh(p2/2)-(1/p1)*tan(p1/2);
  endif
end

...

1 个答案:

答案 0 :(得分:4)

比较语言之间的性能是一件棘手的事情。 Octave会马上告诉你,你应该对你的代码进行矢量化。这就是语言的设计目标。 Python将他的代码编译成字节代码,这将允许优化。 Matlab有JIT,它也是如此。但不是Octave。 Octave将完全按照您所写的内容执行,并且会一次读取您的程序。这意味着如果您没有编写好的代码,您的表现将会受到影响。

虽然进行函数调用可能会有很大的开销(我没有检查你的数字),但如果你只进行一些函数调用那就不那么重要了。你经常会处理大型阵列,所以它是实际的" sciency"应该导致性能问题的计算(当然,除非您没有编写正确的Octave程序并使用不必要的循环)。

您提到的功能coscosh将接受一个向量,因此无需使用for循环。