Leistung von Ausdrucksbäumen

.net c# expression-trees performance

Frage

Mein derzeitiges Verständnis ist dieser "hart codierte" Code wie folgt:

public int Add(int x, int y) {return x + y;}

wird immer besser als Ausdruck Baumcode wie folgt:

Expression<Func<int, int, int>> add = (x, y) => x + y; 
var result = add.Compile()(2, 3);

var x = Expression.Parameter(typeof(int)); 
var y = Expression.Parameter(typeof(int)); 
return (Expression.Lambda(Expression.Add(x, y), x, y).
    Compile() as Func<int, int, int>)(2, 3);

Der Compiler verfügt über mehr Informationen und kann mehr Aufwand in die Optimierung des Codes investieren, wenn Sie ihn zur Kompilierzeit kompilieren. Ist das allgemein wahr?

Akzeptierte Antwort

Zusammenstellung

Der Aufruf von Expression.Compile durchläuft genau denselben Prozess wie jeder andere .NET-Code, den Ihre Anwendung in dem Sinne enthält, dass

  • IL-Code wird generiert
  • IL-Code ist JIT-Ted Maschinencode zu bearbeiten

(Der Parsing-Schritt wird übersprungen, da ein Ausdrucksbaum bereits erstellt wurde und nicht aus dem Eingabecode generiert werden muss)

Sie können den Quellcode des Ausdruckscompilers anzeigen, um zu überprüfen, ob IL-Code tatsächlich generiert wird.

Optimierung

Bitte beachten Sie, dass fast die gesamte Optimierung, die von der CLR durchgeführt wird, im JIT-Schritt erfolgt, nicht durch das Kompilieren von C # -Quellcode. Diese Optimierung wird auch durchgeführt, wenn der IL-Code von Ihrem Lambda-Delegierten zum Maschinencode kompiliert wird.

Dein Beispiel

In Ihrem Beispiel vergleichen Sie Äpfel und Orangen. Das erste Beispiel ist eine Methodendefinition, das zweite Beispiel ist Laufzeitcode, der eine Methode erstellt, kompiliert und ausführt. Die Zeit, die zum Erstellen / Kompilieren der Methode benötigt wird, ist viel länger als das tatsächliche Ausführen. Sie können jedoch eine Instanz der kompilierten Methode nach der Erstellung beibehalten. Wenn Sie das getan haben, sollte die Leistung Ihrer generierten Methode identisch mit der ursprünglichen C # -Methode sein.

Betrachte diesen Fall:

private static int AddMethod(int a, int b)
{
    return a + b;
}

Func<int, int, int> add1 = (a, b) => a + b;
Func<int, int, int> add2 = AddMethod;

var x = Expression.Parameter(typeof (int));
var y = Expression.Parameter(typeof (int));
var additionExpr = Expression.Add(x, y);
Func<int, int, int> add3 = 
              Expression.Lambda<Func<int, int, int>>(
                  additionExpr, x, y).Compile();
//the above steps cost a lot of time, relatively.

//performance of these three should be identical
add1(1, 2);
add2(1, 2);
add3(1, 2);

Die Schlussfolgerung, die man daraus ziehen könnte, ist: IL-Code ist IL-Code, egal wie er generiert wird, und Linq Expressions generiert IL-Code.


Beliebte Antwort

Ihre Add Funktion kompiliert wahrscheinlich zu einem Funktions-Overhead (wenn nicht inline) und einem einzelnen Add-Befehl. Geht nicht schneller als das.

Selbst die Konstruktion dieses Ausdrucksbaums wird um Größenordnungen langsamer sein. Das Kompilieren einer neuen Funktion für jeden Aufruf wird im Vergleich zur direkten C # -Implementierung unglaublich teuer.

Versuchen Sie, die Funktion nur einmal zu kompilieren und irgendwo zu speichern.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow