2013-04-24 43 views
27

A question được đăng trước đó đã cho tôi suy nghĩ. Any()Count() có hoạt động tương tự khi được sử dụng trong danh sách trống không?C#: Bất kỳ() vs Count() cho một danh sách trống

Như được giải thích here, cả hai phải đi qua các bước tương tự của GetEnumerator()/MoveNext()/Dispose().

Tôi thử nghiệm này bằng cách sử dụng chương trình nhanh chóng trên LINQPad:

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

Và kết quả chung dường như chỉ ra rằng Count() là nhanh hơn trong tình huống này. Tại sao vậy?

Tôi không chắc liệu mình có được điểm chuẩn hay không, tôi sẽ đánh giá cao bất kỳ sự điều chỉnh nào nếu không.


Chỉnh sửa: Tôi hiểu rằng điều đó có ý nghĩa hơn về mặt ngữ nghĩa. Liên kết đầu tiên tôi đã đăng trong câu hỏi cho thấy một tình huống mà nó có ý nghĩa để sử dụng trực tiếp Count() vì giá trị sẽ được sử dụng, do đó là câu hỏi.

+0

Cả hai đều sẽ rất nhanh nhưng, nếu thử nghiệm một 'List' chỉ cần gõ 'tài sản Count', chứ không phải là' Count()' mở rộng, mà không yêu cầu phải liệt kê. – Jodrell

+7

Điểm chuẩn của bạn hiển thị chính xác những gì? Tôi hy vọng rằng việc gọi một trong số này chỉ 10000 lần sẽ nhanh đến mức không thể đo lường được một cách hợp lý. –

+3

Lý do 'Bất kỳ' là tốt hơn thường là vì nó chỉ cần tìm một thứ trong liệt kê, nhưng đếm cần phải tìm tất cả chúng. Trong thử nghiệm của bạn danh sách là trống rỗng vì vậy rõ ràng là tìm kiếm đầu tiên, và việc tìm kiếm tất cả doesnt làm cho nhiều sự khác biệt –

Trả lời

22

Phương pháp Count() được tối ưu hóa cho loại ICollection<T>, do đó, mẫu GetEnumerator()/MoveNext()/Dispose() không được sử dụng.

list.Count(); 

được phiên dịch sang

((ICollection)list).Count; 

Trong khi Any() có để xây dựng một Enumerator. Vì vậy, phương pháp Count() nhanh hơn.

Dưới đây là điểm chuẩn cho 4 differents IEnumerable ví dụ. Các MyEmpty trông giống như 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 

Như casperOne nói trong bình luận, Enumerable.Empty<int>() is ICollection<int>, bởi vì nó là một mảng, và mảng không tốt với phần mở rộng Count()the cast to ICollection<int> is not trivial.

Dù sao, đối với một tự làm rỗng IEnumerable, chúng tôi có thể thấy những gì chúng tôi mong đợi, rằng Count() chậm hơn Any(), do chi phí kiểm tra nếu IEnumerableICollection.

Toàn bộ điểm chuẩn:

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

Sử dụng 'Enumerable.Empty ()' cho thấy rằng 'Any()' nhanh hơn 'Count()'. Cảm ơn! –

+5

-1: Bài kiểm tra của bạn không chính xác. 'Enumerable.Empty ()' trả về một mảng trống và các mảng thực thi 'IList ' mở rộng 'ICollection '. Bạn cần một phương thức không làm gì ngoài 'ngắt kết quả '. Các cuộc gọi về cơ bản sniffs ra cùng một đường dẫn mã trong thử nghiệm của bạn. – casperOne

Các vấn đề liên quan