2010-02-14 46 views
30

Bất kỳ ai biết bất kỳ sự khác biệt về tốc độ nào giữa Địa điểm và Tìm kiếm trên danh sách. Tôi biết đâu là một phần của IEnumerable và FindAll là một phần của Danh sách, tôi chỉ tò mò những gì nhanh hơn.C# FindAll VS Tốc độ ở đâu

+0

bản sao có thể có của [FindAll vs Where extension-method] (http://stackoverflow.com/questions/1531702/findall-vs-where-extension-method) –

Trả lời

45

Phương thức FindAll của lớp <T> thực sự tạo đối tượng danh sách mới và thêm kết quả vào đối tượng đó. Phương pháp mở rộng ở đâu cho IEnumerable <T> sẽ đơn giản lặp qua danh sách hiện có và đưa ra một liệt kê các kết quả phù hợp mà không tạo hoặc thêm bất kỳ thứ gì (ngoài chính điều tra viên.)

Cho một bộ nhỏ, hai khả năng thực hiện tương đương. Tuy nhiên, với một tập lớn hơn, Trường hợp sẽ hoạt động tốt hơn FindAll, vì Danh sách mới được tạo để chứa kết quả sẽ phải phát triển động để chứa các kết quả bổ sung. Việc sử dụng bộ nhớ FindAll cũng sẽ bắt đầu tăng theo cấp số nhân khi số lượng kết quả phù hợp tăng lên, ở đâu khi có mức sử dụng bộ nhớ tối thiểu liên tục (trong và của chính nó ... loại trừ bất cứ điều gì bạn làm với kết quả.)

+19

Trường hợp ngoại lệ là nơi bạn thực sự muốn có danh sách sau đó (có thể bạn cần phải gọi 'Đếm' hoặc thay đổi thành viên, hoặc lặp lại qua nhiều lần). Trong khi 'Where()' đánh bại 'FindAll()', 'FindAll()' beats 'Where(). ToList()'. –

+5

@JonHanna: Lúc đầu, tôi nghĩ rằng tôi đồng ý, tôi thực sự không chắc chắn. Bạn có bất kỳ tài liệu tham khảo chỉ ra một .ToList() là chậm hơn một .indAll()? Gọi .ToList() trên một truy vấn sẽ ** được ** lặp đi lặp lại của enumerable, và do đó nên duy trì hiệu quả bộ nhớ của nó. Không chỉ vậy, một số triển khai nội bộ nhất định của nơi lặp có thể tạo danh sách chính xác kích thước (cấp phát bộ nhớ) lên phía trước, vượt trội hơn FindAll trong các trường hợp như vậy. Tôi không đặc biệt không đồng ý, tuy nhiên nó sẽ là tốt đẹp để có một tài liệu tham khảo vững chắc làm rõ lợi ích FindAlls. – jrista

+1

Câu trả lời này đã sai. Xem @Wiory, người đã thực sự quan tâm đến việc đo lường. –

3

.FindAll() nên được nhanh hơn, nó tận dụng lợi thế của đã biết kích thước của danh sách và looping thông qua mảng nội bộ với một vòng lặp đơn giản for. .Where() phải kích hoạt một điều tra viên (một lớp khung kín được gọi là WhereIterator trong trường hợp này) và làm cùng một công việc theo một cách ít cụ thể hơn.

Hãy nhớ rằng, .Where() được liệt kê, không chủ động tạo Danh sách trong bộ nhớ và điền vào. Nó giống như một dòng, do đó, việc sử dụng bộ nhớ trên một cái gì đó rất lớn có thể có một sự khác biệt đáng kể. Ngoài ra, bạn có thể bắt đầu sử dụng kết quả theo kiểu song song nhanh hơn nhiều bằng cách sử dụng ở đó .Where() cách tiếp cận trong 4.0.

+1

Bộ chuyển đổi vị trí số, thay vì bộ biến tần, được sử dụng thực sự trừ khi bạn liên quan đến chỉ mục trong mệnh đề where. WhereEnumerableIterator là hiệu quả hơn đáng kể so với WhereIterator. Trong trường hợp của Danh sách , nó chịu chi phí của một cuộc gọi phương thức bổ sung (cần được nêu trong mã phát hành), nhưng không cần phải tự động thay đổi kích thước danh sách nội bộ như một phần của quá trình xử lý. Hiệu quả của trường hợp sẽ vượt trội hơn FindAll trong tất cả các danh sách nhỏ nhất (bất kỳ thứ gì lớn hơn 4 kết quả sẽ dẫn đến một hoặc nhiều lần tái tạo.) – jrista

+0

Trong trường hợp gọi trên một Array hoặc List , có hai lớp lặp nội bộ bổ sung, WhereArrayIterator và WhereListIterator, được tối ưu hóa cho hai trường hợp đó. Nói chung, gọi điện thoại ở đâu sẽ hiệu quả hơn gọi FindAll. – jrista

+2

@jrista - Tôi ** hoàn toàn ** bỏ lỡ ngăn xếp trường hợp trong '.Trường hợp() 'quá tải trở lại những người, cảm ơn! Nhìn qua đoạn mã đó tôi đồng ý, .Tại sao nên có, ở mức tồi tệ nhất, hiệu suất ngang nhau nhưng hầu như luôn luôn tốt hơn. Ngoài ra, SO sẽ vô ích nếu không dành cho những người dành thêm thời gian để giáo dục người khác, ví dụ: bạn và những nhận xét này, +1 để dạy tôi điều gì đó. –

4

Where là nhiều, nhanh hơn nhiều so với FindAll. Bất kể danh sách lớn đến mức nào, Where sẽ mất khoảng thời gian chính xác.

Tất nhiên Where chỉ cần tạo truy vấn. Nó không thực sự làm bất cứ điều gì, không giống như FindAll mà không tạo ra một danh sách.

+5

Điều này có thể đúng về mặt kỹ thuật, nhưng tôi nghĩ rõ ràng là OP hỏi về hiệu suất trong ngữ cảnh thực sự liệt kê kết quả, chứ không phải là phương thức khỏa thân gọi chính nó. –

-3

Câu trả lời từ jrista làm cho các giác quan. Tuy nhiên, danh sách mới thêm các đối tượng tương tự, do đó chỉ tăng trưởng với tham chiếu đến các đối tượng hiện có, không nên chậm. Miễn là 3.5/LINQ mở rộng có thể, ở đâu tốt hơn anyway. FindAll có ý nghĩa hơn nhiều khi bị giới hạn với 2.0

6

FindAll rõ ràng là chậm hơn ở đâu, bởi vì nó cần tạo danh sách mới.

Dù sao, tôi nghĩ bạn thực sự nên xem xét Jon Hanna bình luận - có thể bạn sẽ cần phải làm một số hoạt động trên kết quả của bạn và danh sách sẽ hữu ích hơn IEnumerable trong nhiều trường hợp.

Tôi đã viết thử nghiệm nhỏ, chỉ cần dán nó vào dự án Ứng dụng giao diện điều khiển. Nó đo thời gian/ticks của: thực hiện chức năng, hoạt động trên bộ sưu tập kết quả (để có được perf. Của sử dụng 'thực', và để chắc chắn rằng trình biên dịch sẽ không tối ưu hóa dữ liệu không sử dụng vv - Tôi mới đến C# và không biết làm thế nào nó hoạt động được nêu ra, xin lỗi).

Lưu ý: mọi hàm được đo trừ Trường hợpIENumerable() tạo Danh sách các phần tử mới. Tôi có thể làm một cái gì đó sai, nhưng rõ ràng lặp IEnumerable mất nhiều thời gian hơn so với danh sách lặp.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Diagnostics; 

namespace Tests 
{ 

    public class Dummy 
    { 
     public int Val; 
     public Dummy(int val) 
     { 
      Val = val; 
     } 
    } 
    public class WhereOrFindAll 
    { 
     const int ElCount = 20000000; 
     const int FilterVal =1000; 
     const int MaxVal = 2000; 
     const bool CheckSum = true; // Checks sum of elements in list of resutls 
     static List<Dummy> list = new List<Dummy>(); 
     public delegate void FuncToTest(); 

     public static long TestTicks(FuncToTest function, string msg) 
     { 
      Stopwatch watch = new Stopwatch(); 
      watch.Start(); 
      function(); 
      watch.Stop(); 
      Console.Write("\r\n"+msg + "\t ticks: " + (watch.ElapsedTicks)); 
      return watch.ElapsedTicks; 
     } 
     static void Check(List<Dummy> list) 
     { 
      if (!CheckSum) return; 
      Stopwatch watch = new Stopwatch(); 
      watch.Start(); 

      long res=0; 
      int count = list.Count; 
      for (int i = 0; i < count; i++)  res += list[i].Val; 
      for (int i = 0; i < count; i++)  res -= (long)(list[i].Val * 0.3); 

      watch.Stop(); 
      Console.Write("\r\n\nCheck sum: " + res.ToString() + "\t iteration ticks: " + watch.ElapsedTicks); 
     } 
     static void Check(IEnumerable<Dummy> ieNumerable) 
     { 
      if (!CheckSum) return; 
      Stopwatch watch = new Stopwatch(); 
      watch.Start(); 

      IEnumerator<Dummy> ieNumerator = ieNumerable.GetEnumerator(); 
      long res = 0; 
      while (ieNumerator.MoveNext()) res += ieNumerator.Current.Val; 
      ieNumerator=ieNumerable.GetEnumerator(); 
      while (ieNumerator.MoveNext()) res -= (long)(ieNumerator.Current.Val * 0.3); 

      watch.Stop(); 
      Console.Write("\r\n\nCheck sum: " + res.ToString() + "\t iteration ticks :" + watch.ElapsedTicks); 
     } 
     static void Generate() 
     { 
      if (list.Count > 0) 
       return; 
      var rand = new Random(); 
      for (int i = 0; i < ElCount; i++) 
       list.Add(new Dummy(rand.Next(MaxVal))); 

     } 
     static void For() 
     { 
      List<Dummy> resList = new List<Dummy>(); 
      int count = list.Count; 
      for (int i = 0; i < count; i++) 
      { 
       if (list[i].Val < FilterVal) 
        resList.Add(list[i]); 
      }  
      Check(resList); 
     } 
     static void Foreach() 
     { 
      List<Dummy> resList = new List<Dummy>(); 
      int count = list.Count; 
      foreach (Dummy dummy in list) 
      { 
       if (dummy.Val < FilterVal) 
        resList.Add(dummy); 
      } 
      Check(resList); 
     } 
     static void WhereToList() 
     { 
      List<Dummy> resList = list.Where(x => x.Val < FilterVal).ToList<Dummy>(); 
      Check(resList); 
     } 
     static void WhereIEnumerable() 
     { 
      Stopwatch watch = new Stopwatch(); 
      IEnumerable<Dummy> iEnumerable = list.Where(x => x.Val < FilterVal); 
      Check(iEnumerable); 
     } 
     static void FindAll() 
     { 
      List<Dummy> resList = list.FindAll(x => x.Val < FilterVal); 
      Check(resList); 
     } 
     public static void Run() 
     { 
      Generate(); 
      long[] ticks = { 0, 0, 0, 0, 0 }; 
      for (int i = 0; i < 10; i++) 
      { 
       ticks[0] += TestTicks(For, "For \t\t"); 
       ticks[1] += TestTicks(Foreach, "Foreach \t"); 
       ticks[2] += TestTicks(WhereToList, "Where to list \t"); 
       ticks[3] += TestTicks(WhereIEnumerable, "Where Ienum \t"); 
       ticks[4] += TestTicks(FindAll, "FindAll \t"); 
       Console.Write("\r\n---------------"); 
      } 
      for (int i = 0; i < 5; i++) 
       Console.Write("\r\n"+ticks[i].ToString()); 
     } 

    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      WhereOrFindAll.Run(); 
      Console.Read(); 
     } 
    } 
} 

Kết quả (ticks) - Checksum kích hoạt (một số hoạt động trên kết quả), phương thức: phát hành mà không cần gỡ lỗi (Ctrl + F5):

  • 16222276 (cho -> list)
  • 17151121 (foreach -> danh sách)
  • 4741494 (nơi -> danh sách)
  • 27122285 (nơi -> ienum)
  • 18 821.571 (findall -> danh sách)

checksum vô hiệu hóa (không sử dụng trở lại danh sách ở tất cả):

  • 10885004 (cho -> danh sách)
  • 11221888 (foreach -> list)
  • 18688433 (nơi -> danh sách)
  • 1075 (nơi -> ienum)
  • 13720243 (findall -> list)

Kết quả của bạn có thể khác đôi chút, để có được kết quả thực, bạn cần lặp lại nhiều hơn.

+0

kiểm tra của bạn là tốt. Chúng cho thấy cơ chế LINQ chậm hơn so với hoạt động trực tiếp trên danh sách. Không ngạc nhiên. "1075 (where -> ienum)" của bạn sai ở chỗ sử dụng một nơi mà không đi qua các yếu tố kết quả sẽ không bao giờ thực sự thực hiện một nơi! –

+1

Xin lỗi Carlo, nhưng anh ta gọi phương thức "Check()" của anh ta ngay cả ở nơi thực hiện. Kiểm tra() lặp lại tất cả các bộ sưu tập, vì vậy kết quả của mình là hoàn toàn hợp lệ. Kết quả là, điều đó cũng làm cho câu trả lời của tôi đúng ... câu trả lời mà bạn gọi là "sai lầm". – jrista

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