我有一个简单的场景,我试图在一个库存对象列表上测试表达式编译树的性能.下面是代码
表达式编译树的性能比静态lambda调用慢5倍.我不确定这是否是表达式编译树可以预期的标准性能.非常感谢任何见解.
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); }
解决方法
不同之处在于编译器有更多的信息,如果你在编译时编译它而不是在运行时就花费更多的精力优化代码…而且,使用lambda,你有一个更“灵活”的程序(你可以在运行时选择lambda).这需要付出代价
一个额外的函数调用,并失去了许多潜在的优化.
一个额外的函数调用,并失去了许多潜在的优化.
为了进行更“公平”的比较,您可以使用以下内容比较静态lambda与动态lambda:
Func<Stock,bool> compileTime = (Stock d) => (d.Symbol == "MSFT" && d.CloseDate.Millisecond > 0) || d.Price > 0;
而不是硬编码的代码..
在那里你也会发现一个区别,但是稍微小一点……差别是出于同样的原因(更多优化)……你可以通过手工优化你的lambda来减少差异(尽管这并不总是可能的),因为编译器可以创建无法使用lambda手动创建的有效CLI代码.
但是,例如,如果您从以下位置更改动态lambda:
var line5 = Expression.OrElse(line4,line3);
至:
var line5 = Expression.OrElse(line3,line4);
您将看到lambda如何在原始编译代码的1x和2x之间执行.