v8 JavaScript implicaciones de rendimiento de const, let y var?


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 ha señalado aquí parece indicar un potencial real de diferencias de rendimiento en ciertos escenarios: https://esdiscuss.org/topic/performance-concern-with-let-const

Author: Evan Carroll, 2016-10-16

1 answers

TL; DR

En teoría , una versión no optimizada de este bucle:

for (let i = 0; i < 500; ++i) {
    doSomethingWith(i);
}

Sería más lento que una versión no optimizada del mismo bucle con var:

for (var i = 0; i < 500; ++i) {
    doSomethingWith(i);
}

Porque un diferente i la variable se crea para cada iteración de bucle con let, mientras que solo hay una i con var.

En la práctica , aquí en 2018, V8 hace suficiente introspección del bucle para saber cuándo puede optimizar esa diferencia. (Incluso antes de eso, las probabilidades son que tu bucle estaba haciendo suficiente trabajo que la sobrecarga adicional relacionada con let se eliminó de todos modos. Pero ahora ni siquiera tienes que preocuparte por eso.)

Detalles

La diferencia importante entre var y let en un bucle for es que se crea un i diferente para cada iteración; aborda el problema clásico de "cierres en bucle":

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);

Creando el nuevo EnvironmentRecord para cada cuerpo de bucle (spec link ) es trabajo, y el trabajo lleva tiempo, por lo que en teoría la versión let es más lenta que la versión var.

Pero la diferencia solo importa si creas una función (closure) 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á haciendo suficiente introspección que no hay costo de rendimiento en un bucle que no hace uso de la semántica variable-por-iteración de let. Véase esta prueba jsPerf.


En algunos casos, const bien puede proporcionar una oportunidad para la optimización que var 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 ella. Entonces, si declara una variable con var que nunca tiene la intención de cambiar (y nunca cambia en su código), el motor no se puede asumir que nunca va a cambiar como resultado de código cargado más tarde o similar.

Con const, sin embargo, le estás diciendo explícitamente al motor que el valor no puede cambiar1. Por lo tanto, es libre de hacer cualquier optimización que desee, incluida la emisión de un literal en lugar de una referencia variable al código que lo usa, sabiendo que los valores no se pueden cambiar.

1 Recuerde que con objetos, el valor es una referencia al objeto, no al objeto en sí. Así que con const o = {}, puede cambiar el estado del objeto (o.answer = 42), pero no puede hacer que o apunte a un nuevo objeto (porque eso requeriría cambiar la referencia de objeto que contiene).


Cuando se usa let o const en otras situaciones similares a var, no es probable que tengan un rendimiento diferente. Esta función debe tener exactamente el mismo rendimiento si se utiliza var o let, por ejemplo:

function foo() {
    var i = 0;
    while (Math.random() < 0.5) {
        ++i;
    }
    return i;
}

Es todo, por supuesto, poco probable que importe y algo de lo que preocuparse solo si y cuando hay un problema real que resolver.

 42
Author: T.J. Crowder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-11 12:08:12