w3schools usar support entre ecmascript diferencias cuando javascript performance google-chrome ecmascript-6 let

usar - let javascript support



¿Por qué es tan lento el uso de `let` dentro de un bucle` for` en Chrome? (3)

ACTUALIZACIÓN PRINCIPAL.

Aún no se ha pensado en la versión principal de Chrome: los nuevos motores Ignition + Turbofan para Chrome Canary 59 han resuelto el problema. Prueba muestra tiempos idénticos para let y var declaradas variables de bucle.

Pregunta original (ahora muda).

Cuando se usa let in a for loop en Chrome, se ejecuta muy lentamente, en comparación con mover la variable justo fuera del alcance del loop.

for(let i = 0; i < 1e6; i ++);

toma el doble de tiempo que

{ let i; for(i = 0; i < 1e6; i ++);}

Que esta pasando?

Snippet demuestra la diferencia y solo afecta a Chrome y lo ha sido durante todo el tiempo que puedo recordar que Chrome admite let .

var times = [0,0]; // hold total times var count = 0; // number of tests function test(){ var start = performance.now(); for(let i = 0; i < 1e6; i += 1){}; times[0] += performance.now()-start; setTimeout(test1,10) } function test1(){ // this function is twice as quick as test on chrome var start = performance.now(); {let i ; for(i = 0; i < 1e6; i += 1);} times[1] += performance.now()-start; setTimeout(test2,10) } // display results function test2(){ var tot =times[0]+times[1]; time.textContent = tot.toFixed(3) + "ms"; time1.textContent = ((times[0]/tot)*100).toFixed(2) + "% " + times[0].toFixed(3) + "ms"; time2.textContent = ((times[1]/tot)*100).toFixed(2) + "% " + times[1].toFixed(3) + "ms"; if(count++ < 1000){; setTimeout(test,10); } } var div = document.createElement("div"); var div1 = document.createElement("div"); var div2 = document.createElement("div"); var time = document.createElement("span"); var time1 = document.createElement("span"); var time2 = document.createElement("span"); div.textContent = "Total execution time : " div1.textContent = "Test 1 : " div2.textContent = "Test 2 : " div.appendChild(time); div1.appendChild(time1); div2.appendChild(time2); document.body.appendChild(div); document.body.appendChild(div1); document.body.appendChild(div2); test2()

Cuando me encontré con esto por primera vez, pensé que era debido a la instancia recién creada de i, pero a continuación se muestra que esto no es así.

Vea el fragmento de código, ya que eliminé cualquier posibilidad de que la declaración adicional let se optimice con ini con random y luego se agregue al valor indeterminado de k.

También he añadido un segundo contador de bucle p

var times = [0,0]; // hold total times var count = 0; // number of tests var soak = 0; // to stop optimizations function test(){ var j; var k = time[1]; var start = performance.now(); for(let p =0, i = 0; i+p < 1e3; p++,i ++){j=Math.random(); j += i; k += j;}; times[0] += performance.now()-start; soak += k; setTimeout(test1,10) } function test1(){ // this function is twice as quick as test on chrome var k = time[1]; var start = performance.now(); {let p,i ; for(p = 0,i = 0; i+p < 1e3; p++, i ++){let j = Math.random(); j += i; k += j}} times[1] += performance.now()-start; soak += k; setTimeout(test2,10) } // display results function test2(){ var tot =times[0]+times[1]; time.textContent = tot.toFixed(3) + "ms"; time1.textContent = ((times[0]/tot)*100).toFixed(2) + "% " + times[0].toFixed(3) + "ms"; time2.textContent = ((times[1]/tot)*100).toFixed(2) + "% " + times[1].toFixed(3) + "ms"; if(count++ < 1000){; setTimeout(test,10); } } var div = document.createElement("div"); var div1 = document.createElement("div"); var div2 = document.createElement("div"); var time = document.createElement("span"); var time1 = document.createElement("span"); var time2 = document.createElement("span"); div.textContent = "Total execution time : " div1.textContent = "Test 1 : " div2.textContent = "Test 2 : " div.appendChild(time); div1.appendChild(time1); div2.appendChild(time2); document.body.appendChild(div); document.body.appendChild(div1); document.body.appendChild(div2); test2()


ACTUALIZACIÓN PRINCIPAL.

Pensado aún no en la versión principal de Chrome, los nuevos motores Ignition + Turbofan para Chrome Canary 60.0.3087 han resuelto el problema. Prueba muestra tiempos idénticos para let y var declaradas variables de bucle.

Nota al margen. Mi código de prueba usa Function.toString() y falló en Canary porque devuelve "function() {" not "function () {" como versiones anteriores (solución fácil usando regexp) pero un problema potencial para aquellos que usan Function.toSting()

Actualización Gracias al usuario Dan. M que proporciona el enlace https://bugs.chromium.org/p/v8/issues/detail?id=4762 (y cara a cara) que tiene más información sobre el tema.

Respuesta anterior

Optimizador optó por salir.

Esta pregunta me ha desconcertado durante algún tiempo y las dos respuestas son las respuestas obvias, pero no tenía sentido ya que la diferencia de tiempo era demasiado grande para ser la creación de una nueva variable de ámbito y un contexto de ejecución.

En un esfuerzo por demostrar esto encontré la respuesta.

Respuesta corta

El optimizador no admite un bucle for con una declaración let en la declaración.

Chrome Version 55.0.2883.35 beta, Windows 10.

Una imagen que vale más que mil palabras, y debería haber sido el primer lugar para mirar.

Las funciones relevantes para el perfil anterior.

var time = [0,0]; // hold total times function letInside(){ var start = performance.now(); for(let i = 0; i < 1e5; i += 1); // <- if you try this at home don''t forget the ; time[0] += performance.now()-start; setTimeout(letOutside,10); } function letOutside(){ // this function is twice as quick as test on chrome var start = performance.now(); {let i; for(i = 0; i < 1e5; i += 1)} time[1] += performance.now()-start; setTimeout(displayResults,10); }

Como Chrome es el jugador principal y las variables de ámbito bloqueadas para los contadores de bucle están en todas partes, aquellos que necesitan un código de ejecución y sienten que las variables de ámbito de bloque son una function{}(for(let i; i<2;i++}{...})//?WHY? importante function{}(for(let i; i<2;i++}{...})//?WHY? Debería considerar por el momento la sintaxis alternativa y declarar el contador de bucle fuera del bucle.

Me gustaría decir que la diferencia de tiempo es trivial, pero a la luz del hecho de que todo el código dentro de la función no se optimiza usando for(let i... debe usarse con cuidado.


@TJCrowder ya respondió la pregunta del título, pero yo responderé a tus dudas.

Cuando me encontré con esto por primera vez, pensé que era debido a la instancia recién creada de i, pero a continuación se muestra que esto no es así.

En realidad, se debe al alcance recién creado para la variable i . Lo que no está (todavía) optimizado, ya que es más complicado que un simple alcance de bloque .

Vea el segundo fragmento de código, ya que he eliminado cualquier posibilidad de que la declaración let adicional se optimice con ini con random y luego se agregue al valor indeterminado de k.

Su declaración adicional let j en

{let i; for (i = 0; i < 1e3; i ++) {let j = Math.random(); j += i; k += j;}} // I''ll ignore the `p` variable you had in your code

fue optimizado fuera Eso es algo bastante trivial para un optimizador, puede evitar esa variable por completo simplificando su cuerpo de bucle para

k += Math.random() + i;

El alcance no es realmente necesario a menos que crees cierres allí o utilices eval o abominaciones similares.

Si introducimos dicho cierre (como código muerto, es de esperar que el optimizador no se dé cuenta de eso) y haga un pit

{let i; for (i=0; i < 1e3; i++) { let j=Math.random(); k += j+i; function f() { j; }}}

en contra

for (let i=0; i < 1e3; i++) { let j=Math.random(); k += j+i; function f() { j; }}

Luego veremos que corren aproximadamente a la misma velocidad.

var times = [0,0]; // hold total times var count = 0; // number of tests var soak = 0; // to stop optimizations function test1(){ var k = time[1]; var start = performance.now(); {let i; for(i=0; i < 1e3; i++){ let j=Math.random(); k += j+i; function f() { j; }}} times[0] += performance.now()-start; soak += k; setTimeout(test2,10) } function test2(){ var k = time[1]; var start = performance.now(); for(let i=0; i < 1e3; i++){ let j=Math.random(); k += j+i; function f() { j; }} times[1] += performance.now()-start; soak += k; setTimeout(display,10) } // display results function display(){ var tot =times[0]+times[1]; time.textContent = tot.toFixed(3) + "ms"; time1.textContent = ((times[0]/tot)*100).toFixed(2) + "% " + times[0].toFixed(3) + "ms"; time2.textContent = ((times[1]/tot)*100).toFixed(2) + "% " + times[1].toFixed(3) + "ms"; if(count++ < 1000){ setTimeout(test1,10); } } var div = document.createElement("div"); var div1 = document.createElement("div"); var div2 = document.createElement("div"); var time = document.createElement("span"); var time1 = document.createElement("span"); var time2 = document.createElement("span"); div.textContent = "Total execution time : " div1.textContent = "Test 1 : " div2.textContent = "Test 2 : " div.appendChild(time); div1.appendChild(time1); div2.appendChild(time2); document.body.appendChild(div); document.body.appendChild(div1); document.body.appendChild(div2); display();


Actualización: junio de 2018: Chrome ahora optimiza esto mucho mejor que cuando esta pregunta y respuesta se publicaron por primera vez; ya no hay una penalización apreciable por el uso de let en for si no está creando funciones en el bucle (y si lo está, los beneficios valen el costo).

Debido a que se crea una nueva i para cada iteración del bucle, los cierres creados dentro del bucle se cierran sobre la i para esa iteración . Esto está cubierto por la especificación en el algoritmo para la evaluación de un cuerpo de bucle for , que describe la creación de un nuevo entorno variable por iteración de bucle.

Ejemplo:

for (let i = 0; i < 5; ++i) { setTimeout(function() { console.log("i = " + i); }, i * 50); } // vs. setTimeout(function() { let j; for (j = 0; j < 5; ++j) { setTimeout(function() { console.log("j = " + j); }, j * 50); } }, 400);

Eso es más trabajo. Si no necesita una nueva i para cada bucle, utilice let fuera del bucle. Consulte la actualización anterior, no hay necesidad de evitarlo, excepto los casos de borde.

Podemos esperar que ahora que todo excepto los módulos se ha implementado, V8 probablemente mejorará la optimización de las nuevas cosas, pero no es sorprendente que la funcionalidad se deba priorizar inicialmente a la optimización.

Es genial que otros motores ya hayan hecho la optimización, pero el equipo V8 aparentemente aún no ha llegado allí. Ver actualización arriba.