Reputation: 779
I have a simple scenario, where i am trying to test the performance of a expression compiled tree on an list of stock objects. Below is the code
The performance of expression compiled tree is 5x slower than static lambda call. I am not sure whether this is a standard performance one can expect with expression compiled tree. Would appreciate any insight.
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);
}
Upvotes: 7
Views: 5099
Reputation: 4950
Here the alternative approach that utilizes manual visiting of expression and emitting the effective code.
Upvotes: 0
Reputation: 3049
I did a bit of testing myself comparing lambda expression, compiled expression tree, straight function call and inline code. The results were very interesting. I almost think there is a fault in my test because the expression tree was faster but I guess this is not impossible. The lambda expression is the slowest!! The interesting thing is that the expression tree is quicker than the function call and is only slightly slower than inline code. Not what I expected at all.
Edit: Actually I would consider the lambda and compiled function to be equal in speed in the results below
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();
}
Results for 3 runs are:
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
Upvotes: 7
Reputation: 2400
The difference has to do with the compiler having more information and spending more effort on optimizing the code if you compile it at compile time, rather than at runtime... Also, using a lambda, you have a more "flexible" program (you can choose the lambda at run time). That comes at the cost of an extra function call, and losing a lot of potential optimizations.
To make a more "fair" comparison, you can compare a static lambda vs a dynamic lambda using something like:
Func<Stock, bool> compileTime = (Stock d) => (d.Symbol == "MSFT" && d.CloseDate.Millisecond > 0) || d.Price > 0;
instead of the hardcoded code..
There you'll also find a difference, but a slightly smaller one... The difference is for the same reason (more optimization)... You can decrease the difference by optimizing your lambda by hand (although that isn't always possible, since the compiler can create valid CLI code that can't be created manually with a lambda).
But for example, if you change your dynamic lambda from:
var line5 = Expression.OrElse(line4, line3);
to:
var line5 = Expression.OrElse(line3, line4);
You'll see how the lambda performs between 1x and 2x of your original compiled code.
Upvotes: 3