好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

利用表达式树构建委托改善反射性能

利用表达式树构建委托改善反射性能

最近搞一个系统时由于在比较关键地方用到反射了,所以要关注了一下反射的性能问题。搜索一下,不难搜到老赵的 这篇文章 ,下面是一些杂乱的笔记。(建议先看老赵的文章)

.Net4.0反射性能改善

看老赵的文章,老赵得到的结果是这样的:

?

0.0125539 (Directly invoke)

00:00:04.5349626 (Reflection invoke)

0.0322555 (Dynamic executor)

而我把代码搞下来自己运行得到这样的结果:

?

0.0009710 (Directly invoke)

0.4142893 (Reflection invoke)

0.0194501 (Dynamic executor)

这里不是说机器性能造成绝对的时间,而是差距比例完全不一样,想了一阵想起了老赵当时应该是基于.Net3.5,果断把程序的目标框架切换到.Net3.5,结果如下:

?

0.0018801 (Directly invoke)

00:00:02.4288876 (Reflection invoke)

0.0141537 (Dynamic executor)

三者的差距仍然有些不一样,老赵那边的直接调用与动态执行同一数量级的结果还是没有。但发现了另一些信息。反射和直接调用方法.Net4.0比.Net3.5有非常大的改善,特别是反射,性能提升了好几倍。反而构建表达式树动态调用的方式性能比.Net3.5差了一点。但是相对反射还是有差距,按照这个比例,写写表达式树还是值得的。 改善老赵的DynamicMethodExecutor

老赵的那篇的文章的思路是使用DynamicMethodExecutor来构造一个万能的委托Func<object, object[],="" object=""><object, object[], object>其中第一个参数是实例对象,第二是参数列表,第三是返回值。.Net4.0的表达式树要比3.5的先进一点,经过一番改造发现是不需要这么一个万能委托的,直接用Expression.Lambda.Compile()编译出来的Delegate强制转换为强类型的委托来得更加简单。全部代码一个方法即可,精简了许多。

?

/// <summary>

/// 动态构造委托

/// </summary>

/// <param name="methodInfo">方法元数据</param>

/// <returns>委托</returns>

public   static   Delegate BuildDynamicDelegate(MethodInfo methodInfo)

{

     if   (methodInfo == null )

         throw   new   ArgumentNullException( "methodInfo" );

 

     var paramExpressions = methodInfo.GetParameters().Select((p, i) =>

     {

         var name = "param"   + (i + 1).ToString(CultureInfo.InvariantCulture);

         return   Expression.Parameter(p.ParameterType, name);

     }).ToList();

 

     MethodCallExpression callExpression;

     if   (methodInfo.IsStatic)

     {

         //Call(params....)

         callExpression = Expression.Call(methodInfo, paramExpressions);

     }

     else

     {

         var instanceExpression = Expression.Parameter(methodInfo.ReflectedType, "instance" );

         //insatnce.Call(params….)

         callExpression = Expression.Call(instanceExpression, methodInfo, paramExpressions);

         paramExpressions.Insert(0, instanceExpression);

     }

     var lambdaExpression = Expression.Lambda(callExpression, paramExpressions);

     return   lambdaExpression.Compile();

}

使用时转换为强类型的委托即可:

?

var action = (Action<TInstance, T1, T2>)BuildDynamicDelegate(methodInfo);

var func = (Func<TInstance, T1, T2, TReturn>)BuildDynamicDelegate(methodInfo);

老赵那个委托都是object,使用时的类型转换,还有装箱,拆箱都会有一定的性能损失,而强类型就没有这个问题。

首先在老赵的那篇文章上一个方法改为两个方法,然后测试:

?

public   void   Call1( object   o1, object   o2, object   o3) { }

public   void   Call2( int   o1, int   o2, int   o3) { }

?

private   static   void   DynamicExecutor_ObjectType()

{

     var executor = new   DynamicMethodExecutor(Call1MethodInfo);

     var watch1 = new   Stopwatch();

     watch1.Start();

     for   (var i = 0; i < Times; i++)

     {

         executor.Execute(ProgramInstance, ObjectParameters);

     }

     watch1.Stop();

     Console.WriteLine(watch1.Elapsed + " (Dynamic executor(object))(JeffreyZhao)" );

}

private   static   void   DynamicExecutor_IntType()

{

     var executor = new   DynamicMethodExecutor(Call2MethodInfo);

     var watch1 = new   Stopwatch();

     watch1.Start();

     for   (var i = 0; i < Times; i++)

     {

         executor.Execute(ProgramInstance, IntParameters);

     }

     watch1.Stop();

     Console.WriteLine(watch1.Elapsed + " (Dynamic executor(int))(JeffreyZhao)" );

}

private   static   void   DynamicExecutor_StrongObject()

{

     var action = DynamicMethodBuilder.BuildAction<Program, object , object , object >(Call1MethodInfo);

     var watch1 = new   Stopwatch();

     watch1.Start();

     for   (var i = 0; i < Times; i++)

     {

         action(ProgramInstance, ObjectParameters[0], ObjectParameters[1], ObjectParameters[2]);

     }

     watch1.Stop();

     Console.WriteLine(watch1.Elapsed + " (Dynamic executor(object))(zhangweiwen)" );

}

 

private   static   void   DynamicExecutor_StrongInt()

{

     var action = DynamicMethodBuilder.BuildAction<Program, int , int , int >(Call2MethodInfo);

     var watch1 = new   Stopwatch();

     watch1.Start();

     for   (var i = 0; i < Times; i++)

     {

         action(ProgramInstance, IntParameters1[0], IntParameters1[1], IntParameters1[2]);

     }

     watch1.Stop();

     Console.WriteLine(watch1.Elapsed + " (Dynamic executor(int))(zhangweiwen)" );

}

结果:

?

0.0188422 (Dynamic executor( object ))(JeffreyZhao)

0.0210869 (Dynamic executor( int ))(JeffreyZhao)

0.0142841 (Dynamic executor( object ))(zhangweiwen)

0.0147589 (Dynamic executor( int ))(zhangweiwen)

差距不大,但是还是有一定得改善,特别参数是int的方法,用了强类型后性能比较稳定,不会出现偏差。

构建委托动态赋值

既然有动态调用方法,同样也可以动态赋值,而且据我的经验,根据PropertyInfo的SetValue去反射设属性值用得比反射调用方法更加频繁。所以同样需要有方法来动态构建委托改善性能。

幸好,.Net4.0提供了支持,.Net4.0新增了 Expression.Assign 来表示一个赋值表达式。有了它,构建起来比方法的更加简单:

?

private   static   Action<TInstance, TProperty> BuildSetPropertyAction<TInstance, TProperty>(PropertyInfo propertyInfo)

{

     var instanceParam = Expression.Parameter( typeof (TInstance), "instance" );

     var valueParam = Expression.Parameter( typeof (TProperty), "value" );

     //instance.Property

     var propertyProperty = Expression.Property(instanceParam, propertyInfo);

     //instance.Property = value

     var assignExpression = Expression.Assign(propertyProperty, valueParam);

     var lambdaExpression = Expression.Lambda<Action<TInstance, TProperty>>(assignExpression, instanceParam, valueParam);

     return   lambdaExpression.Compile();

}

直接返回了强类型的委托,所以使用起来更加简单:

?

var action = BuildSetPropertyAction<Program, object >(ObjectPropertyInfo);

action(ProgramInstance, ObjectValue);

来测试一下性能:

?

private   static   void   DirectlySetValueType()

{

     var watch1 = new   Stopwatch();

     watch1.Start();

     for   (var i = 0; i &lt; Times; i++)

     {

         ProgramInstance.IntProperty = IntValue;

     }

     watch1.Stop();

     Console.WriteLine(watch1.Elapsed + " (Directly Set IntProperty)" );

}

 

private   static   void   ReflectionSetValueType()

{

     var watch2 = new   Stopwatch();

     watch2.Start();

     for   (var i = 0; i &lt; Times; i++)

     {

         IntPropertyInfo.SetValue(ProgramInstance, IntValue, null );

     }

     watch2.Stop();

     Console.WriteLine(watch2.Elapsed + " (Reflection Set IntProperty)" );

}

 

private   static   void   DynamicSetValueType()

{

     var action = BuildSetPropertyAction&lt;Program, int &gt;(IntPropertyInfo);

     var watch1 = new   Stopwatch();

     watch1.Start();

     for   (var i = 0; i &lt; Times; i++)

     {

         action(ProgramInstance, IntValue);

     }

     watch1.Stop();

     Console.WriteLine(watch1.Elapsed + " (Dynamic Set IntProperty)" );

}

 

private   static   void   DirectlySetReferenceType()

{

     var watch1 = new   Stopwatch();

     watch1.Start();

     for   (var i = 0; i &lt; Times; i++)

     {

         ProgramInstance.ObjectProperty = ObjectValue;

     }

     watch1.Stop();

     Console.WriteLine(watch1.Elapsed + " (Directly Set ObjectProperty)" );

}

 

private   static   void   ReflectionSetReferenceType()

{

     var watch2 = new   Stopwatch();

     watch2.Start();

     for   (var i = 0; i &lt; Times; i++)

     {

         ObjectPropertyInfo.SetValue(ProgramInstance, ObjectValue, null );

     }

     watch2.Stop();

     Console.WriteLine(watch2.Elapsed + " (Reflection Set ObjectProperty)" );

}

 

private   static   void   DynamicSetReferenceType()

{

     var action = BuildSetPropertyAction&lt;Program, object &gt;(ObjectPropertyInfo);

     //action(ProgramInstance, ObjectValue);

     var watch1 = new   Stopwatch();

     watch1.Start();

     for   (var i = 0; i &lt; Times; i++)

     {

         action(ProgramInstance, ObjectValue);

     }

     watch1.Stop();

     Console.WriteLine(watch1.Elapsed + " (Dynamic Set ObjectProperty)" );

}

结果如下:

?

Test Set Value:

0.0003237 (Directly Set IntProperty)

0.3160570 (Reflection Set IntProperty)

0.0132668 (Dynamic Set IntProperty)

-----

0.0028183 (Directly Set ObjectProperty)

0.2937783 (Reflection Set ObjectProperty)

0.0150118 (Dynamic Set ObjectProperty)

虽然跟直接赋值不能比,但比反射快大概30倍。

全部代码

希望对大家有帮助

The End。

 http://www.cnblogs.com/lemontea/archive/2013/02/04/2891281.html

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于利用表达式树构建委托改善反射性能的详细内容...

  阅读:41次