Во время рефакторинга кода 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
Как я вижу, проблема в некоторой степени связана с так называемой функцией «Временная мертвая зона», но все еще неясна:
Почему два основных браузера (основные движки JavaScript) до сих пор не могут оптимизировать эти (разные) части фрагмента кода?
Существуют ли какие-либо обходные пути, чтобы продолжать использовать
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