从不运行的线路是否会影响 V8 的性能?

Does the line which never runs affect performance in V8?

我想知道这两个代码运行速度不同的原因。

fast.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()
})();

slow.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 循环。而 fast.js 中的 count 在寄存器中。

这造成了速度差异。

有谁知道 V8 中代码的哪一部分造成了这种行为? 当一个数字变量在一个永远不会 运行s 的闭包和一个变量中使用时,它是在堆中分配的,并且当它不在闭包中使用时它在寄存器中,这是真的吗?

@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 开发人员。)

Does anyone know which part of the code in V8 makes this behavior?

它是“作用域解析”系统的一部分,大部分代码在src/ast/scopes.cc中。 (它相当复杂,因为它要处理的事情太多了。)

Is it true that a number variable is allocated in heap when it's used in a closure that never runs and a variable and it's in a register when it's not used in a closure?

是的。

变量是否包含数字或其他任何内容都没有关系。

闭包 运行 的频率并不重要,包括它是否会 运行。在需要决定在何处分配变量时,尚无关于将执行或不执行的信息。 (此外,静态保证永远不会执行的闭包在实际代码中非常罕见,即使可能,也很可能不值得对其进行优化。)

闭包使用的变量是“上下文分配的”,这使得访问它们比“堆栈分配的”变量慢一点。 If/when 有问题的函数得到了优化,堆​​栈分配的变量可能会或可能不会最终保留在寄存器中,这取决于优化编译器做出的其他寄存器分配决定。

在大多数现实世界的情况下,您不会注意到差异(因此这通常不值得担心),但是像循环这样的微基准测试只做 count++ 可以观察到。