During JavaScript code refactoring in my project I've found that some of my loops slowed down drastically. Searching for root cause I've found this SO question stating slowdown is caused by let
statement inside for
loop and closure creation.
To my surprise moving let
and closure out of the for
loop didn't help, and even using var
instead of let
for loop variable also does not help because slowdown is caused by let
placed after thefor
loop.
By removing extra details I've obtained this code snippet:
"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
})();
(also on JS Fiddle Note: using JS Fiddle shows little bit different results but similar slowdown is still present in the same places)
Running this on Chrome produces following
without_let: 122ms
let_below: 411ms <----------- Slowdown for v8
let_above_and_in_loop: 153ms
let_in_loop: 137ms
Some googling brought me to the article stating that let
caused deoptimization prior to Chrome 56 / V8 5.6! but my chrome is 57.0.2987.133 (64-bit) and v8 ver 5.7.492.71.
More surprises trying to run this on Firefox 52.0.2 (32-bit). Here we have slowdown in another place, when variable created with let
is used inside closure:
without_let: 101.9ms
let_below: 99ms
let_above_and_in_loop: 899ms <----- Slowdown for SpiderMonkey
let_in_loop: 102ms
As I see the issue is somewhat related to so called "Temporal Dead Zone" feature, but still unclear:
Why two major browsers (major JavaScript engines) still cannot optimize those (different) parts of the snippet?
Are there any workarounds to keep using
let
(except using Babel to turn let into var)? Assume I'm able to pass options to Chrome or even directly to v8 viav8::V8::SetFlagsFromCommandLine(&argc, argv, true);
UPD: In Chrome ver 58.0.3029.96, correspondng v8 version 5.8.283.37 (aconding to https://omahaproxy.appspot.com/) after enabling chrome://flags/#enable-v8-future as jmrk suggested below there is still slowdown for third case (now 2 times instead of 8 times)
without_let: 157.000ms
let_below: 155.000ms
let_above_and_in_loop: 304.000ms
let_in_loop: 201.000ms
Firefox 53.0 (32bit)
without_let: 278.650ms
let_below: 310.290ms
let_above_and_in_loop: 848.325ms
let_in_loop: 275.495ms