2010-02-14 7 views
30

Кто-нибудь знает разницу в скорости между Where и FindAll on List. Я знаю, где часть IEnumerable и FindAll входит в список, мне просто интересно, что быстрее.C# FindAll VS Where Speed ​​

+0

Возможный дубликат [FindAll vs Where extension-method] (http://stackoverflow.com/questions/1531702/findall-vs-where-extension-method) –

ответ

45

Метод FindAll для списка <T> класс фактически создает новый объект списка и добавляет к нему результаты. Метод Где расширений для IEnumerable <T> просто перебрать существующий список и дает перечисление результатов согласования без создания или добавлений чего-либо (кроме самого переписчика.)

Учитывая небольшой набор, два, скорее всего, выполняются сравнительно. Тем не менее, учитывая больший набор, Where's outperform FindAll, поскольку новый список, созданный для того, чтобы содержать результаты, должен динамически расти, чтобы содержать дополнительные результаты. Использование памяти FindAll также начнет экспоненциально возрастать по мере увеличения количества совпадающих результатов, так как Where должно иметь постоянное минимальное использование памяти (само по себе ... исключая все, что вы делаете с результатами.)

+19

Исключением является то, что вы действительно хотите иметь список (возможно, вам нужно вызвать «Count» или изменить участников или повторить его несколько раз). В то время как 'Where()' beats 'FindAll()', 'FindAll()' превосходит 'Where(). ToList()'. –

+5

@JonHanna: Хотя первоначально я думал, что согласен, я на самом деле не уверен. У вас есть ссылки, указывающие на .ToList() медленнее, чем .FindAll()? Вызов .ToList() в запросе ** будет ** итерацией перечислимого, и для этого должен поддерживать эффективность памяти. Не только это, некоторые внутренние реализации того, где итератор может даже иметь возможность создать список точно нужного размера (распределение памяти) спереди, превосходящий FindAll в таких случаях. Я не особо не согласен, однако было бы неплохо иметь прочную ссылку, которая разъясняет пользу FindAlls. – jrista

+1

Этот ответ неправ. См. @Wiory, который потрудился на самом деле измерения. –

3

.FindAll() следует быть быстрее, он использует преимущества уже зная размер List и цикл через внутренний массив с простым циклом for. .Where() должен активировать перечислитель (в этом случае запертый класс каркаса WhereIterator) и выполнять ту же работу менее определенным образом.

Имейте ввиду, что .Where() перечислимо, не активно создавая список в памяти и заполняя его. Это больше похоже на поток, поэтому использование памяти на чем-то очень большом может иметь существенную разницу. Кроме того, вы можете начать использовать результаты в параллельном режиме намного быстрее, используя там .Where() подход в 4.0.

+1

Идентификатор WhereEnumerableIterator, а не WhereIterator, фактически используется, если вы не включили индекс в предложение where. WhereEnumerableIterator значительно эффективнее, чем WhereIterator. В случае списка , он несет стоимость вызова дополнительного метода (который должен быть встроен в код выпуска), но не требует динамического изменения внутреннего списка как части его обработки. Эффективность Where должна превосходить FindAll во всех, кроме самых маленьких списках (что-то большее, чем 4 результата, приведет к одному или нескольким изменениям.) – jrista

+0

В случае вызова Where in Array или List существуют два дополнительных внутренних класса итератора, WhereArrayIterator и WhereListIterator, которые оптимизированы для этих двух случаев. Вообще говоря, вызов Where должен быть более эффективным, чем вызов FindAll. – jrista

+2

@jrista - I ** полностью ** пропустил стек дела в '.Где() 'перегрузка, возвращающая их, спасибо! Просматривая этот код, я согласен, что в худшем случае будет равная производительность, но почти всегда лучше. Кроме того, SO будет бесполезным, если не для людей, занимающих дополнительное время для обучения других, например. вы и эти комментарии, +1 за то, что научили меня чему-то. –

4

Where намного, намного быстрее, чем FindAll. Независимо от того, насколько велик список, Where занимает ровно столько же времени.

Конечно, Where просто создает запрос. На самом деле он ничего не делает, в отличие от FindAll, который создает список.

+5

Это может быть технически справедливо, но я думаю, что довольно ясно, что OP задает вопрос о производительности в контексте фактического перечисления результата, а не для самого голого метода. –

-3

Ответ от jrista делает чувства. Тем не менее, новый список добавляет одни и те же объекты, таким образом, только растет со ссылкой на существующие объекты, которые не должны быть такими медленными. До тех пор, пока возможно расширение 3.5/Linq, где все равно будет лучше. FindAll имеет гораздо больше смысла при ограничении 2.0

6

FindAll явно медленнее, чем Где, поскольку ему нужно создать новый список.

В любом случае, я думаю, вы действительно должны рассмотреть комментарий Джона Ханны - вам, вероятно, потребуется сделать некоторые операции над вашими результатами, и список во многих случаях будет более полезен, чем IEnumerable.

Я написал небольшой тест, просто вставьте его в проект приложения консоли. Он измеряет время/тики: выполнение функции, операции по сбору результатов (чтобы получить перформанс «реального» использования и быть уверенным, что компилятор не будет оптимизировать неиспользуемые данные и т. Д. - я новичок в C# и не знаю, как это работает, извините).

Примечание: каждая измеренная функция, кроме WhereIENumerable(), создает новый список элементов. Возможно, я что-то делаю неправильно, но явное повторение IEnumerable занимает гораздо больше времени, чем итерирующий список.

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

Результаты (клещи) - CheckSum включен (некоторые операции по результатам), режим: релиз без отладки (CTRL + F5):

  • 16222276 (для -> список)
  • 17151121 (Еогеасп -> список)
  • 4741494 (где - список>)
  • 27122285 (где -> ienum)
  • 18 821571 (FindAll -> список)

CheckSum отключена (не используется возвращаемый список на всех):

  • 10885004 (для -> список)
  • 11221888 (Еогеасп -> список)
  • 18688433 (где -> список)
  • 1075 (где -> ienum)
  • 13720243 (FindAll -> список)

Ваши результаты могут быть немного разными, чтобы получить реальные результаты, вам нужно больше итераций.

+0

ваши тесты в порядке. Они показывают, что механизм LINQ работает медленнее, чем работает непосредственно в списке. Не удивительно. Ваш «1075 (где -> ienum)» ошибочен в том, что использование где без прохождения результирующих элементов никогда не будет выполняться где-нибудь! –

+1

Извините Карло, но он называет свой метод «Проверить()» даже в том, где -> ienum. Check() выполняет итерацию всех коллекций, поэтому его результаты полностью действительны. Как следствие, это также делает мой ответ правильным ... ответ, который вы назвали «мертвым неправильно». – jrista