Niezależnie od różnic funkcjonalnych, czy użycie nowych słów kluczowych „let” i „const” ma jakikolwiek ogólny lub konkretny wpływ na wydajność w stosunku do „var”?
Po uruchomieniu programu:
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)));
.. Moje wyniki były następujące:
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}
Jednak dyskusja, jak tu wspomniano, wydaje się wskazywać na rzeczywisty potencjał różnic w wydajności w pewnych scenariuszach: https://esdiscuss.org/topic/performance-concern-with-let-const
javascript
performance
constants
v8
let
sean2078
źródło
źródło
let
użycie w zakresie blokowym powinno być bardziej wydajne niżvar
, które nie ma zakresu blokowego, ale tylko zakres funkcji.let
zrobiłaby to, a następnie została zebrana jako śmieci, podczas gdyvar
, która jest objęta zakresem funkcji, nie musi działać w ten sam sposób. Znowu myślę, że jest to tak specyficzne dla zastosowania, że jednolet
i drugieconst
może być bardziej wydajne, ale nie zawsze będzie.var
ilet
: W ogóle nie używalet
.Odpowiedzi:
TL; DR
W teorii niezoptymalizowana wersja tej pętli:
for (let i = 0; i < 500; ++i) { doSomethingWith(i); }
może być wolniejsze niż niezoptymalizowana wersja tej samej pętli z
var
:for (var i = 0; i < 500; ++i) { doSomethingWith(i); }
ponieważ dla każdej iteracji pętli tworzona jest inna
i
zmiennalet
, podczas gdy jest tylko jedna zmiennai
zvar
.Argumentując przeciwko temu ,
var
jest podniesiony, więc jest deklarowany poza pętlą, podczas gdylet
jest deklarowany tylko w pętli, co może dać zaletę optymalizacji.W praktyce w 2018 roku nowoczesne silniki JavaScript przeprowadzają wystarczająco dużo introspekcji pętli, aby wiedzieć, kiedy mogą zoptymalizować tę różnicę. (Nawet wcześniej istnieje prawdopodobieństwo, że pętla wykonywała wystarczająco dużo pracy, aby i tak zlikwidować dodatkowe
let
obciążenie. Ale teraz nie musisz się tym nawet martwić).Uważaj na syntetyczne testy porównawcze, ponieważ są one niezwykle łatwe do popełnienia i uruchamiają optymalizatory silnika JavaScript w sposób, w jaki nie ma tego prawdziwy kod (zarówno dobry, jak i zły). Jeśli jednak chcesz syntetycznego testu porównawczego, oto jeden:
Pokaż fragment kodu
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">
Mówi, że nie ma znaczącej różnicy w tym syntetycznym teście na V8 / Chrome lub SpiderMonkey / Firefox. (W powtórzonych testach w obu przeglądarkach jedna wygrywa, a druga wygrywa, w obu przypadkach z marginesem błędu). Ale znowu jest to syntetyczny test porównawczy, a nie twój kod. Martw się o wydajność kodu, kiedy i czy w kodzie występuje problem z wydajnością.
Ze względu na styl, wolę
let
korzyści związane ze skalowaniem i zamknięciem w pętlach, jeśli używam zmiennej pętli w zamknięciu.Detale
Istotna różnica między pętlą
var
ilet
wfor
pętli polega na tym, żei
dla każdej iteracji tworzone jest inne ; rozwiązuje klasyczny problem „zamknięć w pętli”:Pokaż fragment kodu
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);
Tworzenie nowego rejestru EnvironmentRecord dla każdej treści pętli ( łącza specyfikacji ) jest pracochłonne i wymaga czasu, dlatego teoretycznie
let
wersja jest wolniejsza niżvar
wersja.Ale różnica ma znaczenie tylko wtedy, gdy utworzysz funkcję (zamknięcie) w pętli, która używa
i
, tak jak to zrobiłem w powyższym przykładzie wykonywalnego fragmentu. W przeciwnym razie tego rozróżnienia nie można zaobserwować i można je zoptymalizować.Tutaj w 2018 roku wygląda na to, że V8 (i SpiderMonkey w Firefoksie) przeprowadza wystarczającą introspekcję, aby w pętli nie było kosztów wydajnościowych, które nie wykorzystują
let
semantyki zmiennej na iterację. Zobacz ten test .W niektórych przypadkach
const
może stanowić okazję do optymalizacji, któravar
by się nie udała, zwłaszcza w przypadku zmiennych globalnych.Problem ze zmienną globalną polega na tym, że jest ona globalna; każdy kod w dowolnym miejscu mógłby uzyskać do niego dostęp. Więc jeśli zadeklarujesz zmienną
var
, której nigdy nie zamierzasz zmieniać (i nigdy nie zmieniasz kodu), silnik nie może założyć, że nigdy się nie zmieni w wyniku kodu załadowanego później lub w podobny sposób.Dzięki
const
, choć jesteś wyraźnie mówi, że silnik nie może change¹ wartość. Dlatego można dowolnie przeprowadzać optymalizację, w tym emitować literał zamiast odniesienia do zmiennej do kodu, który go używa, wiedząc, że wartości nie można zmienić.¹ Pamiętaj, że w przypadku obiektów wartość jest odniesieniem do obiektu, a nie samym obiektem. Tak więc
const o = {}
, możesz zmienić stan obiektu (o.answer = 42
), ale nie możeszo
wskazać nowego obiektu (ponieważ wymagałoby to zmiany odniesienia do obiektu, który zawiera).Podczas używania
let
lubconst
w innychvar
sytuacjach podobnych, prawdopodobnie nie będą miały innej wydajności. Ta funkcja powinna mieć dokładnie taką samą wydajność, niezależnie od tego, czy używasz,var
czylet
na przykład:function foo() { var i = 0; while (Math.random() < 0.5) { ++i; } return i; }
To wszystko oczywiście nie będzie miało znaczenia i jest czymś, czym należy się martwić tylko wtedy, gdy pojawi się prawdziwy problem do rozwiązania.
źródło
let
w przykładzie pętli. Różnica w wydajności po prostu nie jest warta martwienia się o to w przypadku 99,999%.„LET” JEST LEPSZE W DEKLARACJACH PĘTLI
Za pomocą prostego testu (5 razy) w nawigatorze:
// WITH VAR console.time("var-time") for(var i = 0; i < 500000; i++){} console.timeEnd("var-time")
Średni czas wykonania to ponad 2,5 ms
// WITH LET console.time("let-time") for(let i = 0; i < 500000; i++){} console.timeEnd("let-time")
Średni czas wykonania to ponad 1,5 ms
Odkryłem, że czas pętli z let jest lepszy.
źródło
var=138.8ms
ilet=4ms
. To nie jest literówka,let
jest teraz ponad 30 razy szybszyvar=2.6ms
ilet=1.0ms
. Więc wpuść Node jest nieco ponad dwa razy szybszy.Odpowiedź TJ Crowdera jest doskonała.
Oto dodatek: „Kiedy uzyskam największy zwrot z każdej zainwestowanej złotówki przy edycji istniejących deklaracji var na const?”
Zauważyłem, że największy wzrost wydajności miał związek z funkcjami „eksportowanymi”.
Więc jeśli pliki A, B, R i Z wywołują funkcję „narzędzia” w pliku U, która jest powszechnie używana w Twojej aplikacji, przełączenie tej funkcji narzędzia na „const” i odniesienie do pliku nadrzędnego na stałą może eak trochę lepszej wydajności. Wydawało mi się, że nie jest to mierzalnie szybsze, ale ogólne zużycie pamięci zostało zmniejszone o około 1-3% w przypadku mojej rażąco monolitycznej aplikacji Frankenstein-ed. Co, jeśli wydajesz mnóstwo gotówki w chmurze lub na serwerze baremetal, może być dobrym powodem, aby poświęcić 30 minut na przeszukanie i zaktualizowanie niektórych z tych deklaracji var do const.
Zdaję sobie sprawę, że jeśli przeczytasz jak const, var i pozwolisz pracować pod okładkami, prawdopodobnie już doszedłeś do powyższego ... ale na wypadek, gdybyś "spojrzał" na to: D.
Z tego, co pamiętam z testów porównawczych w węźle v8.12.0, kiedy wykonywałem aktualizację, moja aplikacja przeszła z bezczynnego zużycia ~ 240 MB RAM do ~ 233 MB RAM.
źródło
Odpowiedź TJ Crowdera jest bardzo dobra, ale:
Efekt luki w wydajności między var i let można zobaczyć w prawdziwym, kompletnym programie, a nie w pojedynczej podstawowej pętli.
W każdym razie użycie let tam, gdzie nie musisz, sprawia, że twój kod jest mniej czytelny.
źródło