Чи швидше оператор LINQ, ніж цикл 'foreach'?


124

Я пишу менеджер Mesh Rendering і вважаю, що було б гарною ідеєю згрупувати всі сітки, які використовують один і той же шейдер, а потім зробити їх, поки я перебуваю в цьому проходці шейдера.

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


1
можливий дублікат продуктивності "Вкладений foreach" vs "lambda / linq query" (LINQ-to-Objects)
Daniel Earwicker

1
Будь ласка, врахуйте відповідь налаштування @ MarcGravell на прийняту, є ситуації, наприклад, linq to sql, коли linq швидше, ніж for / foreach.
paqogomez

Відповіді:


222

Чому LINQ повинен бути швидшим? Він також використовує петлі внутрішньо.

У більшості випадків LINQ буде трохи повільнішим, оскільки він вводить накладні витрати. Не використовуйте LINQ, якщо ви сильно дбаєте про продуктивність. Використовуйте LINQ, тому що ви хочете коротший, що краще читається та підтримується.


7
Тож ваш досвід полягає в тому, що LINQ швидше і робить код важче читати та підтримувати? Будь ласка, поясніть.
кодиманікс

87
Я думаю, ти це мав назад. Він каже, що LINQ - ПОЛІЗНИЙ. Це пов’язано з надмірною головою. Він також каже, що LINQ легше читати та підтримувати.
Джозеф Макінтайр

5
Вибачте. Тим часом у нас було чимало речей, де ми порівнювали linq та ефективність роботи, або більшу частину часу linq була швидшою.
Розпродаж

34
Якщо чесно, на мою думку, цикл foreach є більш читабельним, ніж його метод LINQ. Я використовую LINQ, тому що це круто :)
LuckyLikey

4
Так, але в деяких випадках LINQ може дійсно покращити читабельність, тому забудьте мій бездумний коментар <3
LuckyLikey

59

Загалом LINQ до об'єктів збирається додати деякі граничні накладні витрати (кілька ітераторів тощо). Він все ще повинен робити цикли та делегувати виклики, і , як правило, доведеться робити додаткові перенаправлення, щоб потрапити на захоплені змінні тощо. У більшості кодів це буде практично невизначним, і більше, ніж надається простим для розуміння кодом.

З іншими постачальниками LINQ , як LINQ до SQL, то , так як запит може фільтрувати на сервері повинно бути набагато краще , ніж в квартирі foreach, але , швидше за все , ви не зробили б ковдру в "select * from foo" будь-якому випадку , так що це не обов'язково справедливим порівняння.

Re PLINQ; паралелізм може скоротити минулий час, але загальний час процесора зазвичай трохи збільшиться за рахунок накладних витрат управління потоками тощо.


В іншій відповіді ви натякали на те, що не використовувати LINQ у колекціях пам'яті - наприклад List<Foo>; натомість я повинен використовувати foreachблок цих колекцій. Рекомендація використовувати foreachв цих контекстах має сенс. Моє занепокоєння: чи слід замінювати запити LINQ лише foreach тоді, коли виявляю проблеми з продуктивністю? Йдучи вперед, я розгляну foreachперше.
IАнотація

19

Я думаю, що LINQ краще використовувати в foreachциклі, оскільки він дає набагато більш чистий і простий для розуміння код. Але LINQ повільніше, ніж foreach. Щоб отримати більше, перегляньте статтю LINQ vs FOREACH проти FOR Loop Performance .


15

LINQ зараз повільніше, але в якийсь момент він може стати швидшим. Хороша річ у LINQ в тому, що вам не потрібно дбати про те, як вона працює. Якщо придумати новий метод, який надзвичайно швидкий, люди в Microsoft можуть його реалізувати, навіть не повідомивши вам, і ваш код буде набагато швидшим.

Що ще важливіше, LINQ просто читати набагато простіше. Цього має бути достатньо підстав.


3
Мені подобається рядок "Майкрософт може це реалізувати", чи це можливо, я маю на увазі, чи можливо це без оновлення рамки?
Шріваллаб

1
LINQ ніколи насправді не стане швидшим за нативну реалізацію, оскільки наприкінці дня він перекладається на нативну реалізацію. Не існує спеціальних інструкцій процесора LINQ та регістрів LINQ, які можна використовувати для швидшого перекладу машинного коду LINQ - і якби вони були, вони також використовувалися б і не-LINQ-кодом.
mg30rg

Ні правда, в якийсь момент певні операції зв'язку можуть стати багатопотоковими або навіть використовувати GPU в якийсь момент.
Джон Сток



5

Мене це питання зацікавило, тому я зробив тест саме зараз. Використання .NET Framework 4.5.2 для процесора Intel (R) 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

2
Ви думали про те, як зробити лінк спочатку і пізніше пропонувати, це теж може
змінитися

3
Цікаво. Я отримав Exists=True Time=184 Exists=True Time=135це на ігровому ноутбуці Apache (Win 10, C # 7.3). Скомпільовано та запущено в режимі налагодження. Якщо я скасую отримані тести Exists=True Time=158 Exists=True Time=194. Здається, Linq є більш оптимізованим, я думаю.
Джеймс Вілкінс

1
У цій публікації є непорозуміння щодо тесту об'єкта. Хоча, безумовно, цікаво, що List.Exists та .Contains здаються ефективнішими, ніж передбачення. Важливо зауважити, що .Exists не є методом linkq для сутностей, і він буде працювати лише над списками, його еквівалентний метод linq, .Any (), безумовно, працює повільніше, ніж foreach.
AbdulG

3

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

Загалом, для ідентичного коду linq буде повільнішим через накладні витрати виклику делегата.

Якщо ви обробляєте великий масив даних і застосовуєте до елементів відносно прості обчислення, ви отримаєте величезне підвищення продуктивності, якщо:

  1. Ви використовуєте масив для зберігання даних.
  2. Ви використовуєте цикл for для доступу до кожного елемента (на відміну від foreach або linq).

    • Примітка. Під час тестування, будь ласка, всі пам’ятайте - якщо ви використовуєте один і той же масив / список протягом двох послідовних тестів, кеш процесора зробить другий швидше. *
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.