优化参数有什么用?

What's the deal with optimising arguments?

众所周知,在 JavaScript 中不正确地使用 arguments 可能会导致函数不可优化(参见 here and here by the end):

function notOptimisable (a, b) {
  // Optimising compiler says: Nope.
  var args = [].slice.call(arguments)
}

但是,none 的消息来源到目前为止已经设法解释了 为什么这会阻止优化的发生

更令人费解的是我要做的就是

function optimisable (a, b) {
  // Optimising compiler says: I can do this!
  var args = new Array(arguments.length)
    , i = 0

  // Copy the arguments into an actual array, very carefully...
  for(i; i < args.length; ++i)
     args[i] = arguments[i]
}

瞧 - 我有一个 arguments 的副本,它是一个实际的数组,可以优化函数:

node --trace_opt --trace_deopt script.js # Exerpt below

[marking 0x24ba2c0bf0f1 <JS Function notoptimisable (SharedFunctionInfo 0x26b62a724859)> for recompilation, reason: small function, ICs with typeinfo: 3/3 (100%), generic ICs: 0/3 (0%)]
[disabled optimization for 0x26b62a724859 <SharedFunctionInfo notoptimisable>, reason: Bad value context for arguments value]
[failed to optimize notoptimisable: Bad value context for arguments value]
[marking 0x24ba2d0041b1 <JS Function optimisable (SharedFunctionInfo 0x26b62a7247b1)> for recompilation, reason: small function, ICs with typeinfo: 7/7 (100%), generic ICs: 0/7 (0%)]
[optimizing 0x24ba2d0041b1 <JS Function optimisable (SharedFunctionInfo 0x26b62a7247b1)> - took 0.039, 0.164, 0.051 ms]

所以,我问你:

为什么?

原因可能是因为 JavaScript 没有实现传统意义上的实际数组。数组更像是 JavaScript 个对象,即 {},长度为 属性。 arguments 不是 Array 这一事实是语言中的设计错误。这可能是优化器对此有问题的原因。

arguments不是数组,例如考虑:

function abc(a) {
    arguments[0] = 1;
    console.log(a);
}
abc(0);

这里的问题是 arguments[0] = 1 的赋值神奇地改变了 a 的值,I.E. arguments[0]aliasesa。这与 evalwith 导致的问题相同 - 您无法再静态查看正在更改哪些变量以及何时更改。

在删除别名错误功能的 strict mode 下,没有技术原因不能优化它,但是它可能被认为是不值得的,因为大多数网络不使用 strict模式或将其删除以用于生产代码。

没有无法克服的技术挑战。这只是在 Crankshaft 中实现参数对象期间做出的一个捷径决定:仅支持可以 轻松 避免参数对象实现的情况。

即使 Crankshaft 支持参数对象的具体化,生成的代码仍然比不分配参数对象的代码

这只是在 10 分钟内支持最快的案例与在 10 天内支持较慢但更通用的案例的问题。 (10 分钟/10 天是虚数,我只是想传达实施复杂性的规模差异)。

如果想要支持参数对象具体化(并可能泄漏)的情况,则需要考虑参数对象和参数之间的别名——这会改变为这些变量构建 SSA 形式的方式。出于类似的原因,这也使内联变得复杂。

一个更通用的参数对象方法应该基于逃逸分析/分配下沉通道——但 Crankshaft 在实现时没有类似的东西,它仍然需要至少支持一些参数的快速路径操纵。