Czy instrukcja LINQ jest szybsza niż pętla „foreach”?


124

Piszę do menedżera renderowania siatki i pomyślałem, że dobrym pomysłem byłoby zgrupowanie wszystkich siatek, które używają tego samego modułu cieniującego, a następnie renderowanie ich, gdy jestem w tej przepustce do cieniowania.

Obecnie używam foreachpętli, ale zastanawiałem się, czy użycie LINQ może dać mi wzrost wydajności?



1
Proszę rozważyć ustawienie odpowiedzi @ MarcGravell na akceptowaną, są sytuacje, na przykład linq do sql, w których linq jest szybszy niż for / foreach.
paqogomez

Odpowiedzi:


222

Dlaczego LINQ powinien być szybszy? Używa również pętli wewnętrznie.

W większości przypadków LINQ będzie nieco wolniejszy, ponieważ wprowadza narzut. Nie używaj LINQ, jeśli zależy Ci na wydajności. Użyj LINQ, ponieważ potrzebujesz krótszego, bardziej czytelnego i łatwego w utrzymaniu kodu.


7
Czy masz doświadczenie w tym, że LINQ jest szybszy i sprawia, że ​​kod jest trudniejszy do odczytania i utrzymania? Proszę wytłumacz.
codymanix

87
Myślę, że miałeś to wstecz. Mówi, że LINQ jest wolniejszy. Wynika to z nad głową. Mówi też, że LINQ jest łatwiejszy do odczytania i utrzymania.
Joseph McIntyre,

5
Przepraszam. W międzyczasie mieliśmy wiele rzeczy, w których porównaliśmy linq i wydajność for lub foreach, i przez większość czasu linq był szybszy.
Offler

34
Szczerze mówiąc, moim zdaniem pętla foreach jest bardziej czytelna niż jej metoda LINQ. Używam LINQ, ponieważ jest fajny :)
LuckyLikey

4
Tak, ale w niektórych przypadkach LINQ może naprawdę poprawić czytelność, więc zapomnij o moim bezmyślnym komentarzu <3
LuckyLikey

59

LINQ-to-Objects generalnie doda pewne marginalne narzuty (wiele iteratorów itp.). Nadal musi zrobić pętle, i ma powołuje delegata i generalnie trzeba zrobić kilka dodatkowych dereferencing dostać na przechwyconych zmiennych itd. W większości kodu będzie to praktycznie niewykrywalne, a więcej niż zapewniana przez prostsze do zrozumienia kodu.

Z innych dostawców LINQ jak LINQ-SQL, a następnie od kwerendy można filtrować na serwerze powinno być znacznie lepiej niż mieszkania foreach, ale najprawdopodobniej nie zrobiłby koc "select * from foo" i tak , więc nie jest to koniecznie uzasadniona porównanie.

Re PLINQ; równoległość może skrócić czas, który upłynął , ale całkowity czas procesora zwykle nieco wzrośnie ze względu na narzuty związane z zarządzaniem wątkami itp.


W innej odpowiedzi nawiązywałeś do nieużywania LINQ na kolekcjach w pamięci - np List<Foo>; zamiast tego powinienem użyć foreachbloku na tych kolekcjach. Rekomendacja użycia foreachw tych kontekstach ma sens. Mój problem: czy powinienem zastępować zapytania LINQ tylko foreach wtedy, gdy wykryję problem z wydajnością? Idąc dalej, rozważę foreachpierwszy.
IAbstract


15

LINQ jest teraz wolniejszy, ale w pewnym momencie może stać się szybszy. Zaletą LINQ jest to, że nie musisz przejmować się tym, jak to działa. Jeśli zostanie wymyślona nowa metoda, która jest niewiarygodnie szybka, ludzie w firmie Microsoft mogą ją wdrożyć, nawet nie mówiąc Tobie, a Twój kod byłby znacznie szybszy.

Co ważniejsze, LINQ jest po prostu znacznie łatwiejszy do odczytania. To powinien być wystarczający powód.


3
Podoba mi się linia "Microsoft może to zaimplementować" czy to możliwe, to znaczy czy jest to możliwe bez aktualizacji frameworka?
Shrivallabh

1
LINQ nigdy tak naprawdę nie będzie szybszy niż implementacja natywna, ponieważ na koniec dnia przekłada się na implementację natywną. Nie ma specjalnych instrukcji procesora LINQ i rejestrów LINQ, których można użyć do tłumaczenia szybszego kodu maszynowego LINQ - a gdyby tak było, byłyby również używane przez kod inny niż LINQ.
mg30rg

Nieprawda, w pewnym momencie niektóre operacje na łączach mogą stać się wielowątkowe lub nawet wykorzystywać GPU.
John Stock



5

Zainteresowało mnie to pytanie, więc przed chwilą zrobiłem test. Korzystanie z .NET Framework 4.5.2 na procesorze Intel (R) Core (TM) i3-2328M @ 2,20 GHz, 2200 MHz, 2 rdzenie z 8 GB pamięci RAM i systemem Microsoft Windows 7 Ultimate.

Wygląda na to, że LINQ może być szybsze niż dla każdej pętli. Oto wyniki, które otrzymałem:

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

Byłoby interesujące, gdyby niektórzy z was mogli skopiować i wkleić ten kod w aplikacji konsolowej i również przetestować. Przed testowaniem na obiekcie (pracownik) wykonałem ten sam test z liczbami całkowitymi. LINQ również tam był szybszy.

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

}

Oto co otrzymałem: Exists = True Time = 274 Exists = True Time = 314
PmanAce

2
czy zastanawiałeś się nad zrobieniem linq najpierw i za każdym razem później, może to mieć również znaczenie
Muhammad Mamoor Khan

3
Ciekawy. Mam Exists=True Time=184 Exists=True Time=135to na laptopie Apache Gaming (Win 10, C # 7.3). Skompilowany i uruchomiony w trybie debugowania. Jeśli odwrócę otrzymane testy Exists=True Time=158 Exists=True Time=194. Wydaje mi się, że Linq jest bardziej zoptymalizowany.
James Wilkins

1
W tym poście jest nieporozumienie dotyczące testu obiektowego. Chociaż jest zdecydowanie interesujące, że List.Exists i .Contains wydają się działać lepiej niż foreach. Należy zauważyć, że .Exists nie jest metodą linq do encji i będzie działać tylko na listach, a jej odpowiednik w linq, .Any (), zdecydowanie działa wolniej niż foreach.
AbdulG

3

W rzeczywistości jest to dość złożone pytanie. Linq bardzo ułatwia pewne rzeczy, więc jeśli zaimplementujesz je sam, możesz się o nie potknąć (np. Linq .Except ()). Dotyczy to w szczególności PLinq, a zwłaszcza agregacji równoległej wdrożonej przez PLinq.

Ogólnie rzecz biorąc, dla identycznego kodu linq będzie wolniejsze ze względu na narzut wywołania delegata.

Jeśli jednak przetwarzasz dużą liczbę danych i stosujesz stosunkowo proste obliczenia do elementów, uzyskasz ogromny wzrost wydajności, jeśli:

  1. Używasz tablicy do przechowywania danych.
  2. Używasz pętli for, aby uzyskać dostęp do każdego elementu (w przeciwieństwie do foreach lub linq).

    • Uwaga: podczas testów porównawczych wszyscy pamiętajcie - jeśli użyjesz tej samej tablicy / listy w dwóch kolejnych testach, pamięć podręczna procesora przyspieszy drugą. *
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.