Independentemente das diferenças funcionais, o uso das novas palavras-chave 'let' e 'const' tem algum impacto generalizado ou específico no desempenho em relação a 'var'?
Depois de executar o 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)));
.. Meus resultados foram os seguintes:
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}
No entanto, a discussão, conforme observado aqui, parece indicar um potencial real para diferenças de desempenho em certos cenários: https://esdiscuss.org/topic/performance-concern-with-let-const
javascript
performance
constants
v8
let
sean2078
fonte
fonte
let
usado no escopo de bloco deve ter mais desempenho do quevar
, que não tem escopo de bloco, mas apenas escopo de função.let
faria isso e seria coletado como lixo, enquantovar
, que tem escopo de função, não funcionaria necessariamente da mesma maneira. Novamente, eu acho que é tão específico para o uso, que amboslet
econst
podem ter mais desempenho, mas nem sempre seria.var
elet
: ele nunca usalet
.Respostas:
TL; DR
Em teoria , uma versão não otimizada desse loop:
for (let i = 0; i < 500; ++i) { doSomethingWith(i); }
pode ser mais lento do que uma versão não otimizada do mesmo loop com
var
:for (var i = 0; i < 500; ++i) { doSomethingWith(i); }
porque uma variável diferente
i
é criada para cada iteração do loop comlet
, enquanto há apenas umai
comvar
.O que se argumenta contra isso é o fato de que
var
é içado de modo que seja declarado fora do loop, enquanto quelet
só é declarado dentro do loop, o que pode oferecer uma vantagem de otimização.Na prática , aqui em 2018, os mecanismos JavaScript modernos fazem introspecção suficiente do loop para saber quando ele pode otimizar essa diferença. (Mesmo antes disso, é provável que seu loop estivesse fazendo um trabalho suficiente para que a
let
sobrecarga relacionada ao adicional fosse eliminada de qualquer maneira. Mas agora você nem precisa se preocupar com isso.)Cuidado com os benchmarks sintéticos , pois são extremamente fáceis de errar e acione os otimizadores do mecanismo JavaScript de maneiras que o código real não faz (boas e más). No entanto, se você quiser um benchmark sintético, aqui está um:
Exibir trecho 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">
Ele diz que não há diferença significativa naquele teste sintético no V8 / Chrome ou no SpiderMonkey / Firefox. (Testes repetidos em ambos os navegadores têm um vencedor ou outro vencedor e, em ambos os casos, dentro de uma margem de erro.) Mas, novamente, é um benchmark sintético, não o seu código. Preocupe-se com o desempenho do seu código quando e se ele apresentar um problema de desempenho.
Por uma questão de estilo, eu prefiro
let
o benefício de escopo e o benefício de fechamento em loops se eu usar a variável de loop em um fechamento.Detalhes
A diferença importante entre
var
elet
em umfor
loop é que um diferentei
é criado para cada iteração; trata do problema clássico de "fechamentos em loop":Exibir trecho 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);
Criar o novo EnvironmentRecord para cada corpo de loop ( link de especificação ) é trabalhoso e leva tempo, por isso, em teoria, a
let
versão é mais lenta do que avar
versão.Mas a diferença só importa se você criar uma função (encerramento) dentro do loop que usa
i
, como fiz no exemplo de snippet executável acima. Caso contrário, a distinção não pode ser observada e pode ser otimizada.Aqui em 2018, parece que o V8 (e SpiderMonkey no Firefox) está fazendo introspecção suficiente para que não haja custo de desempenho em um loop que não use a
let
semântica de variável por iteração. Veja este teste .Em alguns casos,
const
pode fornecer uma oportunidade de otimização quevar
não seria, especialmente para variáveis globais.O problema com uma variável global é que ela é, bem, global; qualquer código em qualquer lugar poderia acessá-lo. Portanto, se você declarar uma variável com
var
a qual nunca pretende alterar (e nunca alterar em seu código), o mecanismo não pode assumir que nunca vai mudar como resultado de código carregado posteriormente ou similar.Com
const
, entretanto, você está dizendo explicitamente ao mecanismo que o valor não pode ser alterado¹. Portanto, é livre para fazer qualquer otimização que desejar, incluindo a emissão de um literal em vez de uma referência de variável para o código que o usa, sabendo que os valores não podem ser alterados.¹ Lembre-se de que, com objetos, o valor é uma referência ao objeto, não o objeto em si. Assim
const o = {}
, você pode alterar o estado do objeto (o.answer = 42
), mas não podeo
apontar para um novo objeto (porque isso exigiria a alteração da referência do objeto que ele contém).Ao usar
let
ouconst
em outrasvar
situações semelhantes, eles provavelmente não terão um desempenho diferente. Esta função deve ter exatamente o mesmo desempenho se você usarvar
oulet
, por exemplo:function foo() { var i = 0; while (Math.random() < 0.5) { ++i; } return i; }
É claro que é improvável que isso importe e algo com que se preocupar apenas se e quando houver um problema real para resolver.
fonte
let
o exemplo de loop também. A diferença de desempenho simplesmente não vale a pena se preocupar com isso no case de 99,999%."LET" É MELHOR EM DECLARAÇÕES DE LOOP
Com um teste simples (5 vezes) no navegador assim:
// WITH VAR console.time("var-time") for(var i = 0; i < 500000; i++){} console.timeEnd("var-time")
O tempo médio de execução é de mais de 2,5 ms
// WITH LET console.time("let-time") for(let i = 0; i < 500000; i++){} console.timeEnd("let-time")
O tempo médio de execução é de mais de 1,5 ms
Achei que o tempo de loop com let é melhor.
fonte
var=138.8ms
elet=4ms
. Isso não é um erro de digitação,let
é mais de 30 vezes mais rápido agoravar=2.6ms
elet=1.0ms
. Então vamos deixar o Node é um pouco mais do que duas vezes mais rápido.A resposta de TJ Crowder é excelente.
Aqui está um acréscimo de: "Quando eu obteria o maior retorno do meu investimento na edição de declarações var existentes para const?"
Descobri que o maior aumento de desempenho teve a ver com funções "exportadas".
Portanto, se os arquivos A, B, R e Z estão chamando uma função de "utilitário" no arquivo U que é comumente usada por meio de seu aplicativo, alternar essa função de utilitário para "const" e a referência do arquivo pai para um const pode ser algumas melhorias no desempenho. Pareceu-me que não era mensuravelmente mais rápido, mas o consumo geral de memória foi reduzido em cerca de 1-3% para o meu aplicativo Frankenstein grosseiramente monolítico. O que, se você está gastando muito dinheiro na nuvem ou no servidor baremetal, pode ser um bom motivo para gastar 30 minutos para vasculhar e atualizar algumas dessas declarações var para const.
Sei que se você ler como const, var e let trabalhar nos bastidores, você provavelmente já concluiu o acima ... mas no caso de você "dar uma olhada" nisso: D.
Pelo que me lembro do benchmarking no nó v8.12.0 quando estava fazendo a atualização, meu aplicativo passou de um consumo ocioso de ~ 240 MB de RAM para ~ 233 MB de RAM.
fonte
A resposta de TJ Crowder é muito boa, mas:
O efeito da lacuna de desempenho entre var e let pode ser visto no programa completo da vida real e não em um único loop básico.
De qualquer forma, usar let onde você não precisa, torna seu código menos legível.
fonte