Ist eine LINQ-Anweisung schneller als eine 'foreach'-Schleife?

123

Ich schreibe einen Mesh-Rendering-Manager und dachte, es wäre eine gute Idee, alle Meshes, die denselben Shader verwenden, zu gruppieren und diese dann zu rendern, während ich mich in diesem Shader-Pass befinde.

Ich verwende derzeit eine foreachSchleife, habe mich aber gefragt, ob die Verwendung von LINQ zu einer Leistungssteigerung führen könnte.

Neil Knight
quelle
1
Mögliches Duplikat der Leistung von "Nested foreach" vs "Lambda / Linq Query" (LINQ-to-Objects)
Daniel Earwicker
1
Bitte ziehen Sie in Betracht, die Antwort von @ MarcGravell auf die akzeptierte zu setzen. Es gibt Situationen, z. B. Linq zu SQL, in denen Linq schneller ist als For / Foreach.
Paqogomez

Antworten:

221

Warum sollte LINQ schneller sein? Es werden auch intern Schleifen verwendet.

In den meisten Fällen ist LINQ etwas langsamer, da dadurch Overhead entsteht. Verwenden Sie LINQ nicht, wenn Sie viel Wert auf Leistung legen. Verwenden Sie LINQ, weil Sie kürzeren, besser lesbaren und wartbaren Code wünschen.

Codymanix
quelle
7
Ihre Erfahrung ist also, dass LINQ schneller ist und das Lesen und Verwalten von Code erschwert? Bitte erkläre.
Codymanix
87
Ich denke du hattest es rückwärts. Er sagt, LINQ ist langsamer. Dies ist auf Overhead zurückzuführen. Er sagt auch, dass LINQ einfacher zu lesen und zu warten ist.
Joseph McIntyre
5
Es tut uns leid. In der Zwischenzeit hatten wir viele Dinge, bei denen wir linq und für oder für jede Leistung verglichen haben, und die meiste Zeit war linq schneller.
Offler
34
Um ehrlich zu sein, ist eine foreach-Schleife meiner Meinung nach besser lesbar als ihre LINQ-Methode. Ich benutze LINQ, weil es cool ist :)
LuckyLikey
4
Ja, aber in einigen Fällen kann LINQ die Lesbarkeit wirklich verbessern. Vergessen Sie also meinen sinnlosen Kommentar <3
LuckyLikey
59

LINQ-to-Objects wird im Allgemeinen einige marginale Gemeinkosten hinzufügen (mehrere Iteratoren usw.). Es muss immer noch die Schleifen ausführen und hat Delegatenaufrufe und muss im Allgemeinen eine zusätzliche Dereferenzierung durchführen, um an erfasste Variablen usw. zu gelangen. In den meisten Codes ist dies praktisch nicht nachweisbar und wird durch den einfacher zu verstehenden Code mehr als ermöglicht .

Bei anderen LINQ-Anbietern wie LINQ-to-SQL sollte die Abfrage, da sie auf dem Server gefiltert werden kann, viel besser sein als eine Wohnung foreach, aber höchstwahrscheinlich hätten Sie "select * from foo" sowieso keine Decke erstellt , sodass dies nicht unbedingt fair ist Vergleich.

Re PLINQ; Parallelität kann die verstrichene Zeit verkürzen , aber die Gesamt-CPU-Zeit erhöht sich normalerweise geringfügig aufgrund des Overheads der Thread-Verwaltung usw.

Marc Gravell
quelle
In einer anderen Antwort erwähnt Sie nicht mithilfe von LINQ auf Sammlungen im Speicher - zB List<Foo>; Stattdessen sollte ich einen foreachBlock für diese Sammlungen verwenden. Die Empfehlung, foreachin diesen Kontexten zu verwenden, ist sinnvoll. Mein Anliegen: foreach Sollte ich LINQ-Abfragen nur ersetzen, wenn ich ein Leistungsproblem feststelle? In Zukunft werde ich das foreacherste betrachten.
IAbstract
15

LINQ ist jetzt langsamer, aber es könnte irgendwann schneller werden. Das Gute an LINQ ist, dass Sie sich nicht darum kümmern müssen, wie es funktioniert. Wenn eine neue Methode entwickelt wird, die unglaublich schnell ist, können die Mitarbeiter von Microsoft sie implementieren, ohne es Ihnen zu sagen, und Ihr Code wäre viel schneller.

Noch wichtiger ist jedoch, dass LINQ viel einfacher zu lesen ist. Das sollte Grund genug sein.

Jouke van der Maas
quelle
3
Ich mag die Zeile "Microsoft kann es implementieren" ist es möglich, ich meine, ist es möglich, ohne dass ich das Framework aktualisiere?
Shrivallabh
1
LINQ wird nie wirklich schneller als die native Implementierung, da es letztendlich in die native Implementierung übersetzt wird. Es gibt keine speziellen LINQ-CPU-Anweisungen und LINQ-Register, mit denen schnellerer LINQ-Maschinencode übersetzt werden kann - und wenn dies der Fall wäre, würden sie auch von Nicht-LINQ-Code verwendet.
mg30rg
Nein, irgendwann können bestimmte Verbindungsvorgänge zu Multithreading-Vorgängen werden oder irgendwann sogar die GPU verwenden.
John Stock
9

Wenn Sie paralleles LINQ für mehrere Kerne verwenden, erhalten Sie möglicherweise eine Leistungssteigerung. Siehe Parallel LINQ (PLINQ) (MSDN).

mcintyre321
quelle
5

Ich war an dieser Frage interessiert, also habe ich gerade einen Test gemacht. Verwenden von .NET Framework 4.5.2 auf einer Intel (R) Core (TM) i3-2328M-CPU mit 2,20 GHz, 2200 MHz, 2 Core (s) und 8 GB RAM unter Microsoft Windows 7 Ultimate.

Es sieht so aus, als wäre LINQ möglicherweise schneller als für jede Schleife. Hier sind die Ergebnisse, die ich erhalten habe:

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

Es wäre interessant, wenn einige von Ihnen diesen Code kopieren und in eine Konsolen-App einfügen und auch testen könnten. Vor dem Testen mit einem Objekt (Mitarbeiter) habe ich denselben Test mit ganzen Zahlen versucht. Auch dort war LINQ schneller.

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

}
Theo Kand.
quelle
Dies ist, was ich bekam: Exists = True Time = 274 Exists = True Time = 314
PmanAce
2
Haben Sie darüber nachgedacht, das Linq zuerst und später zu machen, könnte es auch einen Unterschied machen
Muhammad Mamoor Khan
3
Interessant. Ich habe Exists=True Time=184 Exists=True Time=135es auf einem Apache Gaming-Laptop (Win 10, C # 7.3). Kompiliert und im Debug-Modus ausgeführt. Wenn ich die Tests rückgängig mache, bekomme ich Exists=True Time=158 Exists=True Time=194. Scheint, als wäre Linq optimierter, denke ich.
James Wilkins
1
In diesem Beitrag gibt es ein Missverständnis bezüglich des Objekttests. Es ist zwar auf jeden Fall interessant, dass List.Exists und .Contains eine bessere Leistung zu erzielen scheinen als foreach. Es ist wichtig zu beachten, dass .Exists keine Linq-to-Entities-Methode ist und nur für Listen funktioniert. Die Linq-äquivalente Methode .Any () ist definitiv langsamer als die foreach-Methode.
AbdulG
3

Dies ist eigentlich eine ziemlich komplexe Frage. Mit Linq ist es sehr einfach, bestimmte Dinge zu tun. Wenn Sie sie selbst implementieren, können Sie darüber stolpern (z. B. linq .Except ()). Dies gilt insbesondere für PLinq und insbesondere für die von PLinq implementierte parallele Aggregation.

Im Allgemeinen ist linq bei identischem Code aufgrund des Overheads des Delegatenaufrufs langsamer.

Wenn Sie jedoch eine große Anzahl von Daten verarbeiten und relativ einfache Berechnungen auf die Elemente anwenden, erhalten Sie eine enorme Leistungssteigerung, wenn:

  1. Sie verwenden ein Array, um die Daten zu speichern.
  2. Sie verwenden eine for-Schleife, um auf jedes Element zuzugreifen (im Gegensatz zu foreach oder linq).

    • Hinweis: Bitte denken Sie beim Benchmarking daran, dass der CPU-Cache den zweiten schneller macht, wenn Sie dasselbe Array / dieselbe Liste für zwei aufeinanderfolgende Tests verwenden. * *
Adam Brown
quelle