2013-04-24 7 views
27

Ein question früher geschrieben hat mich denken. Würden Any() und Count() ähnlich funktionieren, wenn sie auf einer leeren Liste verwendet werden?C#: Beliebige() vs Count() für eine leere Liste

Wie erklärt here, sollten beide die gleichen Schritte von GetEnumerator()/MoveNext()/Dispose() gehen.

Getestet habe ich diese auf LINQPad schnelles Programm aus:

static void Main() 
{ 
    var list = new List<int>(); 

    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start(); 

    for (int i = 0; i < 10000; i++) 
     list.Any(); 

    stopwatch.Stop(); 
    Console.WriteLine("Time elapsed for Any() : {0}", stopwatch.Elapsed); 


    stopwatch = new Stopwatch(); 
    stopwatch.Start(); 

    for (int i = 0; i < 10000; i++) 
     list.Count(); 

    stopwatch.Stop(); 
    Console.WriteLine("Time elapsed for Count(): {0}", stopwatch.Elapsed); 
} 

Und das allgemeine Ergebnis scheint darauf hinzudeuten, dass Count() schneller in dieser Situation ist. Warum das?

Ich bin mir nicht sicher, ob ich den Benchmark richtig gemacht habe, ich würde jede Korrektur schätzen, wenn nicht.


Edit: Ich verstehe, dass es semantisch mehr Sinn machen würde. Der erste Link, den ich in der Frage gepostet habe, zeigt eine Situation, in der es sinnvoll ist, Count() direkt zu verwenden, da der Wert verwendet würde, daher die Frage.

+0

würden sie beide sehr schnell, aber, wenn ein 'list' nur die' Count' Eigenschaft zu testen, anstatt die 'Count()' Erweiterung, die keine Aufzählung erfordert. – Jodrell

+7

Was genau haben Ihre Benchmarks gezeigt? Ich würde erwarten, dass es nur so schnell sein würde, wenn man nur 10000 Mal anruft, um nicht vernünftig messbar zu sein. –

+3

Der Grund "Any" ist im Allgemeinen besser, weil es nur eine Sache in der Aufzählung finden muss, aber die Zählung muss alle von ihnen finden. In Ihrem Test ist die Liste leer, also finden Sie offensichtlich die erste, und das Finden aller macht keinen großen Unterschied –

Antwort

22

Die Methode Count() ist für den Typ ICollection<T> optimiert, daher wird das Muster GetEnumerator()/MoveNext()/Dispose() nicht verwendet.

list.Count(); 

Ist übersetzt

((ICollection)list).Count; 

Während die Any() einen Enumerator zu bauen hat. So ist die Count() Methode schneller.

Hier ein Benchmark für 4 verschiedene IEnumerable Instanz. Die MyEmpty sieht aus wie IEnumerable<T> MyEmpty<T>() { yield break; }

iterations : 100000000 

Function      Any()  Count() 
new List<int>()    4.310  2.252 
Enumerable.Empty<int>()  3.623  6.975 
new int[0]     3.960  7.036 
MyEmpty<int>()    5.631  7.194 

Wie casperOne im Kommentar sagte Enumerable.Empty<int>() is ICollection<int>, weil es ein Array ist, und Arrays ist nicht gut mit der Count() Erweiterung weil the cast to ICollection<int> is not trivial.

Wie auch immer, für ein hausgemachtes leer IEnumerable, Wir können sehen, was wir erwartet haben, dass Count() ist langsamer als Any(), aufgrund der Overhead der Prüfung, wenn die IEnumerable ist ein ICollection.

komplette Benchmark:

class Program 
{ 
    public const long Iterations = (long)1e8; 

    static void Main() 
    { 
     var results = new Dictionary<string, Tuple<TimeSpan, TimeSpan>>(); 
     results.Add("new List<int>()", Benchmark(new List<int>(), Iterations)); 
     results.Add("Enumerable.Empty<int>()", Benchmark(Enumerable.Empty<int>(), Iterations)); 
     results.Add("new int[0]", Benchmark(new int[0], Iterations)); 
     results.Add("MyEmpty<int>()", Benchmark(MyEmpty<int>(), Iterations)); 

     Console.WriteLine("Function".PadRight(30) + "Any()".PadRight(10) + "Count()"); 
     foreach (var result in results) 
     { 
      Console.WriteLine("{0}{1}{2}", result.Key.PadRight(30), Math.Round(result.Value.Item1.TotalSeconds, 3).ToString().PadRight(10), Math.Round(result.Value.Item2.TotalSeconds, 3)); 
     } 
     Console.ReadLine(); 
    } 

    public static Tuple<TimeSpan, TimeSpan> Benchmark(IEnumerable<int> source, long iterations) 
    { 
     var anyWatch = new Stopwatch(); 
     anyWatch.Start(); 
     for (long i = 0; i < iterations; i++) source.Any(); 
     anyWatch.Stop(); 

     var countWatch = new Stopwatch(); 
     countWatch.Start(); 
     for (long i = 0; i < iterations; i++) source.Count(); 
     countWatch.Stop(); 

     return new Tuple<TimeSpan, TimeSpan>(anyWatch.Elapsed, countWatch.Elapsed); 
    } 

    public static IEnumerable<T> MyEmpty<T>() { yield break; } 
} 
+0

Die Verwendung von 'Enumerable.Empty ()' zeigt, dass 'Any()' schneller ist als 'Count()'. Vielen Dank! –

+5

-1: Ihr Test ist falsch. 'Enumerable.Empty ()' gibt ein leeres Array zurück, und Arrays implementieren 'IList ', was 'ICollection ' erweitert. Sie brauchen eine Methode, die nichts als "Ausbeute brechen" bewirkt. Der Anruf ruft im Wesentlichen denselben Codepfad in Ihrem Test auf. – casperOne

Verwandte Themen