Javascript时间死区在NodeJS 7.9.0中导致性能下降?

7
我注意到在另一个问题中,在循环中使用letvar变量声明会有性能差异。
原始问题的正确答案是,在for循环中使用let会更慢,因为let为每次迭代创建一个新作用域来保存let声明的变量的值。需要进行更多的工作,所以较慢是正常的。仅供参考,我提供了代码和在NodeJS(7.9.0)执行时间中的结果:
请注意,下面所有的javascript代码都涉及NodeJS版本7.9.0 常规代码:
'use strict';
console.time('var');
for (var i = 0; i < 100000000; i++) {}
console.timeEnd('var');


console.time('let');
for (let j = 0; j < 100000000; j++) {}
console.timeEnd('let');

输出:

var: 55.792ms
let: 247.123ms

为了避免在每次循环中都声明额外的j作用域,我们在循环之前声明j变量。人们会期望这样做会使let循环的性能与var相匹配。但是,结果并不是这样!以下是代码和结果供参考:

在循环之前定义let的代码:

'use strict';
console.time('var');
for (var i = 0; i < 100000000; i++) {}
console.timeEnd('var');


console.time('let');
let j;
for (j = 0; j < 100000000; j++) {}
console.timeEnd('let');

输出:

var: 231.249ms
let: 233.485ms

我们可以看到,不仅let循环没有变得更快,而且var循环也变得和let一样慢了!!!唯一的解释是,由于我们不在任何块或函数中,因此两个变量都声明在全局模块作用域中。然而,正如这里所引用的,变量在作用域中间的let声明会创建一个暂时性死区,这使得变量j未初始化,而var则将变量初始化为已定义。
因此,在暂时性死区中运行代码,即使未引用未初始化的变量,速度也会慢得多....
最后,为了展示差异,我们将j变量声明在程序顶部,以展示在没有暂时性死区的情况下运行它的结果。 没有暂时性死区的代码:
'use strict';
let j;
console.time('var');
for (var i = 0; i < 100000000; i++) {}
console.timeEnd('var');


console.time('let');
for (j = 0; j < 100000000; j++) {}
console.timeEnd('let');

输出:

var: 55.586ms
let: 55.009ms

现在,letvar 循环的性能都得到了类似的优化!
有人知道我关于暂时性死区性能的假设是否正确,或者提供一个不同的解释吗?

有趣的问题 - 赞成,你也应该在其他环境中尝试一下。 - Abhinav Gauniyal
我也对那个很好奇,可能与内存分配有关。 - Volkan Seçkin Akbayır
请在 esdiscuss 上进一步讨论。 - James Thorpe
@JamesThorpe 是的,你说得对,我读得很快... - mitsos1os
显示剩余3条评论
1个回答

2
这是您在几个 Node 版本上测试的输出结果:
node-v4.0.0
var: 92ms
let: 336ms
var: 220ms
let: 230ms
=====
node-v4.2.2
var: 95ms
let: 342ms
var: 228ms
let: 233ms
=====
node-v5.1.0
var: 93.418ms
let: 342.050ms
var: 264.895ms
let: 228.310ms
=====
node-v5.12.0
var: 103.254ms
let: 340.990ms
var: 228.698ms
let: 228.213ms
=====
node-v6.3.1
var: 109.476ms
let: 338.127ms
var: 232.381ms
let: 241.795ms
=====
node-v6.5.0
var: 96.630ms
let: 339.570ms
var: 686.631ms
let: 612.820ms
=====
node-v6.7.0
var: 106.760ms
let: 349.677ms
var: 690.753ms
let: 587.444ms
=====
node-v7.0.0
var: 95.366ms
let: 333.880ms
var: 222.668ms
let: 234.101ms
=====
node-v7.4.0
var: 101.074ms
let: 330.778ms
var: 221.869ms
let: 238.053ms
=====
node-v7.8.0
var: 93.604ms
let: 338.447ms
var: 224.263ms
let: 233.313ms
=====
node-v7.9.0
var: 92.622ms
let: 333.552ms
var: 275.980ms
let: 230.990ms

这些不是所有的Node版本,只是我本地安装的一些版本,以便于测试。

显然,在大多数版本中,行为是一致的:将 let 放在循环外面会使它稍微快一点,但会使其他地方的 var 变得更慢。

似乎在6.5.0版本中出现了一些问题,但在7.x版本中已经修复了。

这可能会留下一些优化的空间,但我不会过分担心 var 变慢。我更感兴趣的是让 let 变得更快。

将这些示例转换成函数并让它们运行多次可以启动JIT并优化函数,因此结果可能与第一次运行时看到的结果有所不同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接