2012-07-26 18 views
5

Tengo un escenario simple, en el que intento probar el rendimiento de un árbol compilado de expresiones en una lista de objetos de stock. Debajo está el códigoRendimiento del árbol de expresiones compilado

El rendimiento del árbol compilado de expresión es 5 veces más lento que la llamada lambda estática. No estoy seguro de si este es un rendimiento estándar que uno puede esperar con el árbol compilado de expresiones. Agradecería cualquier idea.

LambdaExpression(); 
List<Stock> stocks = new List<Stock>(); 
for (int ctr = 0; ctr <= 5000000; ctr++) 
{ 
    Stock stk1 = new Stock() { Price = ctr, Symbol = "A", CloseDate = DateTime.Now, FaceValue = ctr } ; 
    stocks.Add(stk1); 
} 
CompileTimeLamda(a); 
DynamicLambda(a); 


public static void LambdaExpression() 
{ 
    ParameterExpression CS1 = Expression.Parameter(typeof(Stock), "d"); 

    var line1 = Expression.Equal(Expression.Property(CS1, typeof(Stock).GetProperty("Symbol")), Expression.Constant("MSFT", typeof(string))); 
    var line2 = Expression.GreaterThan(Expression.Property(Expression.Property(CS1, typeof(Stock).GetProperty("CloseDate")),typeof(DateTime).GetProperty("Millisecond")), 
           Expression.Constant(0, typeof(int))); 
    var line3 = Expression.GreaterThan(Expression.Property(CS1, typeof(Stock).GetProperty("Price")), Expression.Constant((double)0, typeof(double))); 
    var line4 = Expression.And(line1,line2); 
    var line5 = Expression.OrElse(line4, line3); 

    func = Expression.Lambda<Func<Stock, bool>>(line5, new ParameterExpression[] { CS1 }).Compile(); 
} 


public static void DynamicLambda(List<Stock> stks) 
{ 
    Stopwatch watch = new Stopwatch(); 
    watch.Start(); 
    foreach (var d in stks) 
    { 
     func(d); 
    } 
    watch.Stop(); 
    Console.WriteLine("Dynamic Lambda :" + watch.ElapsedMilliseconds); 
} 

public static void CompileTimeLamda(List<Stock> stks) 
{ 
    Stopwatch watch = new Stopwatch(); 
    watch.Start(); 
    foreach (var d in stks) 
    { 
     if (d.Symbol == "MSFT" && d.CloseDate.Millisecond > 0 || 
            (d.Price) > 0) ; 
    } 
    watch.Stop(); 
    Console.WriteLine("Compile Time Lamda " +watch.ElapsedMilliseconds); 
} 
+3

Compruebe la IL compilada; el optimizador puede estar matando a todo tu código. – SLaks

+0

En realidad, no está comparando 2 expresiones lambda. El segundo es solo código compilado, es decir, no delegado. Es muy posible que el delegado sea lo que lo hace más lento. – MikeKulls

+0

Parece que está comparando manzanas y naranjas. El "tiempo de compilación lambda" no usa una lambda en absoluto. Además, el compilador probablemente está optimizando el bucle porque realmente no estás haciendo nada (instrucción vacía ";") –

Respuesta

2

La diferencia tiene que ver con el compilador tener más información y gastar más esfuerzo en la optimización del código si se compila en tiempo de compilación, en lugar de en tiempo de ejecución ... Además, el uso de un lambda, que tienen una programa más "flexible" (puede elegir el lambda en tiempo de ejecución). Esto conlleva el costo de una llamada de función adicional y la pérdida de muchas optimizaciones potenciales.

Para hacer una comparación más "justo", se puede comparar una lambda estática vs lambda dinámica usando algo como:

Func<Stock, bool> compileTime = (Stock d) => (d.Symbol == "MSFT" && d.CloseDate.Millisecond > 0) || d.Price > 0; 

en lugar del código codificado ..

Allí también encuentra una diferencia, pero una más pequeña ... La diferencia es por la misma razón (más optimización) ... Puedes disminuir la diferencia optimizando tu lambda a mano (aunque eso no siempre es posible, ya que el compilador puede crear código de CLI válido que no se puede crear manualmente con un lambda).

Pero por ejemplo, si cambia de lambda dinámica desde:

var line5 = Expression.OrElse(line4, line3); 

a:

var line5 = Expression.OrElse(line3, line4); 

Usted verá cómo el lambda realiza entre 1x y 2x de su código compilado originales.

5

Hice algunas pruebas comparando expresiones lambda, árbol de expresiones compiladas, llamadas directas a funciones y códigos en línea. Los resultados fueron muy interesantes. Casi creo que hay un error en mi prueba porque el árbol de expresiones fue más rápido, pero creo que esto no es imposible. ¡La expresión lambda es la más lenta! Lo interesante es que el árbol de expresiones es más rápido que la llamada a la función y solo es un poco más lento que el código en línea. No es lo que esperaba en absoluto.

Editar: En realidad yo consideraría la lambda y la función compilada a ser igual en la velocidad en los resultados de abajo

void TestIt() 
    { 
     var ints = new int[10000000]; 
     Random rand = new Random(); 
     for (int i = 0; i < ints.Length; i++) 
      ints[i] = rand.Next(100); 

     Func<int, int> func1 = i => i + 2; 
     Func<int, int> func2 = CompileIt(); 

     var stopwatch = new Stopwatch(); 

     for (int x = 0; x < 3; x++) 
     { 
      stopwatch.Restart(); 
      for (int i = 0; i < ints.Length; i++) 
       ints[i] = func1(ints[i]); 
      stopwatch.Stop(); 
      Console.Write("Lamba      "); 
      Console.Write(stopwatch.ElapsedMilliseconds); 
      ShowSum(ints); 

      stopwatch.Restart(); 
      for (int i = 0; i < ints.Length; i++) 
       ints[i] = func2(ints[i]); 
      stopwatch.Stop(); 
      Console.Write("Lambda from expression tree "); 
      Console.Write(stopwatch.ElapsedMilliseconds); 
      ShowSum(ints); 

      stopwatch.Restart(); 
      for (int i = 0; i < ints.Length; i++) 
       ints[i] = AddTwo(ints[i]); 
      stopwatch.Stop(); 
      Console.Write("Compiled function   "); 
      Console.Write(stopwatch.ElapsedMilliseconds); 
      ShowSum(ints); 

      stopwatch.Restart(); 
      for (int i = 0; i < ints.Length; i++) 
       ints[i] = ints[i] + 2; 
      stopwatch.Stop(); 
      Console.Write("Compiled code    "); 
      Console.Write(stopwatch.ElapsedMilliseconds); 
      ShowSum(ints); 
     } 
    } 

    private int AddTwo(int value) 
    { 
     return value + 2; 
    } 

    private void ShowSum(int[] ints) 
    { 
     Console.WriteLine(" Sum = " + ints.Sum(i => i).ToString()); 
    } 

    private Func<int, int> CompileIt() 
    { 
     var param1 = Expression.Parameter(typeof(int)); 
     Expression body = Expression.Add(param1, Expression.Constant(2)); 
     return Expression.Lambda<Func<int, int>>(body, new [] { param1 }).Compile(); 
    } 

Resultados para 3 carreras son:

Lamba      164 Sum = 515074919 
Lambda from expression tree 86 Sum = 535074919 
Compiled function   155 Sum = 555074919 
Compiled code    54 Sum = 575074919 

Lamba      153 Sum = 595074919 
Lambda from expression tree 88 Sum = 615074919 
Compiled function   156 Sum = 635074919 
Compiled code    53 Sum = 655074919 

Lamba      156 Sum = 675074919 
Lambda from expression tree 88 Sum = 695074919 
Compiled function   157 Sum = 715074919 
Compiled code    54 Sum = 735074919 
Cuestiones relacionadas