2009-03-18 9 views
3

Beim Erstellen von LINQ-Ausdrücken (für mich, linq to objects) gibt es viele Möglichkeiten, etwas zu erreichen, einige viel, viel besser und effizienter als andere.Was ist der beste Weg, LINQ-Ausdrücke zu optimieren oder zu "tunen"?

  • Gibt es eine gute Möglichkeit, diese Ausdrücke zu "tunen" oder zu optimieren?
  • Welche grundlegenden Metriken verwenden Leute und wie sammeln Sie sie?
  • Gibt es eine Möglichkeit, um "Total Iterations" oder eine andere Metrik zu erhalten, wo Sie wissen könnten, dass niedriger bedeutet besser?

EDIT

Dank Richard/Jon für Ihre Antworten.

Was ich wirklich will, ist eine Möglichkeit, eine einfache Operation Count "OCount" für einen LINQ-Ausdruck zu bekommen, obwohl ich nicht sicher bin, dass die Haken in LINQ existieren, um es zu erlauben. Angenommen, ich habe ein bestimmtes Leistungsniveau für eine bestimmte Maschinenhardware (ein SLA). Idealerweise würde ich einen Komponententest hinzufügen, um zu bestätigen, dass die typischen Daten, die durch diese Abfrage verschoben wurden, innerhalb der zugewiesenen Zeit (aus dem SLA) verarbeitet werden. Problem ist, dass dies auf dem Build-Server/Entwickler-Rechner/etc ausgeführt werden würde. was wahrscheinlich wenig Ähnlichkeit mit der Maschinen-Hardware des SLA hat. Also die Idee ist, dass ich einen akzeptablen maximalen "OCount" für den Ausdruck bestimmen würde, wissend, dass, wenn der OCount kleiner als X ist, er sicherlich akzeptable Leistung unter dem SLA auf der "typischen" Zielhardware bereitstellen wird. Wenn der OCount diesen Schwellenwert überschreitet, würde der Build/Unit-Test eine Warnung generieren. Im Idealfall würde Ich mag so etwas wie dieser (Pseudo-Code-ish) haben:

var results = [big linq expression run against test dataset]; 
Assert.IsLess(MAXALLOWABLE_OCOUNT, results.OCount) 

wo results.OCount würde einfach geben Sie mir die Gesamt Iterationen (n) notwendig, um die Ergebnismenge zu erzeugen.

Warum würde ich das mögen ??

Nun, selbst mit einem LINQ-Ausdruck mittlerer Größe kann eine kleine Änderung/Addition RIESIGE Auswirkungen auf die Leistung als Folge der Erhöhung der Gesamtzahl der Operationen haben. Der Anwendungscode würde immer noch alle Komponententests bestehen, da er immer noch das korrekte Ergebnis liefert, aber bei der Bereitstellung kläglich langsam arbeitet.

Der andere Grund ist für einfaches Lernen. Wenn Sie etwas tun und der OCount um eine Größenordnung nach oben oder unten geht, dann lernen Sie etwas.

EDIT # 2 Ich werde auch eine mögliche Antwort werfen. Es ist nicht meins, es kommt von Cameron MacFarland von einer anderen Frage, die ich fragte, die dieses hervorbrachte. Es stellt sich heraus, dass die Antwort hier in einer Unit-Test-Umgebung funktionieren könnte, wie sie in der ersten Bearbeitung dieser Frage beschrieben wurde.

Die Essenz davon wäre, die Testdatensätze in der Unit Test Fixture zu erstellen, die Sie in den LINQ-Ausdruck in der in dieser Antwort skizzierten Weise einspeisen und dann die Iterationszahlen addieren und mit der maximal zulässigen Iterationszahl vergleichen.

Siehe Cameron's answer here

Antwort

3
  1. ein SLA Get (oder eine andere Definition) beschreibt die Gesamtleistung erforderlich.

  2. Messen Sie die Anwendungsleistung, und wie weit unter Anforderungen ist (wenn innerhalb der Anforderungen dann stoppen und etwas Nützliches tun).

  3. Verwenden Sie einen Profiler, um eine detaillierte Leistungsaufschlüsselung zu erhalten, identifizieren Sie die Teile des Systems, die am besten verbessert werden können (eine kleine Verbesserung von Hot Code ist wahrscheinlich besser als eine große Verbesserung zu selten genanntem Code).

  4. Führen Sie die Änderung durch, führen Sie die Geräte-/Funktionstests erneut aus (es ist kein Problem, die falsche Sache schnell auszuführen).

  5. Zum 1.

Wenn in # 3 Sie einen LINQ Ausdruck finden, ist ein performamce Problem dann auf diese Frage zu benötigen eine Antwort nachzudenken. Die Antwort hängt vollständig davon ab, welchen LINQ-Anbieter Sie verwenden und welche Details in Ihrem Fall verwendet werden. Es gibt keine allgemeine Antwort.

6

Sie müssen im Grunde die Komplexitätsfunktion erarbeiten. Das hängt vom Betreiber ab, ist aber leider nicht sehr gut dokumentiert.

(Für den allgemeinen Grundsatz I mit Richard Antwort zustimmen - dies ist nur LINQ to Objects Sachen.)

Wenn Sie bestimmte Betreiber haben Sie daran interessiert sind, würde es über sie zu fragen wert sein, aber aus der Spitze von meinem Kopf:

  • Select = O (n)
  • Wo = O (n)
  • Verbinden = O (Innen + Außen + entspricht) (dh es ist kein billiger als inner + outer, konnte aber sein so schlecht wie inner * outer abhängig von th e Ergebnisse)
  • GroupJoin = wie verbinden, aber anstelle des Streamings von äußeren
  • SortiertNach = O (n log n)
  • Select = O (n + Ergebnisse)
  • Count = O (1 gepuffert) oder O (n), je nachdem, ob es implementiert IList
  • Count (Prädikat) = O (n)
  • Max/Min = O (n)
  • Alle/Alle = O (n) (mit möglichst früh)
  • Eindeutig = O (n)
  • Überspringen/Take = O (n)
  • Skipwhile/Takewhile = O (n)

Die genauen Eigenschaften davon abhängen, ob die Betreiber Puffer oder zu Bächen.

0

auf Jon Hinzufügen, die auf Richard fügt

Ein weiteres Problem ist zu überlegen, ob Sie alle Ergebnisse einer LINQ-Abfrage verarbeiten. In bestimmten Situationen, insbesondere in der Benutzeroberfläche, verarbeiten Sie nur eine Teilmenge der Ergebnisse, die von einer LINQ-Abfrage zurückgegeben werden. In diesen Situationen ist es wichtig zu wissen, welche LINQ-Abfragen eine faule Auswertung unterstützen. Das ist die Möglichkeit, eine Teilmenge der Ergebnisse zurückzugeben, ohne die gesamte Sammlung zu verarbeiten.

Zum Beispiel ruft Movenext() auf den folgenden LINQ-Operationen wird ein Ergebnis in einer Zeit

  • Select
  • Wo

Aber die folgende Prozess muss jedes Element in der Sammlung verarbeiten, bevor Rückgabe eines einzelnen Artikels.

  • SortiertNach
  • Ausnahme von (verarbeitet der anderen Sammlung vollständig)