Оператор LINQ быстрее, чем цикл foreach?

124

Я пишу диспетчер рендеринга сетки и подумал, что было бы неплохо сгруппировать все меши, которые используют один и тот же шейдер, а затем визуализировать их, пока я нахожусь в этом проходе шейдера.

В настоящее время я использую foreachцикл, но мне интересно, может ли использование LINQ повысить производительность?

Нил Найт
источник
1
возможный дубликат производительности «вложенного foreach» и «лямбда / linq-запроса» (LINQ-to-Objects)
Дэниел Эрвикер, 01
1
Пожалуйста, подумайте о том, чтобы установить ответ @ MarcGravell на принятый, есть ситуации, например, linq to sql, где linq быстрее, чем for / foreach.
paqogomez

Ответы:

222

Почему LINQ должен быть быстрее? Он также использует циклы внутри.

В большинстве случаев LINQ будет немного медленнее, потому что он вызывает накладные расходы. Не используйте LINQ, если вам важна производительность. Используйте LINQ, потому что вам нужен более короткий, удобочитаемый и поддерживаемый код.

codymanix
источник
7
Итак, ваш опыт показывает, что LINQ работает быстрее и затрудняет чтение и сопровождение кода? Пожалуйста, объясни.
codymanix
87
Я думаю, у вас все было наоборот. Он говорит, что LINQ МЕДЛЕНнее. Это из-за над головой. Он также говорит, что LINQ легче читать и поддерживать.
Джозеф Макинтайр
5
Сожалею. Тем временем у нас было много вещей, в которых мы сравнивали производительность linq и for или foreach, и большую часть времени linq был быстрее.
Оффлер
34
Честно говоря, на мой взгляд, цикл foreach более читабелен, чем его метод LINQ. Я использую LINQ, потому что это круто :)
LuckyLikey
4
Да, но в некоторых случаях LINQ действительно может улучшить читаемость, так что забудьте мой бессмысленный комментарий <3
LuckyLikey
59

LINQ-to-Objects обычно добавляет некоторые незначительные накладные расходы (несколько итераторов и т. Д.). Он по-прежнему выполняет циклы и вызывает вызовы делегатов, и , как правило, ему приходится выполнять дополнительное разыменование, чтобы добраться до захваченных переменных и т. Д. В большинстве кодов это будет практически незаметно, и более того, чем это будет предоставлено более простым для понимания кодом.

С другими поставщиками LINQ , как LINQ к SQL, то , так как запрос может фильтровать на сервере должно быть гораздо лучше , чем в квартире foreach, но , скорее всего , вы не сделали бы одеяло в "select * from foo" любом случае , так что это не обязательно справедливым сравнение.

Re PLINQ; параллелизм может сократить затраченное время, но общее время ЦП обычно немного увеличивается из-за накладных расходов на управление потоками и т. д.

Марк Гравелл
источник
В другом ответе вы упомянули, что не используете LINQ для коллекций в памяти, например List<Foo>; вместо этого я должен использовать foreachблок для этих коллекций. Рекомендация использовать foreachв этих контекстах имеет смысл. Мое беспокойство: следует ли заменять запросы LINQ на запросы только в foreach случае обнаружения проблем с производительностью? Забегая вперед, я рассмотрю foreachпервое.
IAbstract
19

Я думаю, что LINQ лучше использовать в foreachцикле, потому что он дает более чистый и простой для понимания код. Но LINQ медленнее, чем foreach. Чтобы узнать больше, прочтите статью LINQ vs FOREACH vs FOR Loop Performance .

Праная Рана
источник
15

LINQ сейчас работает медленнее, но в какой-то момент может стать быстрее. Преимущество LINQ в том, что вам не нужно заботиться о том, как он работает. Если придумать новый метод, который будет невероятно быстрым, люди в Microsoft смогут реализовать его, даже не сообщая вам, и ваш код будет намного быстрее.

Что еще более важно, LINQ намного легче читать. Этого должно быть достаточно.

Юке ван дер Маас
источник
3
Мне нравится строчка «Microsoft может это реализовать», возможно ли это, я имею в виду, возможно ли это без обновления фреймворка?
Шриваллаб,
1
LINQ никогда не станет быстрее, чем нативная реализация, поскольку в конце концов он преобразуется в нативную реализацию. Не существует специальных инструкций ЦП LINQ и регистров LINQ, которые можно было бы использовать для трансляции более быстрого машинного кода LINQ - и если бы они были, они также использовались бы не-LINQ кодом.
mg30rg
Нет, в какой-то момент определенные операции ссылки могут стать многопоточными или даже использовать графический процессор в какой-то момент.
Джон Сток
11

Вероятно, следует отметить, что forцикл быстрее, чем foreach. Итак, для исходного сообщения, если вас беспокоит производительность критического компонента, такого как средство визуализации, используйте forцикл.

Ссылка: в .NET, какой цикл выполняется быстрее: for или foreach?

peewee_RotA
источник
9

Вы можете повысить производительность, если используете параллельный LINQ для многоядерных процессоров. См. Параллельный LINQ (PLINQ) (MSDN).

mcintyre321
источник
5

Меня заинтересовал этот вопрос, поэтому я только что сделал тест. Использование .NET Framework 4.5.2 на процессоре Intel (R) Core (TM) i3-2328M @ 2,20 ГГц, 2200 МГц, 2 ядра с 8 ГБ оперативной памяти под управлением Microsoft Windows 7 Ultimate.

Похоже, LINQ может быть быстрее, чем для каждого цикла. Вот результаты, которые я получил:

Exists = True
Time   = 174
Exists = True
Time   = 149

Было бы интересно, если бы некоторые из вас могли скопировать и вставить этот код в консольное приложение и тоже протестировать. Перед тестированием с объектом (Сотрудник) я попробовал тот же тест с целыми числами. LINQ там тоже был быстрее.

public class Program
{
    public class Employee
    {
        public int id;
        public string name;
        public string lastname;
        public DateTime dateOfBirth;

        public Employee(int id,string name,string lastname,DateTime dateOfBirth)
        {
            this.id = id;
            this.name = name;
            this.lastname = lastname;
            this.dateOfBirth = dateOfBirth;

        }
    }

    public static void Main() => StartObjTest();

    #region object test

    public static void StartObjTest()
    {
        List<Employee> items = new List<Employee>();

        for (int i = 0; i < 10000000; i++)
        {
            items.Add(new Employee(i,"name" + i,"lastname" + i,DateTime.Today));
        }

        Test3(items, items.Count-100);
        Test4(items, items.Count - 100);

        Console.Read();
    }


    public static void Test3(List<Employee> items, int idToCheck)
    {

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

        bool exists = false;
        foreach (var item in items)
        {
            if (item.id == idToCheck)
            {
                exists = true;
                break;
            }
        }

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    public static void Test4(List<Employee> items, int idToCheck)
    {

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

        bool exists = items.Exists(e => e.id == idToCheck);

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    #endregion


    #region int test
    public static void StartIntTest()
    {
        List<int> items = new List<int>();

        for (int i = 0; i < 10000000; i++)
        {
            items.Add(i);
        }

        Test1(items, -100);
        Test2(items, -100);

        Console.Read();
    }

    public static void Test1(List<int> items,int itemToCheck)
    {

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

        bool exists = false;
        foreach (var item in items)
        {
            if (item == itemToCheck)
            {
                exists = true;
                break;
            }
        }

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    public static void Test2(List<int> items, int itemToCheck)
    {

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

        bool exists = items.Contains(itemToCheck);

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    #endregion

}
Тео Канд.
источник
Вот что у меня получилось: Exists = True Time = 274 Exists = True Time = 314
PmanAce 03
2
Рассматривали ли вы сначала сделать linq, а потом foreach, это тоже может иметь значение
Мухаммад Мамур Хан
3
Интересный. У меня Exists=True Time=184 Exists=True Time=135это на ноутбуке Apache Gaming (Win 10, C # 7.3). Скомпилирован и запущен в режиме отладки. Если я переверну тесты, я получу Exists=True Time=158 Exists=True Time=194. Думаю, Linq более оптимизирован.
Джеймс Уилкинс
1
В этом посте есть недоразумение относительно объектного теста. Хотя определенно интересно, что List.Exists и .Contains работают лучше, чем foreach. Важно отметить, что .Exists не является методом linq to entity и будет работать только со списками, его эквивалентный метод linq, .Any (), определенно работает медленнее, чем foreach.
AbdulG
3

На самом деле это довольно сложный вопрос. Linq делает определенные вещи очень простыми, и если вы их реализуете самостоятельно, вы можете споткнуться (например, linq .Except ()). Это особенно относится к PLinq, и особенно к параллельной агрегации, реализованной в PLinq.

В общем, для идентичного кода linq будет медленнее из-за накладных расходов на вызов делегата.

Однако если вы обрабатываете большой массив данных и применяете относительно простые вычисления к элементам, вы получите огромный прирост производительности, если:

  1. Вы используете массив для хранения данных.
  2. Вы используете цикл for для доступа к каждому элементу (в отличие от foreach или linq).

    • Примечание. При тестировании, пожалуйста, всем помните - если вы используете один и тот же массив / список для двух последовательных тестов, кеш ЦП сделает второй быстрее. *
Адам Браун
источник