Wir versuchen derzeit, die Leistung unserer Entity Framework-Abfragen zu optimieren. Insbesondere suchen wir nach Möglichkeiten, die CPU-Auslastung zu reduzieren.EF Performance: ComputeHashValue() in der Kompilierung von Abfragen
Mit dotTrace haben wir analysiert, was die meiste CPU-Zeit beim Ausführen verschiedener Abfragen kostet. Siehe den folgenden Schnappschuss:
Dieser Schnappschuss stammt aus einer ziemlich einfachen Abfrage, zeigt aber immer noch, welche die zeitaufwendigste Operation ist: GetExecutionPlan(). Dringt man noch weiter hinein, so sieht man, dass in der Methode ComputeHashValue(), die für alle Knoten in der Ausdrucksbaumstruktur rekursiv aufgerufen wird, viel Zeit in Anspruch genommen wird.
This blog post besagt, dass
Das Entity Framework wird die Knoten im Ausdrucksbaum gehen und einen Hash erzeugen, die der Schlüssel platzieren Sie es in der Abfrage Cache verwendet wird.
So scheint es, dass die Hash-Werte nur als Schlüssel für den Abfrage-Cache verwendet werden. Da wir IEnumerable.Contains verwenden() in unsere Anfragen, EF sie nicht Chache (this MSDN article (chapters 3.2 & 4.1) sehen Deshalb haben wir deaktiviert Query Plan Caching wie folgt:.
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var objectSet = objectContext.CreateObjectSet<Customer>();
objectSet.EnablePlanCaching = false;
// use objectSet for queries..
Wir hoffen, dass dann ComputeHashValue() nicht aufgerufen werden mehr. es gibt jedoch in der Aufrufstruktur keine Änderung von dotTrace und Leistung war identisch gezeigt wurde, wie mit Query Plan Caching aktiviert.
gibt es einen Grund, warum ComputeHashValue() immer noch, wenn Query Plan benötigt wird Caching deaktiviert ist?
Bei komplexeren Abfragen benötigen alle Aufrufe von ComputeHashValue() bis zu 70% der gesamten CPU Zeit, die für die Ausführung der Abfrage benötigt wird. Diese Aufrufe zu vermeiden (wenn sie nicht benötigt werden) würde unsere Leistung massiv beeinträchtigen.
Nach dem Screenshot fast keine Zeit dort verbracht. Ist dieses Profil nicht repräsentativ für Ihre Arbeitsbelastung? – usr
Die 3 Aufrufe werden hier mit 0ms bezeichnet. Aber ComputeHashValue() wird für jeden Knoten im Ausdrucksbaum aufgerufen, was insgesamt zu Hunderten von Aufrufen führt und fast alle der 93ms werden von ihnen verwendet. Sie können die verschiedenen rekursiven Aufrufe von ApplyRulesToSubtree() sehen, die letztendlich zu Aufrufen von ComputeHashValue() führen. –