从不运行的行是否会影响V8中的性能?
我想知道这两个代码以不同速度运行的原因。
快速.js
var N = 2e9;
(function () {
console.time();
var count = 0;
for (var i = 0; i < N; i++) count++;
if(false) console.log(count);
console.timeEnd()
})();
慢速js
var N = 2e9;
(function () {
console.time();
var count = 0;
for (var i = 0; i < N; i++) count++;
if(false) (() => console.log(count));
console.timeEnd()
})();
执行
% node fast.js
default: 1.297s
% node slow.js
default: 3.175s
代码之间的区别是第 6 行。
if(false) console.log(count);
if(false) (() => console.log(count));
但是这些部分永远不会运行,因为它是死代码。
@tkihira分析并得出结论,变量count是在堆中分配的,因为它在运行 slow.js 时在闭包中使用,因此在 for 循环中增加它需要更多时间。而count在 fast.js 中是在寄存器中。
这造成了速度差异。
有谁知道V8 中代码的哪一部分导致了这种行为?当一个数字变量在一个从不运行的闭包中使用时,它是在堆中分配的,而当一个变量没有在闭包中使用时,它在一个寄存器中,这是真的吗?
@tkihira分析:源代码:https : //gist.github.com/tkihira/b9c7fe8c31e21f1022011e377f7e672d
jit 输出:https ://gist.github.com/tkihira/67a07231cc6a4ce55462d8cdfa51a6e2
差异:https : //gist.github.com/tkihira/5f5877cde581c66cae3bc02f88e505e4
推特线程:https : //twitter.com/tkihira/status/1365280241195782149
回答
(此处为 V8 开发人员。)
有谁知道 V8 中代码的哪一部分导致了这种行为?
它是“范围解析”系统的一部分,大部分代码都在src/ast/scopes.cc. (这相当复杂,因为它要处理的事情太多了。)
当一个数字变量在一个从不运行的闭包中使用时,它被分配在堆中,当一个变量没有在闭包中使用时,它在一个寄存器中,这是真的吗?
是的。
变量是否包含数字或其他任何内容都没有关系。
关闭运行的频率无关紧要,包括它是否会运行。在需要决定在何处分配变量时,尚无关于将执行或不执行的信息。(此外,静态保证永远不会执行的闭包在实际代码中非常罕见,即使可能,也很可能不值得对其进行优化。)
闭包使用的变量是“上下文分配的”,这使得访问它们比“堆栈分配的”变量慢一点。如果/当有问题的函数被优化时,堆栈分配的变量可能会或可能不会在某些或所有时间最终保存在寄存器中,这取决于优化编译器做出的其他寄存器分配决定。
在大多数实际情况下,您不会注意到差异(因此这通常不值得担心),但是像循环这样的微基准测试只会count++使其可观察。