Javascript var vs let (де) оптимизация/замедление в v8 и SpiderMonkey

Во время рефакторинга кода JavaScript в моем проекте я обнаружил, что некоторые из моих циклов резко замедляются. В поисках основной причины я нашел этот вопрос SO о том, что замедление вызвано оператором let внутри цикла for и созданием закрытия. К моему удивлению, перемещение let и закрытие цикла for не помогли, и даже использование var вместо let для переменной цикла также не помогает, потому что замедление вызвано let, помещенным после цикла for. Удалив дополнительные детали, я получил этот фрагмент кода:

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

(также на JS Fiddle Примечание. Использование JS Fiddle показывает немного другие результаты, но аналогичное замедление все еще присутствует в одни и те же места)

Запуск этого в Chrome приводит к следующему

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

Некоторое гугление привело меня к статье, в которой говорилось, что let вызывал деоптимизацию до Chrome 56. / V8 5.6! но мой хром 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)? Предположим, я могу передать параметры в Chrome или даже напрямую в v8 через v8::V8::SetFlagsFromCommandLine(&argc, argv, true);

UPD: В Chrome версии 58.0.3029.96, соответствующей версии v8 5.8.283.37 (согласно https://omahaproxy.appspot.com/) после включения chrome://flags/#enable-v8-future, как предложено jmrk ниже, в третьем случае все еще наблюдается замедление (теперь 2 раза вместо 8 раз)

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

Firefox 53.0 (32-разрядная версия)

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

person Chajnik-U    schedule 01.04.2017    source источник
comment
Дело не в том, что они не могут, а в том, что они еще не нашли времени, чтобы реализовать это. Возможно, вы захотите отправить несколько отчетов об ошибках, чтобы они расставили это в приоритете.   -  person Bergi    schedule 01.04.2017
comment
Вот отчет для v8 bugs.chromium.org/p/v8/issues /detail?id=6188 и здесь для FF support.mozilla.org/ en-US/questions/1158956   -  person Chajnik-U    schedule 05.05.2017
comment
Здесь также находится элемент Bugzilla bugzilla.mozilla.org/show_bug.cgi?id=1362930   -  person Chajnik-U    schedule 08.05.2017


Ответы (1)


Я могу ответить на часть вопроса о V8. Замедление, которое вы видите, связано с ограничением старого оптимизирующего компилятора (известного как "Crankshaft"). Это не было решено все это время, потому что команда была занята работой над новым оптимизирующим компилятором («Turbofan»), который поставляется в Chrome 59 (в настоящее время на каналах Canary и Dev, скоро в бета-версии!).

В Chrome 58 (в настоящее время в бета-версии) вы уже можете получить предварительный просмотр, установив для параметра «Экспериментальный конвейер компиляции JavaScript» значение «Включено» (в chrome://flags/#enable-v8-future). Обратите внимание, что в Chrome 59 будет несколько дополнительных улучшений производительности.

Примечание: в существующей кодовой базе переход на let не дает особых преимуществ, поэтому вы можете просто продолжать использовать var.

person jmrk    schedule 01.04.2017
comment
Я тестировал это в Chrome версии 58.0.3029.96 - оптимизация убрала это резкое замедление, но все же третий случай почти в два раза медленнее. - person Chajnik-U; 05.05.2017