11

所以,我正在编写一个 2d Javascript 物理模拟。性能很好,但我正在进行优化以使其更好。因此,由于该程序适用于大量物理几何,因此我在程序中进行了几次勾股定理计算。总共大约有五次计算;它们一起运行大约每秒一百万次。所以,我认为如果我将那个简单的勾股定理代码放入一个新函数并调用它,它会提高性能;毕竟,这样浏览器的编译工作就更少了。所以,我在 Firefox 中运行代码并得到......该计算的执行时间增加了 4000000%。

如何?这是相同的代码:Math.sqrt(x*x+y*y),那么将它作为一个函数添加是如何减慢它的呢?我认为原因是一个函数需要时间才能被调用,而不执行代码,并且每秒增加一百万个这样的延迟会减慢它的速度?

这对我来说似乎相当令人震惊。这也适用于预定义的 js 函数吗?这似乎不太可能,如果是这样,他们如何避免呢?

过去的代码是这样的:

function x()
{
    dx=nx-mx;
    dy=ny-my;
    d=Math.sqrt(dx*dx+dy*dy);
    doStuff(...
}

我尝试的是这样的:

function x()
{
    dx=nx-mx;
    dy=ny-my;
    d=hypo(dx,dy);
    doStuff(...
}
function hypo(x,y)
{
    return Math.sqrt(x*x+y*y);
}

谢谢!

4

2 回答 2

8

函数调用可以忽略不计,甚至可以在 JS 从未有过的预编译语言中进行优化。除此之外,很大程度上取决于浏览器。

它们是 JS 直到最近才主要出现的解释语言中所有性能的死亡。大多数现代浏览器都有 JIT(及时)编译器,这是对过去 JS 解释器的巨大升级,但我相信对另一个范围的函数调用仍然需要一些开销,因为 JS 的调用对象必须确定实际调用的内容,这意味着在各种范围链上上下移动。

因此,作为一般规则:如果您关心 IE8 以及较低和较旧版本的 Chrome 和 Firefox,请避免函数调用期。尤其是内循环。对于 JIT 浏览器,我希望在另一个函数中定义的函数通常是有益的(但我仍然会测试,因为这对于 IE9 来说是全新的技术,对于其他所有人来说都是相对较新的技术)。

另一件需要警惕的事情。如果一个功能特别复杂,JIT 可能不会做任何事情来优化它们。

https://groups.google.com/forum/#!msg/closure-compiler-discuss/4B4IcUJ4SUA/OqYWpSklTE4J

但是要理解的重要一点是,当某些东西被锁定并且仅在上下文中调用时,例如函数中的函数,JIT 应该很容易优化。在函数之外定义,它必须确定准确调用该函数的哪个定义。它可能在外部函数中。它可能是全球性的。它可能是窗口对象的构造函数原型的属性,等等......在函数是第一类的语言中,这意味着它们的引用可以像传递数据一样作为参数传递,你不能真正避免这一步在您当前的上下文之外。

所以尝试在 X 中定义 hypo 看看会发生什么。

来自解释时代的另外几个在 JIT 中可能仍然有价值的一般提示:

  • 这 '。' 中的运算符someObject.property,是一个值得缓存的过程。它会产生开销,因为每次使用它时都会有一个关联的调用对象查找过程。我想 Chrome 不会保留这个过程的结果,因为对父对象或原型的更改可能会改变它在给定上下文之外实际引用的内容。在您的示例中,如果 x 正在被循环使用(如果 x 在与 JIT 中的循环相同的函数中定义 - 在解释器中谋杀),则可能没问题甚至有帮助,我会尝试在使用之前将 Math.sqrt 分配给 var在低。对当前函数上下文之外的东西的引用过多可能会导致一些 JIT 认为不值得麻烦优化,但这纯粹是我的猜测。

  • 以下可能是循环数组的最快方法:

//assume a giant array called someArray
var i = someArray.length; //note the property lookup process being cached here
//'someArray.reverse()' if original order isimportant
while(i--){
  //now do stuff with someArray[i];
}

注意:代码块由于某种原因在这里不起作用。

这样做会很有帮助,因为它基本上将递增/递减步骤和逻辑比较变成了递减,完全消除了对左/右比较运算符的需要。请注意,在 JS 中,右侧递减运算符意味着 i 被传递以进行评估,然后在块内使用之前递减。while(0)评估为假。

于 2012-04-13T00:19:44.530 回答
1

令我惊讶的是,按照 Erik 的建议缓存查找对提高我的浏览器(Chromium、Linux)的性能没有太大帮助,但似乎反而会损害性能:http: //jsperf.com/inline-metric-distance

var optimizedDistance = (function () { 
    var sqrt = Math.sqrt;
    return function (x, y) { return sqrt(x * x + y * y); }
})();

_ _

var unoptimizedDistance = function(x, y) {
    return Math.sqrt(x * x + y * y);
}

即使调用别名也更慢

var _sqrt = Math.sqrt; // _sqrt is slower than Math.sqrt!

但话又说回来,这不是一门精确的科学,现实生活中的测量仍然会有所不同。

尽管如此,我还是会使用Math.sqrt.

于 2012-04-13T08:44:18.333 回答