C # FindAll VS где скорость - PullRequest
       9

C # FindAll VS где скорость

33 голосов
/ 14 февраля 2010

Любой знает разницу в скорости между Where и FindAll в Списке. Я знаю, где находится часть IEnumerable, а FindAll - часть List, мне просто любопытно, что быстрее.

Ответы [ 5 ]

49 голосов
/ 14 февраля 2010

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

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

6 голосов
/ 23 июня 2011

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

В любом случае, я думаю, что вы действительно должны рассмотреть комментарий Джона Ханны - вам, вероятно, нужно будет выполнить некоторые операции со своими результатами, и список во многих случаях будет более полезным, чем 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 (foreach -> список)
  • 4741494 (где -> список)
  • 27122285 (где -> иенум)
  • 18821571 (поиск -> список)

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

  • 10885004 (для -> список)
  • 11221888 (foreach -> список)
  • 18688433 (где -> список)
  • 1075 (где -> иенум)
  • 13720243 (поиск -> список)

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

4 голосов
/ 14 февраля 2010

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

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

2 голосов
/ 14 февраля 2010

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

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

0 голосов
/ 15 июля 2010

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

...