优化参数有什么用?
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]
所以,我问你:
为什么?
- 哪些技术挑战阻碍了优化的进行?
- 为什么 v8 引擎不能简单地 return 一个标准的参数数组,就像第二个代码示例显示和优化函数一样?
- 对于额外的点,为什么
arguments
从语言设计的角度来看只是一个 "array-like" 对象(即它有数字键,但它不是 Array 的后代)并且以某种方式做到这一点在优化过程中发挥作用?
原因可能是因为 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
。这与 eval
和 with
导致的问题相同 - 您无法再静态查看正在更改哪些变量以及何时更改。
在删除别名错误功能的 strict mode
下,没有技术原因不能优化它,但是它可能被认为是不值得的,因为大多数网络不使用 strict模式或将其删除以用于生产代码。
没有无法克服的技术挑战。这只是在 Crankshaft 中实现参数对象期间做出的一个捷径决定:仅支持可以 轻松 避免参数对象实现的情况。
即使 Crankshaft 支持参数对象的具体化,生成的代码仍然比不分配参数对象的代码慢。
这只是在 10 分钟内支持最快的案例与在 10 天内支持较慢但更通用的案例的问题。 (10 分钟/10 天是虚数,我只是想传达实施复杂性的规模差异)。
如果想要支持参数对象具体化(并可能泄漏)的情况,则需要考虑参数对象和参数之间的别名——这会改变为这些变量构建 SSA 形式的方式。出于类似的原因,这也使内联变得复杂。
一个更通用的参数对象方法应该基于逃逸分析/分配下沉通道——但 Crankshaft 在实现时没有类似的东西,它仍然需要至少支持一些参数的快速路径操纵。
众所周知,在 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]
所以,我问你:
为什么?
- 哪些技术挑战阻碍了优化的进行?
- 为什么 v8 引擎不能简单地 return 一个标准的参数数组,就像第二个代码示例显示和优化函数一样?
- 对于额外的点,为什么
arguments
从语言设计的角度来看只是一个 "array-like" 对象(即它有数字键,但它不是 Array 的后代)并且以某种方式做到这一点在优化过程中发挥作用?
原因可能是因为 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
。这与 eval
和 with
导致的问题相同 - 您无法再静态查看正在更改哪些变量以及何时更改。
在删除别名错误功能的 strict mode
下,没有技术原因不能优化它,但是它可能被认为是不值得的,因为大多数网络不使用 strict模式或将其删除以用于生产代码。
没有无法克服的技术挑战。这只是在 Crankshaft 中实现参数对象期间做出的一个捷径决定:仅支持可以 轻松 避免参数对象实现的情况。
即使 Crankshaft 支持参数对象的具体化,生成的代码仍然比不分配参数对象的代码慢。
这只是在 10 分钟内支持最快的案例与在 10 天内支持较慢但更通用的案例的问题。 (10 分钟/10 天是虚数,我只是想传达实施复杂性的规模差异)。
如果想要支持参数对象具体化(并可能泄漏)的情况,则需要考虑参数对象和参数之间的别名——这会改变为这些变量构建 SSA 形式的方式。出于类似的原因,这也使内联变得复杂。
一个更通用的参数对象方法应该基于逃逸分析/分配下沉通道——但 Crankshaft 在实现时没有类似的东西,它仍然需要至少支持一些参数的快速路径操纵。