it-swarm-tr.com

Anonim işlevlerin kullanımı performansı etkiler mi?

Merak ediyorum, Javascript'te adlandırılmış fonksiyonları ve adsız fonksiyonları kullanma arasında bir performans farkı var mı? 

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = function() {
        // do something
    };
}

vs

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

Bunlardan ilki, kodunuzu nadiren kullanılan işlevlerle karıştırmaz, çünkü bu işlevi birden çok kez yeniden bildirmeniz fark eder mi?

80
nickf

Buradaki performans sorunu, adsız bir işlev kullandığınız gerçeğinden değil, döngünün her yinelemesinde yeni bir işlev nesnesi yaratmanın maliyetidir:

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

Aynı kod gövdesine sahip olsalar ve sözcüksel kapsamla ( kapatma ) bağlayıcı olmasalar bile bin ayrı işlev nesnesi yaratıyorsunuz. Öte yandan, aşağıdakiler daha hızlı görünmektedir, çünkü döngü boyunca dizi elemanlarına same işlevi referansını atamaktadır:

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

Eğer döngüye girmeden önce anonim bir fonksiyon oluşturursanız, o zaman sadece döngü içindeki dizi elemanlarına referanslar atadıysanız, adlandırılmış fonksiyon versiyonuyla karşılaştırıldığında hiçbir performans veya anlamsal fark olmadığını göreceksiniz:

var handler = function() {
    // do something    
};
for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = handler;
}

Kısacası, adlandırılmış işlevler üzerinde anonim kullanmanın gözlemlenebilir bir performans maliyeti yoktur.

Bir kenara bakıldığında, yukarıdan, aşağıdakiler arasında bir fark olmadığı görünebilir:

function myEventHandler() { /* ... */ }

ve:

var myEventHandler = function() { /* ... */ }

İlki, bir işlev bildirimidir, ikincisi ise isimsiz bir işleve değişken atamasıdır. Aynı etkiye sahip gibi görünseler de, JavaScript onlara biraz farklı davranır. Farkı anlamak için, “ JavaScript işlevi bildirim belirsizliği ” okumasını tavsiye ederim.

Herhangi bir yaklaşım için gerçek yürütme süresi, büyük ölçüde tarayıcının derleyici ve çalışma zamanını uygulaması tarafından belirlenir. Modern tarayıcı performansının tam karşılaştırması için JS Perf sitesini

81
Atif Aziz

İşte test kodum:

var dummyVar;
function test1() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = myFunc;
    }
}

function test2() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = function() {
            var x = 0;
            x++;
        };
    }
}

function myFunc() {
    var x = 0;
    x++;
}

document.onclick = function() {
    var start = new Date();
    test1();
    var mid = new Date();
    test2();
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid));
}

Sonuçlar:
Test 1: 142ms Test 2: 1983ms

JS motorunun Test2'deki aynı fonksiyon olduğunu tanımadığı ve her seferinde derlediği anlaşılıyor.

21
nickf

Genel bir tasarım prensibi olarak, aynı kodu birden çok kez uygulamaktan kaçınmalısınız. Bunun yerine, ortak kodu bir işleve kaldırmalı ve bu (genel, iyi test edilmiş, değiştirmesi kolay) işlevini birden fazla yerden çalıştırmalısınız.

(Sorunuzdan ne çıkardığınızın aksine) bir kez dahili işlevi ilan ediyorsanız ve bu kodu bir kez kullanıyorsanız (ve programınızda aynı olan başka bir şey yoksa), o zaman bir anonim işlev muhtemelen (bir tahmin millet) işlem görür derleyici tarafından aynı şekilde normal bir isimlendirilmiş fonksiyon olarak.

Belirli durumlarda çok kullanışlı bir özelliktir, ancak birçok durumda kullanılmamalıdır.

2
Tom Leys

Performans etkisine sahip olabileceğimiz yerler ilan işlevlerinin işleyişidir. Başka bir fonksiyonun içeriğinde veya dışarıda fonksiyonlar bildirmenin bir ölçütü:

http://jsperf.com/function-context-benchmark

Chrome'da işlevi dışarda bildirirsek işlem daha hızlı olur, ancak Firefox'ta bunun tersi olur.

Diğer örnekte, eğer iç işlev saf bir işlev değilse, Firefox'ta da performans eksikliğine sahip olacağını görürüz: http://jsperf.com/function-context-benchmark-3

1
Pablo Estornut

Çok fazla fark beklemem ama bir tane varsa, komut dosyası altyapısına veya tarayıcıya göre değişebilir. 

Kodu grokun daha kolay bulursanız, işlevi milyonlarca kez çağırmayı beklemiyorsanız, performans sorun değil.

1
Joe Skora

Anonim nesneler, adlandırılmış nesnelerden daha hızlıdır. Ancak, daha fazla fonksiyon çağırmak daha pahalıdır ve isimsiz fonksiyonları kullanmaktan elde edeceğiniz tasarrufları azaltan bir dereceye kadardır. Çağrılan her işlev, küçük ancak önemsiz olmayan bir ek yükü sağlayan çağrı yığınına eklenir.

Ancak, şifreleme/şifre çözme yordamları veya performansa benzer bir duyarlılık yazmıyorsanız, diğerlerinin de belirttiği gibi, hızlı kod üzerinden zarif, okunması kolay kod için optimize etmek her zaman daha iyidir.

İyi hazırlanmış bir kod yazdığınızı varsayarsak, hız meseleleri tercüman/derleyici yazanların sorumluluğunda olmalıdır.

1
pcorcoran

@nickf

Yine de, oldukça yorucu bir testtir, burada yöntem 1'e (açıkça N kere derlenir, JS motoruna bağlı olarak) yöntem 2'ye (yani bir kez derlenir) gideceği uygulama ve derleme zamanı karşılaştırırsınız. Deneme yazma kodlarını bu şekilde ileten bir JS geliştiricisi düşünemiyorum.

Aslında çok daha gerçekçi bir yaklaşım, anonim atamadır, gerçekte document.onclick yöntemi için kullandığınızdan, aslında anon yöntemini hafifçe tercih eden aşağıdaki gibidir.

Sizinkine benzer bir test çerçevesi kullanmak:


function test(m)
{
    for (var i = 0; i < 1000000; ++i) 
    {
        m();
    }
}

function named() {var x = 0; x++;}

var test1 = named;

var test2 = function() {var x = 0; x++;}

document.onclick = function() {
    var start = new Date();
    test(test1);
    var mid = new Date();
    test(test2);
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms");
}
0
annakata

@nickf

(Keşke sadece yorum yapacak bir temsilcim olsaydı, ama sadece bu siteyi buldum)

Demek istediğim burada isimlendirilmiş/isimsiz fonksiyonlar ile yineleme içinde çalıştırma + derleme kullanımı arasında bir karışıklık olduğu. Gösterdiğim gibi, anon + ile adlandırılan arasındaki fark kendiliğinden önemsizdir - Bence hatalı olan bu kullanım durumu.

Bana göre bariz gözüküyor, ancak en iyi tavsiyenin "aptalca şeyler yapma" olduğunu düşünmüyorsam (ki bu değişmeyen blok + bu kullanım durumunun nesne yaratması birdir) ve emin değilseniz, test edin!

0
annakata

EVET! Anonim işlevler normal işlevlerden daha hızlıdır. Belki hız çok önemlidir ... kodun tekrar kullanılmasından daha önemliyse adsız işlevler kullanmayı düşünün.

Javascript ve anonim işlevleri burada optimize etmekle ilgili gerçekten güzel bir makale var:

http://dev.opera.com/articles/view/efficient-javascript/?page=2

0

bir referans, atıfta bulunduğu şeyden hemen hemen her zaman daha yavaş olacaktır. Bunu bu şekilde düşünün - diyelim ki, 1 + 1 eklemenin sonucunu yazdırmak istediğinizi varsayalım.

alert(1 + 1);

veya

a = 1;
b = 1;
alert(a + b);

Buna bakmanın çok basit bir yol olduğunun farkındayım, ama açıklayıcı değil mi? Bir referansı yalnızca birden fazla kez kullanacaksanız kullanın; örneğin, bu örneklerden hangisinin daha anlamlı olduğu:

$(a.button1).click(function(){alert('you clicked ' + this);});
$(a.button2).click(function(){alert('you clicked ' + this);});

veya

function buttonClickHandler(){alert('you clicked ' + this);}
$(a.button1).click(buttonClickHandler);
$(a.button2).click(buttonClickHandler);

İkincisi, daha fazla çizgi olsa bile daha iyi bir uygulamadır. Umarım bütün bunlar yardımcı olur. (ve jquery sözdizimi kimseyi atmadı)

0
matt lohkamp

@Nickf cevaplarına yapılan yorumlarda belirtildiği gibi: 

Bir işlevin milyon kez yaratılmasından bir kez daha hızlı bir işlev yaratıyor

sadece evet. Ancak JS perf'in gösterdiği gibi, bir milyon faktöre göre yavaşlamadığından zamanla gerçekte daha hızlı olduğunu gösteriyor.

Bana daha ilginç bir soru:

Tekrarlanan create + run, bir kere + tekrarlanan run ile yaratmak için nasıl bir karşılaştırma yapar.

Bir işlev karmaşık bir hesaplama yaparsa, işlev nesnesini oluşturma süresi büyük olasılıkla ihmal edilebilir düzeydedir. Ancak, run 'in hızlı olduğu durumlarda create' nin başı nedir? Örneğin:

// Variant 1: create once
function adder(a, b) {
  return a + b;
}
for (var i = 0; i < 100000; ++i) {
  var x = adder(412, 123);
}

// Variant 2: repeated creation via function statement
for (var i = 0; i < 100000; ++i) {
  function adder(a, b) {
    return a + b;
  }
  var x = adder(412, 123);
}

// Variant 3: repeated creation via function expression
for (var i = 0; i < 100000; ++i) {
  var x = (function(a, b) { return a + b; })(412, 123);
}

Bu JS Perf işlevi sadece bir kez yaratmanın beklendiği gibi hızlı olduğunu gösterir. Bununla birlikte, basit bir eklenti gibi çok hızlı bir işlem yapılsa bile, işlevi tekrar tekrar oluşturma yükü yalnızca yüzde birkaçdır.

Aradaki fark muhtemelen sadece işlev nesnesinin yaratılmasının karmaşık olduğu durumlarda, örneğin, eğer bütün işlev gövdesi if (unlikelyCondition) { ... } içine sarılırsa, ihmal edilebilir bir çalışma süresini korurken önemli hale gelir.

0
bluenote10

Döngününüzü çeşitli tarayıcılarda, özellikle IE tarayıcılarında daha hızlı bir şekilde kesinlikle daha hızlı hale getirecek olan şey şu döngüdür:

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

Döngü koşulu içine rastgele bir 1000 koydunuz, ancak dizideki tüm öğelerin içinden geçmek istiyorsanız sürüklenmemi elde edersiniz.

0
Sarhanis