Independientemente de las diferencias funcionales, ¿el uso de las nuevas palabras clave 'let' y 'const' tiene algún impacto generalizado o específico en el rendimiento en relación con 'var'?
Después de ejecutar el programa:
function timeit(f, N, S) {
var start, timeTaken;
var stats = {min: 1e50, max: 0, N: 0, sum: 0, sqsum: 0};
var i;
for (i = 0; i < S; ++i) {
start = Date.now();
f(N);
timeTaken = Date.now() - start;
stats.min = Math.min(timeTaken, stats.min);
stats.max = Math.max(timeTaken, stats.max);
stats.sum += timeTaken;
stats.sqsum += timeTaken * timeTaken;
stats.N++
}
var mean = stats.sum / stats.N;
var sqmean = stats.sqsum / stats.N;
return {min: stats.min, max: stats.max, mean: mean, spread: Math.sqrt(sqmean - mean * mean)};
}
var variable1 = 10;
var variable2 = 10;
var variable3 = 10;
var variable4 = 10;
var variable5 = 10;
var variable6 = 10;
var variable7 = 10;
var variable8 = 10;
var variable9 = 10;
var variable10 = 10;
function varAccess(N) {
var i, sum;
for (i = 0; i < N; ++i) {
sum += variable1;
sum += variable2;
sum += variable3;
sum += variable4;
sum += variable5;
sum += variable6;
sum += variable7;
sum += variable8;
sum += variable9;
sum += variable10;
}
return sum;
}
const constant1 = 10;
const constant2 = 10;
const constant3 = 10;
const constant4 = 10;
const constant5 = 10;
const constant6 = 10;
const constant7 = 10;
const constant8 = 10;
const constant9 = 10;
const constant10 = 10;
function constAccess(N) {
var i, sum;
for (i = 0; i < N; ++i) {
sum += constant1;
sum += constant2;
sum += constant3;
sum += constant4;
sum += constant5;
sum += constant6;
sum += constant7;
sum += constant8;
sum += constant9;
sum += constant10;
}
return sum;
}
function control(N) {
var i, sum;
for (i = 0; i < N; ++i) {
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
}
return sum;
}
console.log("ctl = " + JSON.stringify(timeit(control, 10000000, 50)));
console.log("con = " + JSON.stringify(timeit(constAccess, 10000000, 50)));
console.log("var = " + JSON.stringify(timeit(varAccess, 10000000, 50)));
.. Mis resultados fueron los siguientes:
ctl = {"min":101,"max":117,"mean":108.34,"spread":4.145407097016924}
con = {"min":107,"max":572,"mean":435.7,"spread":169.4998820058587}
var = {"min":103,"max":608,"mean":439.82,"spread":176.44417700791374}
Sin embargo, la discusión como se indica aquí parece indicar un potencial real de diferencias de rendimiento en ciertos escenarios: https://esdiscuss.org/topic/performance-concern-with-let-const
javascript
performance
constants
v8
let
sean2078
fuente
fuente
let
usado en el alcance del bloque debería ser más eficiente quevar
, que no tiene alcance del bloque, sino solo alcance de la función.let
lo haría y luego se recolectaría la basura, mientrasvar
que, que tiene un alcance de función, no necesariamente funcionaría de la misma manera. Nuevamente, creo que es tan específico para el uso, que amboslet
yconst
pueden ser más eficaces, pero no siempre lo serán.var
ylet
: Nunca se usalet
en absoluto.Respuestas:
TL; DR
En teoría , una versión no optimizada de este ciclo:
for (let i = 0; i < 500; ++i) { doSomethingWith(i); }
podría ser más lento que una versión no optimizada del mismo bucle con
var
:for (var i = 0; i < 500; ++i) { doSomethingWith(i); }
porque se crea una variable diferente
i
para cada iteración del ciclo conlet
, mientras que solo hay unai
convar
.Un argumento en contra es el hecho de que
var
está elevado, por lo que se declara fuera del ciclo, mientraslet
que solo se declara dentro del ciclo, lo que puede ofrecer una ventaja de optimización.En la práctica , aquí en 2018, los motores JavaScript modernos hacen suficiente introspección del bucle para saber cuándo puede optimizar esa diferencia. (Incluso antes de eso, lo más probable es que su bucle estuviera funcionando lo suficiente como para que la
let
sobrecarga adicional relacionada se elimine de todos modos. Pero ahora ni siquiera tiene que preocuparse por eso).Tenga cuidado con los puntos de referencia sintéticos, ya que es muy fácil equivocarse y activan los optimizadores de motor de JavaScript de formas que el código real no hace (tanto las buenas como las malas). Sin embargo, si desea un punto de referencia sintético, aquí tiene uno:
Mostrar fragmento de código
const now = typeof performance === "object" && performance.now ? performance.now.bind(performance) : Date.now.bind(Date); const btn = document.getElementById("btn"); btn.addEventListener("click", function() { btn.disabled = true; runTest(); }); const maxTests = 100; const loopLimit = 50000000; const expectedX = 1249999975000000; function runTest(index = 1, results = {usingVar: 0, usingLet: 0}) { console.log(`Running Test #${index} of ${maxTests}`); setTimeout(() => { const varTime = usingVar(); const letTime = usingLet(); results.usingVar += varTime; results.usingLet += letTime; console.log(`Test ${index}: var = ${varTime}ms, let = ${letTime}ms`); ++index; if (index <= maxTests) { setTimeout(() => runTest(index, results), 0); } else { console.log(`Average time with var: ${(results.usingVar / maxTests).toFixed(2)}ms`); console.log(`Average time with let: ${(results.usingLet / maxTests).toFixed(2)}ms`); btn.disabled = false; } }, 0); } function usingVar() { const start = now(); let x = 0; for (var i = 0; i < loopLimit; i++) { x += i; } if (x !== expectedX) { throw new Error("Error in test"); } return now() - start; } function usingLet() { const start = now(); let x = 0; for (let i = 0; i < loopLimit; i++) { x += i; } if (x !== expectedX) { throw new Error("Error in test"); } return now() - start; }
<input id="btn" type="button" value="Start">
Dice que no hay una diferencia significativa en esa prueba sintética en V8 / Chrome o SpiderMonkey / Firefox. (Las pruebas repetidas en ambos navegadores hacen que uno gane, o el otro gane, y en ambos casos dentro de un margen de error). Pero nuevamente, es un punto de referencia sintético, no su código. Preocúpese por el rendimiento de su código cuando y si su código tiene un problema de rendimiento.
Por una cuestión de estilo, prefiero
let
para el beneficio de alcance y el beneficio de cierre en bucles si uso la variable de bucle en un cierre.Detalles
La diferencia importante entre
var
ylet
en unfor
bucle es quei
se crea una diferente para cada iteración; aborda el clásico problema de "cierres en bucle":Mostrar fragmento de código
function usingVar() { for (var i = 0; i < 3; ++i) { setTimeout(function() { console.log("var's i: " + i); }, 0); } } function usingLet() { for (let i = 0; i < 3; ++i) { setTimeout(function() { console.log("let's i: " + i); }, 0); } } usingVar(); setTimeout(usingLet, 20);
Crear el nuevo EnvironmentRecord para cada cuerpo de bucle ( enlace de especificación ) es un trabajo y el trabajo lleva tiempo, por lo que, en teoría, la
let
versión es más lenta que lavar
versión.Pero la diferencia solo importa si crea una función (cierre) dentro del bucle que usa
i
, como hice en el ejemplo de fragmento ejecutable anterior. De lo contrario, la distinción no se puede observar y se puede optimizar.Aquí, en 2018, parece que V8 (y SpiderMonkey en Firefox) están haciendo suficiente introspección para que no haya costos de rendimiento en un bucle que no utiliza la
let
semántica de variable por iteración. Vea esta prueba .En algunos casos,
const
puede proporcionar una oportunidad de optimización quevar
no lo haría, especialmente para las variables globales.El problema con una variable global es que es, bueno, global; cualquier código en cualquier lugar podría acceder a él. Entonces, si declara una variable con la
var
que nunca tiene la intención de cambiar (y nunca cambia su código), el motor no puede asumir que nunca cambiará como resultado de un código cargado más tarde o similar.Con
const
, sin embargo, le está diciendo explícitamente al motor que el valor no puede cambiar¹. Por lo tanto, es libre de hacer cualquier optimización que desee, incluida la emisión de una referencia literal en lugar de una variable al código que la usa, sabiendo que los valores no se pueden cambiar.¹ Recuerde que con los objetos, el valor es una referencia al objeto, no al objeto en sí. Entonces
const o = {}
, con , puede cambiar el estado del objeto (o.answer = 42
), pero no puedeo
señalar un nuevo objeto (porque eso requeriría cambiar la referencia del objeto que contiene).Cuando se usa
let
oconst
en otrasvar
situaciones similares, no es probable que tengan un rendimiento diferente. Esta función debería tener exactamente el mismo rendimiento si usavar
olet
, por ejemplo:function foo() { var i = 0; while (Math.random() < 0.5) { ++i; } return i; }
Por supuesto, todo es poco probable que importe y es algo de lo que preocuparse solo si hay un problema real que resolver.
fuente
let
en el ejemplo de bucle. No vale la pena preocuparse por la diferencia de rendimiento en el caso del 99,999%."LET" ES MEJOR EN LAS DECLARACIONES DE BUCLE
Con una simple prueba (5 veces) en un navegador así:
// WITH VAR console.time("var-time") for(var i = 0; i < 500000; i++){} console.timeEnd("var-time")
El tiempo medio de ejecución es de más de 2,5 ms.
// WITH LET console.time("let-time") for(let i = 0; i < 500000; i++){} console.timeEnd("let-time")
El tiempo medio de ejecución es de más de 1,5 ms.
Descubrí que el tiempo de ciclo con let es mejor.
fuente
var=138.8ms
ylet=4ms
. Eso no es un error tipográfico,let
es más de 30 veces más rápido en este momentovar=2.6ms
ylet=1.0ms
. Así que dejar entrar a Node es un poco más del doble de rápido.La respuesta de TJ Crowder es excelente.
Aquí hay una adición de: "¿Cuándo sacaré el máximo provecho de mi inversión en la edición de declaraciones var existentes a const?"
Descubrí que la mayor mejora del rendimiento tenía que ver con las funciones "exportadas".
Entonces, si los archivos A, B, R y Z están llamando a una función de "utilidad" en el archivo U que se usa comúnmente a través de su aplicación, entonces cambiar esa función de utilidad a "const" y la referencia del archivo principal a una const puede eak un rendimiento mejorado. Me pareció que no era mucho más rápido, pero el consumo total de memoria se redujo en aproximadamente un 1-3% para mi aplicación Frankenstein-ed extremadamente monolítica. Lo cual, si está gastando bolsas de dinero en efectivo en la nube o en su servidor baremetal, podría ser una buena razón para dedicar 30 minutos a revisar y actualizar algunas de esas declaraciones var a const.
Me doy cuenta de que si lees cómo const, var y dejas trabajar bajo las sábanas, probablemente ya hayas concluido lo anterior ... pero en caso de que "echas un vistazo": D.
Por lo que recuerdo de la evaluación comparativa en el nodo v8.12.0 cuando estaba haciendo la actualización, mi aplicación pasó de un consumo inactivo de ~ 240 MB de RAM a ~ 233 MB de RAM.
fuente
La respuesta de TJ Crowder es muy buena pero:
El efecto de la brecha de rendimiento entre var y let se puede ver en un programa completo de la vida real y no en un solo bucle básico.
De todos modos, usar let donde no es necesario, hace que su código sea menos legible.
fuente