Ich habe vor kurzem begonnen, LINQ ziemlich zu verwenden, und ich habe wirklich keine Laufzeit-Komplexität für irgendeine der LINQ-Methoden erwähnt. Offensichtlich spielen hier viele Faktoren eine Rolle. Lassen Sie uns die Diskussion daher auf den einfachen LINQ to Objects-Provider beschränken. Nehmen wir weiterhin an, dass jede Func
, die als Selektor/Mutator/etc. übergeben wird, eine billige O (1) -Operation ist.Welche Garantien gibt es für die Laufzeitkomplexität (Big-O) der LINQ-Methoden?
Es scheint offensichtlich, dass alle Single-Pass-Operationen (Select
, Where
, Count
, Take/Skip
, Any/All
usw.) O (n) sein, da sie brauchen nur die Sequenz gehen einmal; obwohl auch dies der Faulheit unterliegt.
Die Dinge sind düsterer für die komplexeren Operationen; die set-like Operatoren (Union
, Distinct
, Except
, usw.) arbeiten standardmäßig mit GetHashCode
(afaik), so dass es vernünftig anzunehmen scheint, dass sie intern eine Hashtabelle verwenden, wodurch diese Operationen auch O (n) sind. Im Algemeinen. Was ist mit den Versionen, die eine IEqualityComparer
verwenden?
OrderBy
würde eine Sortierung benötigen, also betrachten wir am wahrscheinlichsten O (n log n). Was ist, wenn es schon sortiert ist? Wie wäre es wenn ich sage OrderBy().ThenBy()
und den gleichen Schlüssel für beide?
Ich konnte GroupBy
(und Join
) entweder Sortieren oder Hashing sehen. Welches ist es?
Contains
würde O (n) auf einem List
, aber O (1) auf einem HashSet
sein - nicht überprüft LINQ die zugrunde liegenden Container zu sehen, ob es die Sache beschleunigen kann?
Und die eigentliche Frage - bis jetzt habe ich es auf dem Glauben genommen, dass die Operationen performant sind. Aber kann ich darauf bauen? Zum Beispiel legen STL-Container die Komplexität jeder Operation klar fest. Gibt es ähnliche Garantien für die LINQ-Leistung in der .NET-Bibliotheksspezifikation?
Weitere Frage (als Reaktion auf Kommentare):
Hatte nicht wirklich über Overhead gedacht, aber ich habe nicht erwartet, dass es sehr viel für einfache Linq-to-Objects zu sein. Der CodingHorror Post spricht über Linq-to-SQL, wo ich verstehen kann, die Abfrage zu analysieren und SQL würde Kosten hinzufügen - gibt es ähnliche Kosten für den Objects-Provider zu? Wenn ja, ist es anders, wenn Sie die deklarative oder funktionale Syntax verwenden?
Obwohl ich Ihre Frage nicht wirklich beantworten kann, möchte ich bemerken, dass im Allgemeinen der größte Teil der Leistung "Overhead" im Vergleich zur Kernfunktionalität sein wird. Dies ist natürlich nicht der Fall, wenn Sie sehr große Datensätze (> 10k Elemente) haben, also bin ich neugierig, in welchem Fall Sie wissen wollen. – Henri
Re: "ist es anders, wenn Sie die deklarative oder funktionale Syntax verwenden?" - Der Compiler übersetzt die deklarative Syntax in die funktionale Syntax, so dass sie identisch sind. –
"STL-Container geben die Komplexität jeder Operation klar vor" .NET-Container geben auch die Komplexität jeder Operation an. Linq-Erweiterungen sind mit STL-Algorithmen verwandt und nicht mit STL-Containern. Genau wie beim Anwenden eines STL-Algorithmus auf einen STL-Container müssen Sie die Komplexität der Linq-Erweiterung mit der Komplexität der .NET-Containeroperation (en) kombinieren, um die resultierende Komplexität richtig zu analysieren. Dies schließt die Berücksichtigung von Vorlagenspezialisierungen ein, wie Aaronaught in seiner Antwort erwähnt. – Timbo