关于Expression Tree和IL Emit的所谓的"性能差别"
昨天写了《三种属性操作性能比较》,有个网友写信问我一个问题:从性能上看,Expression Tree和IL Emit孰优孰劣?虽然我在回信中作了简单的回答,但不知道这个网友是否懂我的意思。反正今天呆在家里也没事儿,干脆再就这个话题再写一篇文章。
? 一、Expression Tree和IL Emit并不存在所谓的性能差异Expression Tree和IL Emit的性能孰优孰劣,这本是个“不是问题的问题”。因为两者之间并不存在本质的区别,所以也谈不上性能的优劣问题。举个例子来说,我们知道.NET Framework 2.0,3.0和3.5使用的是相同的CLR。但是C# 3.0、3.5在2.0的基础上推出了很多语言层面的特性,比如自动实现属性: 1: public class Foo 3: public Bar Bar{get;set;}
5: { 7: } public Bar Bar Expression Tree和IL Emit之间的关系与这些“语法糖”类似。编译后的Expression Tree就是IL代码;而IL Emit让我们可以用高级语言的编程方式来控制中间语言(IL)程序。由于最终的东西都是一样的,谈不上谁比谁好的问题。编译Expression Tree实现了向IL的转换,如果你通过IL Emit写的IL能够比Expression Tree自动转换的好,那么你的程序性能就好,否则性能就差。但是我们不能说Expression Tree和IL Emit在性能上孰优孰劣。 二、属性赋值操作的两种写法我们说明Expression Tree和IL Emit之间不存在性能的差异,我们不妨写个例子。简单起见,我们还是采用前面谈到过的属性赋值和取值的操作为例。假设有如下一个接口IFoo,包含一个类型和名称均为Bar的可读写的属性。 3: Bar{get;set;}
5: class Bar{}
现在我们通过Expression Tree和IL Emit两种方式编写一个静态方法对IFoo对象的Bar属性进行赋值。简单起见,我们甚至将静态方法的参数类型直接指定为IFoo和Bar,从而省去了类型转换操作。下面是通过Expression Tree进行属性赋值的方法:SetPropertyValueViaExpression。 3: var property = typeof(IFoo).GetProperty("Bar");
5: var propertyValue = Expression.Parameter(typeof(Bar));
7: var setAction= Expression.Lambda<Action<IFoo,Bar>>(setPropertyValue,target,propertyValue).Compile(); 9: } 而下面的SetPropertyValueViaEmit则通过IL Emit的方式完成了一样的工作: 5: ILGenerator ilGenerator = method.GetILGenerator();
7: ilGenerator.Emit(OpCodes.Ldarg_1); 9: ilGenerator.Emit(OpCodes.Ret); 11: method.DefineParameter(1,ParameterAttributes.In,1)">"obj"); 13: var setAction = (Action<IFoo,Bar>)method.CreateDelegate(typeof(Action<IFoo,Bar>));
static Bar GetPropertyValueViaExpression(IFoo foo) 6: var getFunc = Expression.Lambda<Func<IFoo,Bar>>(getPropertyValue,target).Compile(); 8: } 下面则是基于IL Emit的版本: "GetValue",1)">typeof(Bar),1)">typeof(IFoo) });
6: ILGenerator ilGenerator = method.GetILGenerator(); "target"); 13: 14: }
四、看看两种写法对应的IL我们说过,经过编译的Expression Tree就是一段IL代码,而IL Emit则直接反映了IL的执行流程。要判断两者在性能方面孰优孰劣,我们只需要看看Expression Tree最终被转换成怎样的IL。我们现在的做法是动态生成一个程序集,将Expression Tree部分定义到一个方法之中。虽然IL Emit已经是真实底反映了底层的IL代码,但是为了我们的比较更加直观,我们也将IL Emit的部分也写入相应的方法。 为此我们在一个Console应用中的Main方法编写了如下的代码:动态创建了名称为Artech.EmitVsExpression的程序集,其中定义了同名的模块。一个唯一的类型Program定义其中,其中定义了四个静态方法:GetPropertyValueViaExpression、SetPropertyValueViaExpression、GetPropertyValueViaEmit和GetPropertyValueViaEmit。而方法体部分则是上面Expression Tree和IL Emit定义的内容。最后这个程序集被保存为一个同名的.dll文件。 3: var property = 4: var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Artech.EmitVsExpression"),AssemblyBuilderAccess.RunAndSave);
6: var typeBuilder = moduleBuilder.DefineType("Program");
8: //GetPropertyValueViaExpression
10: var target = Expression.Parameter( 11: var getPropertyValue = Expression.Property(target,1)" id="lnum12"> 12: Expression.Lambda<Func<IFoo,target).CompileToMethod(methodBuilder);
14: //SetPropertyValueViaExpression
16: target = Expression.Parameter( 17: var propertyValue = Expression.Parameter( 18: var setPropertyValue = Expression.Call(target,1)" id="lnum19"> 19: Expression.Lambda<Action<IFoo,propertyValue).CompileToMethod(methodBuilder); 21: //GetPropertyValueViaEmit
24: ilGenerator.Emit(OpCodes.Ldarg_0); 26: ilGenerator.Emit(OpCodes.Ret); 28: //SetPropertyValueViaEmit
31: ilGenerator.Emit(OpCodes.Ldarg_0); 33: ilGenerator.EmitCall(OpCodes.Callvirt,1)" id="lnum34"> 34: ilGenerator.Emit(OpCodes.Ret); 36: typeBuilder.CreateType(); 1: .method class [EmitVsExpressionTree]Bar
3: { 5: .maxstack 1 7: IL_0001: callvirt instance class [EmitVsExpressionTree]Bar [EmitVsExpressionTree]IFoo::get_Bar()
9: } // end of method Program::GetPropertyValueViaExpression
14: 15: .maxstack 1
17: IL_0001: callvirt instance 18: IL_0006: ret
void SetPropertyValueViaExpression(class [EmitVsExpressionTree]IFoo A_0,
// Code size 8 (0x8) 7: IL_0001: ldarg.1 9: IL_0007: ret 11:? 13: class [EmitVsExpressionTree]Bar A_1) cil managed
15: // Code size 8 (0x8)
17: IL_0000: ldarg.0 19: IL_0002: callvirt instance class [EmitVsExpressionTree]Bar)
21: } // end of method Program::SetPropertyValueViaEmit 既然在IL上它们没有差别,那么它们就是两对等效的方法。如果你通过Reflector来打开我们生成的.dll,你会清晰地看到这真的是两对完全一致的方法。 // Methods
return foo1.Bar; static Bar GetPropertyValueViaExpression(IFoo foo1) void SetPropertyValueViaEmit(IFoo foo1,Bar bar1) 16: foo1.Bar = bar1; 18:? 20: { 22: } 相关内容
推荐文章
站长推荐
热点阅读
|