例如:
function makeFunc(a,b,c,d,e) {
return () => {
if (a) { /* do something expensive not referencing b,c,d,e */ }
if (b) { /* do something expensive not referencing a,c,d,e */ }
if (c) { /* do something expensive not referencing a,b,d,e */ }
if (d) { /* do something expensive not referencing a,b,c,e */ }
if (e) { /* do something expensive not referencing a,b,c,d */ }
}
}
const func = makeFunc(true, false, false, false, false)
for ( let i=0; i < 100_000; i++) func()
我希望,在这个例子中,V8 能够优化if (x)
闭包上的测试,只发出
/* do something expensive not referencing b,c,d,e */
if (a)
执行100,000次之后。
据我所知,没有。(或者:也许)。
您已将函数柯里化,使其能够进行部分求值,但这实际上仅对代码本身有帮助
makeFunc
。 中的代码func
在每次调用时都会运行。而且 V8 不会进行任何常量传播来将a
/b
/… 的值内联到 中。为什么?主要是因为它们不是常量,而是函数的参数;每次执行func
时,它们的值可能都不同。if
而真正的主要问题(或者说:你所期望的解决方案)是闭包代码的特化。闭包不是模板;即使
makeFunc()
创建了不同的函数对象,它们也共享相同的(字节)代码和相同的优化。从这篇关于 V8 优化的深入分析中可以看出:虽然我认为这不是您想要的通用答案,但您的具体示例代码确实得到了优化。但这仅仅是因为您只调用了一次该函数 :-/ 针对函数的首次调用进行优化(和专门化) 会带来可衡量的加速,因为网站上的很多函数只被调用一次。而且 V8 实际上可以进行函数上下文专门化(整篇深入探讨的文章都是关于它的!),然而,我上面省略的引用部分确实解释了 V8 就是这样做的:
我找到的更多资源:
makeFunc(true, false, false, false, false)()
,希望被调用的函数被内联,然后使用常量值进行优化是的,V8 对此进行了优化,只要外部上下文的变量永远不会被写入(意思是:只要不存在可以写入它们的代码)。
这是一个反例,由于上下文变量不是常量,因此无法进行优化:
显然,当
f1
自行优化时,编译器不能仅仅采用当前值a
并假设它永远不会改变。但只要
f2
赋值a = ...
语句不存在,V8 就能意识到它a
永远不会被写入,因此编译器可以安全地将其视为常量。所以在原帖的例子中,if
检查确实被优化掉了。(感谢@Dada完成这里的大部分调查!)