JavaScript中的var与let变量在V8和SpiderMonkey中存在(de)优化/减速问题

3
在我的项目中进行JavaScript代码重构时,我发现我的一些循环速度急剧下降。寻找根本原因,我发现这个SO问题声称减速是由for循环内部的let语句和闭包创建引起的。 令我惊讶的是,将let和闭包移出for循环并没有帮助,即使使用var代替循环变量的let也没有帮助,因为减速是由放置在for循环后面let导致的。 通过去除额外的细节,我得到了这段代码片段:

"use strict"
console.log("=========================");
(function(){
    var itr = 0;
    function f(){++itr;}
    console.time('without_let');
    for(var i = 0; i < 50000000; ++i){
        f();
    }
    var totals = 0;
    console.timeEnd('without_let'); //chrome: 122ms, FF:102ms
})();

(function(){
    var itr = 0;
    function f(){++itr;}
    console.time('let_below');
    for(var i = 0; i < 50000000; ++i){
        f();
    }
    let totals = 0; // <--- notice let instead of var
    console.timeEnd('let_below'); //chrome: 411ms, FF:99ms
})();

(function(){
    let itr = 0;
    function f(){++itr;}
    console.time('let_above_and_in_loop');
    for(let i = 0; i < 50000000; ++i){
        f();
    }
    var totals = 0;
    console.timeEnd('let_above_and_in_loop'); //chrome: 153ms, FF:899ms
})();

(function(){
    var itr = 0;
    function f(){++itr;}
    console.time('let_in_loop');
    for(let i = 0; i < 50000000; ++i){
        f();
    }
    var totals = 0;
  console.timeEnd('let_in_loop'); //chrome: 137ms, FF:102ms
})();

在 Chrome 上运行此代码会产生以下结果:

(同样可以在 JS Fiddle 上查看。注意:使用 JS Fiddle 会显示略微不同的结果,但在相同位置仍会存在类似的减速现象。)

 without_let: 122ms
 let_below: 411ms <----------- Slowdown for v8
 let_above_and_in_loop: 153ms
 let_in_loop: 137ms

我通过一些搜索找到了这篇文章,它指出在Chrome 56 / V8 5.6之前,let会导致去优化!但是我的Chrome版本是57.0.2987.133(64位),V8版本是5.7.492.71。 当我尝试在Firefox 52.0.2(32位)上运行时,又有了另一个惊喜。在闭包中使用let创建的变量会导致减速。

 without_let: 101.9ms
 let_below: 99ms
 let_above_and_in_loop: 899ms <----- Slowdown for SpiderMonkey
 let_in_loop: 102ms

我发现这个问题与所谓的“暂时性死区”功能有些相关,但仍然不清楚:

  1. 为什么两个主要浏览器(主要的JavaScript引擎)仍然无法优化代码片段中的这些(不同的)部分?

  2. 是否有任何解决方法来继续使用 let除了 使用Babel将let变为var)?假设我能够通过 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); 将选项传递给Chrome甚至直接传递给v8。

更新: 在Chrome版本58.0.3029.96,对应的v8版本是5.8.283.37(根据https://omahaproxy.appspot.com/),按照jmrk下面建议的启用chrome://flags/#enable-v8-future之后,第三种情况仍然会出现减速(现在是2倍而不是8倍)。

without_let: 157.000ms
let_below: 155.000ms
let_above_and_in_loop: 304.000ms
let_in_loop: 201.000ms

火狐浏览器 53.0 (32位)

without_let: 278.650ms
let_below: 310.290ms
let_above_and_in_loop: 848.325ms
let_in_loop: 275.495ms

1
并不是他们“不能”,而是他们还没有找到时间来实现它。您可能需要提交一些错误报告,以便让他们优先考虑此事。 - Bergi
1
这里是关于v8的报告 https://bugs.chromium.org/p/v8/issues/detail?id=6188 以及关于FF的报告 https://support.mozilla.org/en-US/questions/1158956 - Chajnik-U
1
这里还有一个 Bugzilla 项 https://bugzilla.mozilla.org/show_bug.cgi?id=1362930 - Chajnik-U
1个回答

3
我可以回答关于V8的问题。你看到的减速是由于旧的优化编译器(称为“Crankshaft”)的限制造成的。这个问题一直没有得到解决,因为团队忙于开发新的优化编译器(“Turbofan”),它将在Chrome 59中发布(目前在Canary和Dev渠道上,很快就会在Beta上发布!)。
在Chrome 58(目前在Beta上),您已经可以通过将“实验性JavaScript编译管道”设置为“启用”(在chrome://flags/#enable-v8-future)来获得预览。请注意,Chrome 59将有一些额外的性能改进。
顺便说一下:在现有的代码库中,迁移到let并没有太多好处,所以你可以继续使用var

我已在Chrome ver 58.0.3029.96中进行了测试 - 优化已经消除了那种严重的减速,但第三种情况仍然慢了近两倍。 - Chajnik-U

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