未定义参数的性能损失

时间:2012-09-30 15:36:11

标签: javascript performance arguments

我经常在函数中有可选的参数,但是一些测试在firefox和safari中显示出巨大的性能影响(70-95%)。奇怪的是,如果我传入字面值 undefined ,那么就没有惩罚。这可能发生什么?我不会认为它是一个范围链问题,因为它们本身就是函数的本地。我是否开始将 undefined 传递给每个可选参数?

jsPerf:http://jsperf.com/function-undefined-args/2

2 个答案:

答案 0 :(得分:5)

对于这样的函数:

function threeArgs(x, y, z) {
  return x + y + z;
}

这就是这样的:

threeArgs(1, 2, 3);

优化器可以自由选择不生成任何代码。它很容易确定没有副作用,因为函数只是引用其参数值并返回简单表达式的结果。由于忽略了返回值,因此运行时没有理由做任何事情。

除此之外,如果代码是:

something += threeArgs(1, 2, 3);

优化器可能会决定生成大致相当于的代码:

something += 6;

为什么呢?因为调用是使用数字常量进行的,所以它可以在代码生成时安全地折叠它们。它可能是保守的,因为数字很奇怪,但在这里它们都是整数,所以它可以做到这一点。即使它没有,它也可以安全地内联函数:

something += 1 + 2 + 3;

但是,当缺少参数时,可能是优化器挽救并生成实际函数调用。对于这样一个简单的函数,函数调用的开销很容易导致性能的巨大差异。

通过在测试中使用变量而不是常量,并通过实际使用函数的返回值,您可以“混淆”优化器并防止它跳过调用或预先计算结果,但是你不能防止内联。我仍然认为你的结果很有意思:它暴露了这样一个事实:截至今天,这些优化器对调用函数的方式很敏感。

答案 1 :(得分:2)

我认为可以解释性能差异的是参数传递给函数对象的方式:通过arguments对象。当没有传递任何参数时,JS将首先扫描任何给定参数的arguments对象,当这些参数未定义时,arguments原型链将被扫描,一直到Object.prototype。如果那些都缺少所需的属性,JS将返回undefined。然而,显式传递undefined,直接在arguments对象上将其设置为属性:

function foo(arg)
{
    console.log(arguments.hasOwnProperty('0'));
}
foo();//false'
foo('bar');//true
foo(undefined);//true

我认为这就是为什么显式传递undefined会更快的原因。