v8 const, let ve var'ın JavaScript performans etkileri?


90

İşlevsel farklılıklardan bağımsız olarak, yeni anahtar kelimelerin 'izin ver' ve 'sabit' kullanımının performans üzerinde 'var'a göre genelleştirilmiş veya belirli bir etkisi var mı?

Programı çalıştırdıktan sonra:

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

.. Sonuçlarım şöyleydi:

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}

Bununla birlikte, burada belirtildiği gibi tartışma, belirli senaryolar altında performans farklılıkları için gerçek bir potansiyele işaret ediyor gibi görünüyor: https://esdiscuss.org/topic/performance-concern-with-let-const


Bunun kullanıma bağlı olduğunu düşünüyorum, örneğin letblok kapsamında kullanılanlar, blok kapsamı varolmayan, sadece fonksiyon kapsamına sahip olanlardan daha performanslı olmalıdır .
adeneo

Sorabilirsem, bu neden @adeneo?
sean2078

1
@ sean2078 - yalnızca bir blok kapsamında yaşayan bir değişkeni bildirmeniz gerekiyorsa, letbunu yapıp daha sonra çöp toplanırken, varişlev kapsamlı olan, aynı şekilde çalışmayacaktır. Yine bence, o kullanımına özgü böylece, hem de bu letve const edebilirsiniz daha fazla ölçülebilir, ama her zaman olmaz.
adeneo

1
Alıntılanan kodun varve arasındaki herhangi bir farkı göstermesi anlamında kafam karıştı let: Asla hiç kullanmıyor let.
TJ Crowder

1
Şu anda değil - yalnızca const vs. var .. İlk olarak gist.github.com/srikumarks/1431640 adresinden (kredi srikumarks'a kredi) sağlanmıştır ancak kodun sorgulanması için talepte bulunulmuştur
sean2078

Yanıtlar:


123

TL; DR

Teorik olarak , bu döngünün optimize edilmemiş bir versiyonu:

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

aşağıdakilerle aynı döngünün optimize edilmemiş bir sürümünden daha yavaş olabilir var:

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

Bir nedeni farklı i değişken ile, her döngü tekrarında için oluşturulan let, oysa sadece bir tane var iolan var.

Buna karşı tartışmak , bir optimizasyon avantajı sunabilecek şekilde yalnızca döngü içinde bildirilirken var, döngü dışında bildirildiği için kaldırıldığı gerçeğidir let.

Pratikte , 2018'de burada, modern JavaScript motorları, bu farkı ne zaman optimize edebileceğini bilmek için döngünün yeterince iç gözlemini yapıyor. (O zamandan önce bile, letdöngünüzün fazladan ilgili ek yükün yine de ortadan kalkmasına neden olacak kadar çalışması muhtemeldir. Ama şimdi bunun için endişelenmenize gerek yok.)

Yanlış anlaşılmaları son derece kolay olduğundan sentetik karşılaştırmalara dikkat edin ve JavaScript motoru optimize edicileri gerçek kodun yapmadığı şekillerde (hem iyi hem de kötü yollarla) tetikleyin. Bununla birlikte, sentetik bir kıyaslama istiyorsanız, işte bir tane:

V8 / Chrome veya SpiderMonkey / Firefox'taki bu sentetik testte önemli bir fark olmadığını söylüyor. (Her iki tarayıcıda da tekrarlanan testlerde bir kazanan veya diğeri kazanır ve her iki durumda da bir hata marjı dahilindedir.) Ama yine, bu sizin kodunuz değil, sentetik bir kıyaslamadır. Kodunuzda bir performans sorunu olduğunda ve eğer kodunuzun performansından endişe edin.

Bir stil meselesi olarak, letbir kapanışta döngü değişkenini kullanırsam kapsam belirleme avantajını tercih ederim ve döngülerde kapanma fayda sağlar.

Detaylar

Bir döngü arasındaki varve letiçindeki önemli fark , her yineleme için forfarklı bir döngü ioluşturulmasıdır; klasik "döngüdeki kapanışlar" sorununu ele alır:

Her döngü gövdesi ( özellik bağlantısı ) için yeni EnvironmentRecord oluşturmak bir iştir ve çalışma zaman alır, bu nedenle teoride letsürüm sürümden daha yavaştır var.

Ancak, iyukarıdaki runnable snippet örneğinde yaptığım gibi, yalnızca, kullanan döngü içinde bir işlev (kapatma) oluşturduğunuzda fark önemlidir . Aksi takdirde, ayrım gözlenemez ve optimize edilebilir.

Burada 2018'de, V8 (ve Firefox'taki SpiderMonkey), letyineleme başına değişken semantiğini kullanmayan bir döngüde performans maliyeti olmadığına dair yeterli iç gözlem yapıyor gibi görünüyor . Bu testi görün .


Bazı durumlarda, özellikle genel değişkenler için, constsağlamayan bir optimizasyon fırsatı sağlayabilir var.

Küresel değişkenle ilgili sorun, küresel olmasıdır; Herhangi kod yerde bu erişebilir. Bu nedenle, varasla değiştirmek istemediğiniz (ve kodunuzda hiçbir zaman değişiklik yapmadığınız) bir değişken bildirirseniz, motor daha sonra yüklenen veya benzeri bir kodun sonucu olarak bunun asla değişmeyeceğini varsayamaz.

Bununla birlikte const, motora açıkça değerin değiştirilemeyeceğini söylüyorsunuz¹. Dolayısıyla, değerlerin değiştirilemeyeceğini bilerek, onu kullanan koda bir değişken referansı yerine bir hazır bilgi yaymak da dahil olmak üzere, istediği herhangi bir optimizasyonu yapmakta serbesttir.

¹ Nesnelerde değerin nesneye değil, nesneye bir referans olduğunu unutmayın. Böylece const o = {}, nesnenin ( o.answer = 42) durumunu değiştirebilirsiniz , ancak oyeni bir nesneye işaret edemezsiniz (çünkü bu, içerdiği nesne referansının değiştirilmesini gerektirir).


Kullanırken letveya constdiğer varbenzeri durumlarda, farklı performans göstermeleri olası değildir. Kullanmak ister Bu fonksiyon aynı performansa sahip olmalıdır varveya letörneğin:

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

Elbette hepsi önemli değildir ve yalnızca çözülmesi gereken gerçek bir sorun olduğunda endişelenecek bir şey vardır.


Cevabınız için teşekkürler - Katılıyorum, bu yüzden, 1. döngü örneğinizde belirtildiği gibi döngü işlemleri için var kullanımını standartlaştırdım ve performans testinin göründüğü gibi performans farkının esasen var olmadığını varsayarak diğer tüm bildirimler için let / const şimdilik belirtmek için. Belki daha sonra, const üzerindeki optimizasyonlar eklenecektir. Yani, başka biri kod örneği yoluyla fark edilebilir bir fark gösteremediği sürece.
sean2078

@ sean2078: letDöngü örneğinde de kullanıyorum. % 99,999 durumunda performans farkı endişelenmeye değmez.
TJ Crowder

2
2018'in ortalarından itibaren, let ve var özellikli sürümler Chrome'da aynı hıza sahip, bu nedenle artık fark yok.
Maksimum

1
@DanM .: İyi haber, optimizasyon en azından V8 ve SpiderMonkey'de yakalanmış görünüyor. :-)
TJ Crowder

1
Teşekkürler. Yeterince adil.
hypers

20

DÖNGÜ BEYANLARINDA "LET" DAHA İYİ

Navigatörde basit bir testle (5 kez) şöyle:

// WITH VAR
console.time("var-time")
for(var i = 0; i < 500000; i++){}
console.timeEnd("var-time")

Ortalama yürütme süresi 2,5 ms'den fazladır

// WITH LET
console.time("let-time")
for(let i = 0; i < 500000; i++){}
console.timeEnd("let-time")

Ortalama yürütme süresi 1,5 ms'den fazladır

Let ile döngü süresinin daha iyi olduğunu buldum.


7
Bunu Firefox 65.0'da çalıştırarak, ortalama hızları aldım var=138.8msve let=4ms. Bu bir yazım hatası değil, letşu anda 30 kattan fazla daha hızlı
Katamari

7
Bunu Node v12.5'te denedim. Ortalama hızların var=2.6msve olduğunu buldum let=1.0ms. Bu yüzden, Node'un biraz daha hızlı olmasına izin verin.
Kane Hooper

2
İyileştiricilerin varlığında performans testinin zor olduğuna dair olağan noktayı belirtmek gerekirse: let döngüsünün tamamen optimize edildiğini düşünüyorum - sadece bloğun içinde var olmasına izin verin ve döngü hiçbir yan etkiye sahip değil ve V8 bunu yapabileceğini bilecek kadar akıllı. sadece bloğu, ardından döngüyü kaldırın. var bildirimi kaldırıldı, bu yüzden bunu bilemez. Döngüleriniz olduğu gibi 1ms / 0.4ms alırım, ancak her ikisi için de döngü dışında bir değişken j (var veya let) varsa ve bu da artırılmışsa, o zaman 1ms / 1.5ms elde ederim. yani var döngüsü değişiklik yok, döngü artık daha uzun sürecek.
Euan Smith

@KaneHooper - Firefox'ta beş kat farkınız varsa, bunu yapan boş döngü gövdesi olması gerekir. Gerçek döngülerin boş gövdeleri yoktur.
TJ Crowder

2
Sentetik testlere ve özellikle boş gövdeli döngülere dikkat edin . Döngüde gerçekten bir şey yaparsanız, bu sentetik kıyaslama (yine dikkat edin! :-)) önemli bir fark olmadığını gösterir. Ayrıca cevabıma bir tane ekledim, böylece yerinde (bende kaybolmaya devam eden jsPerf testleri gibi değil. :-)). Tekrarlanan koşular birinin kazandığını veya diğerinin kazandığını gösterir. Kesinlikle hiçbir şey kesin değil.
TJ Crowder

10

TJ Crowder'ın cevabı çok mükemmel.

İşte bir ek: "Mevcut değişken bildirimlerini const olarak düzenlerken paranın karşılığını en çok ne zaman alırım?"

En fazla performans artışının "dışa aktarılan" işlevlerle ilgisi olduğunu buldum.

Dolayısıyla, A, B, R ve Z dosyası, U dosyasında, uygulamanız aracılığıyla yaygın olarak kullanılan bir "yardımcı program" işlevini çağırıyorsa, bu yardımcı program işlevini "const" olarak ve bir sabit dosyaya üst dosya başvurusu değiştirebilir bazı iyileştirilmiş performans. Bana göre ölçülebilir derecede daha hızlı değildi, ancak genel bellek tüketimi, büyük ölçüde monolitik Frankenstein-ed uygulamam için yaklaşık% 1-3 oranında azaldı. Eğer bulutta veya baremetal sunucunuzda çantalar dolusu nakit harcıyorsanız, bu değişken beyanlarından bazılarını taramak ve güncellemek için 30 dakika harcamak iyi bir neden olabilir.

Const, var ve kapakların nasıl çalıştığını okursanız, muhtemelen yukarıdakileri zaten bitirmişsinizdir ... ama "göz attığınızda": D.

Güncellemeyi yaptığım zaman v8.12.0 düğümündeki karşılaştırmadan hatırladığım kadarıyla, uygulamam boşta ~ 240MB RAM'den ~ 233MB RAM'e geçti.


3

TJ Crowder'ın cevabı çok iyi ama:

  1. 'Let', kodu daha güçlü değil, daha okunaklı hale getirmek için yapılmıştır
  2. teoriye göre var daha yavaş olur
  3. uygulama ile derleyici tamamlanmamış bir programı tamamen (statik analiz) çözemez, dolayısıyla bazen optimizasyonu kaçırır
  4. her durumda 'let' kullanmak iç gözlem için daha fazla CPU gerektirir, google v8 ayrıştırmaya başladığında tezgah başlatılmalıdır
  5. iç gözlem başarısız olursa 'izin ver', V8 çöp toplayıcısını zorlar, serbest bırakmak / yeniden kullanmak için daha fazla yineleme gerektirir. ayrıca daha fazla RAM tüketecektir. yedek kulübesi bu noktaları dikkate almalıdır
  6. Google Closure let in değişkenini dönüştürecek ...

Var ve let arasındaki performans boşluğunun etkisi, tek bir temel döngüde değil, gerçek hayattaki tam programda görülebilir.

Her neyse, gerek duymadığınız yerde let'i kullanmak, kodunuzu daha az okunabilir hale getirir.


İlgi dışı - hangi teoriye göre letdaha yavaş var? Özellikle yukarıdaki cevaba yapılan yorumlarda daha hızlı olduğunu gösteren fikir birliği göz önüne alındığında ?
JamesTheAwesomeDude
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.