2012-06-14 14 views
23

IEnumerable < int> source toplamını bulmak için üç farklı uygulama, kaynak 10,000 tamsayı olduğunda alınan zamanla birlikte aşağıda verilmiştir.Aggregate ve Sum Performance in LINQ

source.Aggregate(0, (result, element) => result + element); 

3 ms

source.Sum(c => c); 

alır 12 ms

ikinci uygulama ilkinden dört kat daha pahalı olmasının nedeni
source.Sum(); 

merak ediyorum 1 ms

alır götürür . Üçüncü uygulama ile aynı olmamalı.

+0

Test koşullarınız nelerdir? –

+5

Bu saatleri nasıl aldın? Sonuçları kaç kez denediniz? –

+0

DotTrace kullanarak profil oluşturdum. Bir kez koştum, ama üç koşu bağımsız. – Gopal

cevap

68

Not: Bilgisayarım çalışıyor. Net 4.5 RC, bu yüzden sonuçların bundan etkilenmesi olası.

Sadece bir kez bir yöntemi yürütmek için gereken sürenin ölçülmesi genellikle çok kullanışlı değildir. Gerçek kodda gerçek darboğazlar olmayan JIT derleme gibi şeylerin kolayca hakim olabilir. Bu nedenle, her bir yöntemi 100 × (Debugger takılı olmadan Serbest Bırakma modunda) çalıştırmayı ölçtüm. Benim sonuçları şunlardır:

  • Aggregate(): 9 ms
  • Sum(lambda): 12 ms
  • Sum(): 6 ms

Sum() hızlı olması şaşırtıcı değildir: bu basit bir döngü içeriyor Gerçekten hızlı olan herhangi bir delege çağırma olmadan. Sum(lambda) ve Aggregate() arasındaki fark, ölçtüğünüz kadar belirgin değildir, ancak hala oradadır. Bunun nedeni ne olabilir? iki yöntemleri için Decompiled koduna bakalım:

public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func) 
{ 
    if (source == null) 
     throw Error.ArgumentNull("source"); 
    if (func == null) 
     throw Error.ArgumentNull("func"); 

    TAccumulate local = seed; 
    foreach (TSource local2 in source) 
     local = func(local, local2); 
    return local; 
} 

public static int Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector) 
{ 
    return source.Select<TSource, int>(selector).Sum(); 
} 

Gördüğünüz gibi, Aggregate() bir döngü kullanır ama Sum(lambda) sırayla bir yineleme kullanır Select() kullanır. Bir yineleyicinin kullanılması, bazı ek yüklerin var olduğu anlamına gelir: yineleyici nesnesini oluşturmak ve (muhtemelen daha da önemlisi) her bir öğe için bir başka yöntem çağrısı.

kez Select() kullanmadan en Select() kullanarak nedeni kere, iki kere Sum(lambda) kendi yazma çerçevesinden Sum(lambda) aynı davranır ve gereken Select() kullanarak aslında olduğunu doğrulamak edelim:

public static int SlowSum<T>(this IEnumerable<T> source, Func<T, int> selector) 
{ 
    return source.Select(selector).Sum(); 
} 

public static int FastSum<T>(this IEnumerable<T> source, Func<T, int> selector) 
{ 
    if (source == null) 
     throw new ArgumentNullException("source"); 
    if (selector == null) 
     throw new ArgumentNullException("selector"); 

    int num = 0; 
    foreach (T item in source) 
     num += selector(item); 
    return num; 
} 

Benim ölçümleri ne düşündüğümü onaylamak:

  • SlowSum(lambda): 12 ms
  • FastSum(lambda): 9 ms
+0

Çok anlayışlı. Detaylı cevap için teşekkürler. – Gopal

+0

Bu, SO üzerinde gördüğüm en iyi cevaplardan biri, mükemmel açıklanmış ve iyi hazırlanmış. (Açıkçası +1) – RichK