Ich versuche, LINQ in den Griff zu bekommen. Was mich am meisten stört, ist, dass ich, obwohl ich die Syntax besser verstehe, die Leistung nicht unabsichtlich für die Ausdruckskraft opfern möchte.
Sind sie gute zentralisierte Informationsspeicher oder Bücher für 'Effective LINQ'? Wenn dies nicht der Fall ist, was ist Ihre persönliche Lieblings-Hochleistungs-LINQ-Technik?
Ich beschäftige mich hauptsächlich mit LINQ to Objects, aber alle Vorschläge zu LINQ to SQL und LINQ to XML sind natürlich auch willkommen. Vielen Dank.
c#
.net
linq
performance
Steve Townsend
quelle
quelle
yield
(und somit O (N)) resultieren . Ich denke, Sie projizieren mehr Schwierigkeiten als tatsächlich vorhanden sind.Antworten:
Wenn Sie nur verstehen, was LINQ intern tut, sollten Sie genügend Informationen erhalten, um zu wissen, ob Sie einen Leistungseinbruch erleiden.
Hier ist ein einfaches Beispiel, in dem LINQ die Leistung verbessert. Betrachten Sie diesen typischen Ansatz der alten Schule:
List<Foo> foos = GetSomeFoos(); List<Foo> filteredFoos = new List<Foo>(); foreach(Foo foo in foos) { if(foo.SomeProperty == "somevalue") { filteredFoos.Add(foo); } } myRepeater.DataSource = filteredFoos; myRepeater.DataBind();
Der obige Code wird also zweimal durchlaufen und einen zweiten Container für die gefilterten Werte zuweisen. Was für eine Verschwendung! Vergleichen mit:
var foos = GetSomeFoos(); var filteredFoos = foos.Where(foo => foo.SomeProperty == "somevalue"); myRepeater.DataSource = filteredFoos; myRepeater.DataBind();
Dies wird nur einmal wiederholt (wenn der Repeater gebunden ist). Es wird immer nur der Originalbehälter verwendet.
filteredFoos
ist nur ein Zwischenzähler. Und wenn Sie sich aus irgendeinem Grund entscheiden, den Repeater später nicht mehr zu binden, wird nichts verschwendet. Sie iterieren oder bewerten nicht einmal.Wenn Sie in sehr komplexe Sequenzmanipulationen geraten, können Sie möglicherweise viel gewinnen, indem Sie die inhärente Verwendung von Verkettung und verzögerter Auswertung durch LINQ nutzen. Wie bei allem geht es auch hier nur darum zu verstehen, was es tatsächlich tut.
quelle
Linq hat als integrierte Technologie Leistungsvor- und -nachteile. Dem Code hinter den Erweiterungsmethoden wurde vom .NET-Team erhebliche Aufmerksamkeit geschenkt, und seine Fähigkeit, eine verzögerte Auswertung bereitzustellen, bedeutet, dass die Kosten für die Durchführung der meisten Manipulationen an einer Gruppe von Objekten auf den größeren Algorithmus verteilt sind, der die manipulierte Gruppe erfordert . Es gibt jedoch einige Dinge, die Sie wissen müssen, um die Leistung Ihres Codes zu beeinträchtigen oder zu beeinträchtigen.
In erster Linie spart Linq Ihrem Programm nicht auf magische Weise die Zeit oder den Speicher, die für die Ausführung einer Operation erforderlich sind. Dies kann diese Vorgänge nur verzögern, bis sie unbedingt benötigt werden. OrderBy () führt einen QuickSort durch, der genauso lange dauert, als hätten Sie Ihren eigenen QuickSorter geschrieben oder List.Sort () zum richtigen Zeitpunkt verwendet. Denken Sie also immer daran, was Sie von Linq verlangen, wenn Sie Abfragen schreiben. Wenn eine Manipulation nicht erforderlich ist, versuchen Sie, die Abfrage- oder Methodenkette neu zu strukturieren, um dies zu vermeiden.
Aus dem gleichen Grund erfordern bestimmte Operationen (Sortieren, Gruppieren, Aggregieren) die Kenntnis der gesamten Menge, auf die sie einwirken. Das allerletzte Element in einer Reihe könnte das erste sein, das die Operation von ihrem Iterator zurückgeben muss. Da Linq-Operationen ihre aufzählbare Quelle nicht ändern sollten, aber viele der von ihnen verwendeten Algorithmen (dh In-Place-Sortierungen), werden diese Operationen nicht nur ausgewertet, sondern die gesamte Aufzählung in eine konkrete, endliche Struktur kopiert , die Operation ausführen und durch sie nachgeben. Wenn Sie also OrderBy () in einer Anweisung verwenden und nach einem Element aus dem Endergebnis fragen, wird ALLES, was die ihm gegebene IEnumerable erzeugen kann, ausgewertet, als Array im Speicher gespeichert, sortiert und dann ein Element an a zurückgegeben Zeit. Die Moral ist,
Schließlich erhöhen Linq-Methoden die Größe des Aufrufstapels und den Speicherbedarf Ihres Systems drastisch. Jede Operation, die den gesamten Satz kennen muss, behält den gesamten Quellensatz im Speicher, bis das letzte Element iteriert wurde, und die Auswertung jedes Elements umfasst einen Aufrufstapel, der mindestens doppelt so tief ist wie die Anzahl der Methoden in Ihrer Kette oder Ihren Klauseln in Ihrer Inline-Anweisung (ein Aufruf von MoveNext () jedes Iterators oder GetEnumerator plus mindestens ein Aufruf von jedem Lambda auf dem Weg). Dies führt einfach zu einem größeren, langsameren Algorithmus als ein intelligent entwickelter Inline-Algorithmus, der dieselben Manipulationen ausführt. Der Hauptvorteil von Linq ist die Einfachheit des Codes. Das Erstellen und Sortieren eines Wörterbuchs mit Listen von Gruppenwerten ist kein leicht verständlicher Code (vertrauen Sie mir). Mikrooptimierungen können dies weiter verschleiern. Wenn die Leistung Ihr Hauptanliegen ist, verwenden Sie Linq nicht. Dies erhöht den Zeitaufwand um ca. 10% und den Speicheraufwand für die Bearbeitung einer Liste selbst um ein Vielfaches. Die Wartbarkeit ist jedoch in der Regel das Hauptanliegen der Entwickler, und Linq hilft dort ENDGÜLTIG.
Zum Performance-Kick: Wenn die Leistung Ihres Algorithmus die heilige, kompromisslose erste Priorität ist, würden Sie in einer nicht verwalteten Sprache wie C ++ programmieren. .NET wird aufgrund seiner verwalteten Laufzeitumgebung mit nativer JIT-Kompilierung, verwaltetem Speicher und zusätzlichen Systemthreads viel langsamer. Ich würde eine Philosophie annehmen, dass es "gut genug" ist; Linq kann von Natur aus Verlangsamungen einführen, aber wenn Sie den Unterschied nicht erkennen können und Ihr Kunde den Unterschied nicht erkennen kann, gibt es für alle praktischen Zwecke keinen Unterschied. "Vorzeitige Optimierung ist die Wurzel allen Übels"; Lassen Sie es funktionieren, und suchen Sie dann nach Möglichkeiten, um es leistungsfähiger zu machen, bis Sie und Ihr Kunde sich einig sind, dass es gut genug ist. Es könnte immer "besser" sein, aber es sei denn, Sie möchten Maschinencode von Hand verpacken,
quelle
Es gibt verschiedene Faktoren, die die Leistung beeinflussen.
Oft bietet die Entwicklung einer Lösung mit LINQ eine recht vernünftige Leistung, da das System einen Ausdrucksbaum erstellen kann, um die Abfrage darzustellen, ohne die Abfrage tatsächlich auszuführen, während es diese erstellt. Nur wenn Sie die Ergebnisse durchlaufen, wird dieser Ausdrucksbaum zum Generieren und Ausführen einer Abfrage verwendet.
In Bezug auf die absolute Effizienz kann es bei vordefinierten gespeicherten Prozeduren zu Leistungseinbußen kommen. Im Allgemeinen besteht der Ansatz jedoch darin, eine Lösung mit einem System zu entwickeln, das eine angemessene Leistung bietet (z. B. LINQ), und sich nicht um ein paar Prozent Verlust zu sorgen der Leistung. Wenn eine Abfrage dann langsam ausgeführt wird, sehen Sie sich möglicherweise die Optimierung an.
Die Realität ist, dass die meisten Abfragen nicht das geringste Problem haben werden, über LINQ erledigt zu werden. Die andere Tatsache ist, dass wenn Ihre Abfrage langsam ausgeführt wird, es wahrscheinlich wahrscheinlicher ist, dass Probleme mit der Indizierung, Struktur usw. auftreten als mit der Abfrage selbst. Selbst wenn Sie nach Optimierungen suchen, berühren Sie häufig nicht den LINQ, sondern nur den Datenbankstruktur, gegen die es arbeitet.
Wenn beim Umgang mit XML ein Dokument geladen und in den Speicher analysiert wird (wie alles, was auf dem DOM-Modell oder einem XmlDocument oder was auch immer basiert), wird der Speicher stärker beansprucht als bei Systemen, bei denen Ereignisse ausgelöst werden Geben Sie an, dass Sie ein Start- oder End-Tag suchen, aber keine vollständige In-Memory-Version des Dokuments erstellen (z. B. SAX oder XmlReader). Der Nachteil ist, dass die ereignisbasierte Verarbeitung im Allgemeinen etwas komplexer ist. Auch hier gibt es bei den meisten Dokumenten kein Problem - die meisten Systeme verfügen über mehrere GB RAM, sodass das Aufnehmen einiger MB für ein einzelnes XML-Dokument kein Problem darstellt (und Sie häufig einen großen Satz von XML-Dokumenten zumindest etwas verarbeiten der Reihe nach). Nur wenn Sie eine riesige XML-Datei haben, die 100 MB aufnehmen würde, machen Sie sich Sorgen über die jeweilige Auswahl.
Denken Sie daran, dass Sie mit LINQ auch über speicherinterne Listen usw. iterieren können. In einigen Situationen (z. B. wenn Sie eine Reihe von Ergebnissen in einer Funktion immer wieder verwenden) können Sie .ToList verwenden oder .ToArray, um die Ergebnisse zurückzugeben. Manchmal kann dies nützlich sein, obwohl Sie im Allgemeinen versuchen möchten, die Abfrage der Datenbank eher im Arbeitsspeicher zu verwenden.
Bei persönlichen Favoriten - NHibernate LINQ - handelt es sich um ein objektrelationales Zuordnungstool, mit dem Sie Klassen definieren, Zuordnungsdetails definieren und dann die Datenbank aus Ihren Klassen generieren können, und nicht umgekehrt. Die LINQ-Unterstützung ist hübsch gut (sicherlich besser als solche wie SubSonic).
quelle
In Linq to SQL müssen Sie sich nicht so sehr um die Leistung kümmern. Sie können alle Ihre Aussagen so verketten, wie Sie es für am besten lesbar halten. Linq übersetzt am Ende nur alle Ihre Anweisungen in eine SQL-Anweisung, die erst am Ende aufgerufen / ausgeführt wird (wie beim Aufrufen von a
.ToList()
a
var
kann diese Anweisung enthalten, ohne sie auszuführen, wenn Sie verschiedene zusätzliche Anweisungen unter verschiedenen Bedingungen anwenden möchten. Die Ausführung am Ende erfolgt nur, wenn Sie Ihre Anweisungen in ein Ergebnis wie ein Objekt oder eine Liste von Objekten übersetzen möchten.quelle
Es gibt ein Codeplex-Projekt namens i4o, das ich vor einiger Zeit verwendet habe, um die Leistung von Linq to Objects in Fällen zu verbessern, in denen Sie Gleichheitsvergleiche durchführen, z
from p in People where p.Age == 21 select p;
http://i4o.codeplex.com/ Ich habe es nicht mit .Net 4 getestet, kann also nicht sicher sagen, dass es immer noch funktioniert, aber es lohnt sich, es sich anzusehen. Damit es seine Magie entfaltet, müssen Sie Ihre Klasse meist nur mit einigen Attributen dekorieren, um anzugeben, welche Eigenschaft indiziert werden soll. Wenn ich es vorher benutzt habe, funktioniert es allerdings nur mit Gleichheitsvergleichen.
quelle