JS:调用函数需要多长时间?

时间:2012-04-12 23:17:13

标签: javascript performance function math callstack

所以,我正在编写一个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);
}

谢谢!

2 个答案:

答案 0 :(得分:7)

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

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

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

要警惕的另一件事。如果函数特别复杂,JIT可能无法对其进行优化。

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

但重要的是要理解的是,当某些东西被锁定并且只在一个上下文中调用时,就像一个函数中的函数一样,JIT应该很容易进行优化。在函数外部定义,它必须确定正确调用该函数的哪个定义。它可能是一个外部功能。它可能是全球性的。它可能是window对象的构造函数原型的属性等等......在函数是第一类的语言中,意味着它们的引用可以像传递数据一样传递给args,你无法真正避免这一步在你当前的背景之外。

因此,尝试在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中,右侧递减运算符意味着我被传递以进行求值,然后在块内部使用之前递减。 while(0)评估为假。

答案 1 :(得分: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