码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-11-17 21:15:14      阅读:343      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   io   color   ar   os   使用   sp   

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

.Net4.0反射性能改善

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

1 00:00:00.0125539 (Directly invoke)
2 00:00:04.5349626 (Reflection invoke)
3 00:00:00.0322555 (Dynamic executor)

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

1 00:00:00.0009710 (Directly invoke)
2 00:00:00.4142893 (Reflection invoke)
3 00:00:00.0194501 (Dynamic executor)

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

1 00:00:00.0018801 (Directly invoke)
2 00:00:02.4288876 (Reflection invoke)
3 00:00:00.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强制转换为强类型的委托来得更加简单。全部代码一个方法即可,精简了许多。

 1 /// <summary>
 2 /// 动态构造委托
 3 /// </summary>
 4 /// <param name="methodInfo">方法元数据</param>
 5 /// <returns>委托</returns>
 6 public static Delegate BuildDynamicDelegate(MethodInfo methodInfo)
 7 {
 8     if (methodInfo == null)
 9         throw new ArgumentNullException("methodInfo");
10  
11     var paramExpressions = methodInfo.GetParameters().Select((p, i) =>
12     {
13         var name = "param" + (i + 1).ToString(CultureInfo.InvariantCulture);
14         return Expression.Parameter(p.ParameterType, name);
15     }).ToList();
16  
17     MethodCallExpression callExpression;
18     if (methodInfo.IsStatic)
19     {
20         //Call(params....)
21         callExpression = Expression.Call(methodInfo, paramExpressions);
22     }
23     else
24     {
25         var instanceExpression = Expression.Parameter(methodInfo.ReflectedType, "instance");
26         //insatnce.Call(params….)
27         callExpression = Expression.Call(instanceExpression, methodInfo, paramExpressions);
28         paramExpressions.Insert(0, instanceExpression);
29     }
30     var lambdaExpression = Expression.Lambda(callExpression, paramExpressions);
31     return lambdaExpression.Compile();
32 }

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

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

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

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

1 public void Call1(object o1, object o2, object o3) { }
2 public void Call2(int o1, int o2, int o3) { }
 1 private static void DynamicExecutor_ObjectType()
 2 {
 3     var executor = new DynamicMethodExecutor(Call1MethodInfo);
 4     var watch1 = new Stopwatch();
 5     watch1.Start();
 6     for (var i = 0; i < Times; i++)
 7     {
 8         executor.Execute(ProgramInstance, ObjectParameters);
 9     }
10     watch1.Stop();
11     Console.WriteLine(watch1.Elapsed + " (Dynamic executor(object))(JeffreyZhao)");
12 }
13 private static void DynamicExecutor_IntType()
14 {
15     var executor = new DynamicMethodExecutor(Call2MethodInfo);
16     var watch1 = new Stopwatch();
17     watch1.Start();
18     for (var i = 0; i < Times; i++)
19     {
20         executor.Execute(ProgramInstance, IntParameters);
21     }
22     watch1.Stop();
23     Console.WriteLine(watch1.Elapsed + " (Dynamic executor(int))(JeffreyZhao)");
24 }
25 private static void DynamicExecutor_StrongObject()
26 {
27     var action = DynamicMethodBuilder.BuildAction<Program, object, object, object>(Call1MethodInfo);
28     var watch1 = new Stopwatch();
29     watch1.Start();
30     for (var i = 0; i < Times; i++)
31     {
32         action(ProgramInstance, ObjectParameters[0], ObjectParameters[1], ObjectParameters[2]);
33     }
34     watch1.Stop();
35     Console.WriteLine(watch1.Elapsed + " (Dynamic executor(object))(zhangweiwen)");
36 }
37  
38 private static void DynamicExecutor_StrongInt()
39 {
40     var action = DynamicMethodBuilder.BuildAction<Program, int, int, int>(Call2MethodInfo);
41     var watch1 = new Stopwatch();
42     watch1.Start();
43     for (var i = 0; i < Times; i++)
44     {
45         action(ProgramInstance, IntParameters1[0], IntParameters1[1], IntParameters1[2]);
46     }
47     watch1.Stop();
48     Console.WriteLine(watch1.Elapsed + " (Dynamic executor(int))(zhangweiwen)");
49 }

结果:

1 00:00:00.0188422 (Dynamic executor(object))(JeffreyZhao)
2 00:00:00.0210869 (Dynamic executor(int))(JeffreyZhao)
3 00:00:00.0142841 (Dynamic executor(object))(zhangweiwen)
4 00:00:00.0147589 (Dynamic executor(int))(zhangweiwen)

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

构建委托动态赋值

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

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

 1 private static Action<TInstance, TProperty> BuildSetPropertyAction<TInstance, TProperty>(PropertyInfo propertyInfo)
 2 {
 3     var instanceParam = Expression.Parameter(typeof(TInstance), "instance");
 4     var valueParam = Expression.Parameter(typeof(TProperty), "value");
 5     //instance.Property
 6     var propertyProperty = Expression.Property(instanceParam, propertyInfo);
 7     //instance.Property = value
 8     var assignExpression = Expression.Assign(propertyProperty, valueParam);
 9     var lambdaExpression = Expression.Lambda<Action<TInstance, TProperty>>(assignExpression, instanceParam, valueParam);
10     return lambdaExpression.Compile();
11 }

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

1 var action = BuildSetPropertyAction<Program, object>(ObjectPropertyInfo);
2 action(ProgramInstance, ObjectValue);

来测试一下性能:

 1 private static void DirectlySetValueType()
 2 {
 3     var watch1 = new Stopwatch();
 4     watch1.Start();
 5     for (var i = 0; i &lt; Times; i++)
 6     {
 7         ProgramInstance.IntProperty = IntValue;
 8     }
 9     watch1.Stop();
10     Console.WriteLine(watch1.Elapsed + " (Directly Set IntProperty)");
11 }
12  
13 private static void ReflectionSetValueType()
14 {
15     var watch2 = new Stopwatch();
16     watch2.Start();
17     for (var i = 0; i &lt; Times; i++)
18     {
19         IntPropertyInfo.SetValue(ProgramInstance, IntValue, null);
20     }
21     watch2.Stop();
22     Console.WriteLine(watch2.Elapsed + " (Reflection Set IntProperty)");
23 }
24  
25 private static void DynamicSetValueType()
26 {
27     var action = BuildSetPropertyAction&lt;Program, int&gt;(IntPropertyInfo);
28     var watch1 = new Stopwatch();
29     watch1.Start();
30     for (var i = 0; i &lt; Times; i++)
31     {
32         action(ProgramInstance, IntValue);
33     }
34     watch1.Stop();
35     Console.WriteLine(watch1.Elapsed + " (Dynamic Set IntProperty)");
36 }
37  
38 private static void DirectlySetReferenceType()
39 {
40     var watch1 = new Stopwatch();
41     watch1.Start();
42     for (var i = 0; i &lt; Times; i++)
43     {
44         ProgramInstance.ObjectProperty = ObjectValue;
45     }
46     watch1.Stop();
47     Console.WriteLine(watch1.Elapsed + " (Directly Set ObjectProperty)");
48 }
49  
50 private static void ReflectionSetReferenceType()
51 {
52     var watch2 = new Stopwatch();
53     watch2.Start();
54     for (var i = 0; i &lt; Times; i++)
55     {
56         ObjectPropertyInfo.SetValue(ProgramInstance, ObjectValue, null);
57     }
58     watch2.Stop();
59     Console.WriteLine(watch2.Elapsed + " (Reflection Set ObjectProperty)");
60 }
61  
62 private static void DynamicSetReferenceType()
63 {
64     var action = BuildSetPropertyAction&lt;Program, object&gt;(ObjectPropertyInfo);
65     //action(ProgramInstance, ObjectValue);
66     var watch1 = new Stopwatch();
67     watch1.Start();
68     for (var i = 0; i &lt; Times; i++)
69     {
70         action(ProgramInstance, ObjectValue);
71     }
72     watch1.Stop();
73     Console.WriteLine(watch1.Elapsed + " (Dynamic Set ObjectProperty)");
74 }

结果如下:

1 Test Set Value:
2 00:00:00.0003237 (Directly Set IntProperty)
3 00:00:00.3160570 (Reflection Set IntProperty)
4 00:00:00.0132668 (Dynamic Set IntProperty)
5 -----
6 00:00:00.0028183 (Directly Set ObjectProperty)
7 00:00:00.2937783 (Reflection Set ObjectProperty)
8 00:00:00.0150118 (Dynamic Set ObjectProperty)

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

全部代码

希望对大家有帮助

The End。

===重要更新===

我把上面的方法用在项目中才发现陷入了一个悖论。就是往往需要使用反射的上下文中是没有实例类型的,而有了实例类型的上下文中有往往不需要反射。所以构建表达式树是需要去掉实例的类型参数,在表达式树中做类型转换,这样会有一点点的性能损失,但同时又带来一个好处,使用时类型参数少了一个,写起来方便了一点。两个主要代码如下:

 1 /// <summary>
 2 /// 动态构造委托
 3 /// </summary>
 4 /// <param name="methodInfo">方法元数据</param>
 5 /// <returns>委托</returns>
 6 public static Delegate BuildDynamicDelegate(MethodInfo methodInfo)
 7 {
 8     if (methodInfo == null)
 9         throw new ArgumentNullException("methodInfo");
10  
11     var paramExpressions = methodInfo.GetParameters().Select((p, i) =>
12     {
13         var name = "param" + (i + 1).ToString(CultureInfo.InvariantCulture);
14         return Expression.Parameter(p.ParameterType, name);
15     }).ToList();
16  
17     MethodCallExpression callExpression;
18     if (methodInfo.IsStatic)
19     {
20         //Call(params....)
21         callExpression = Expression.Call(methodInfo, paramExpressions);
22     }
23     else
24     {
25         var instanceExpression = Expression.Parameter(typeof(object), "instance");
26         //((T)instance)
27         var castExpression = Expression.Convert(instanceExpression, methodInfo.ReflectedType);
28         //((T)instance).Call(params)
29         callExpression = Expression.Call(castExpression, methodInfo, paramExpressions);
30         paramExpressions.Insert(0, instanceExpression);
31     }
32     var lambdaExpression = Expression.Lambda(callExpression, paramExpressions);
33     return lambdaExpression.Compile();
34 }
35  
36 //使用
37 public static Action<object> BuildAction(MethodInfo methodInfo)
38 {
39     return (Action<object>)BuildDynamicDelegate(methodInfo);
40 }
41  
42 public static Action<object, T1> BuildAction<T1>(MethodInfo methodInfo)
43 {
44     return (Action<object, T1>)BuildDynamicDelegate(methodInfo);
45 }
 1 /// <summary>
 2 /// 动态构造赋值委托
 3 /// </summary>
 4 /// <typeparam name="TProperty">属性类型</typeparam>
 5 /// <param name="propertyInfo">属性元数据</param>
 6 /// <returns>强类型委托</returns>
 7 public static Action<object, TProperty> BuildSetPropertyAction<TProperty>(PropertyInfo propertyInfo)
 8 {
 9     var instanceParam = Expression.Parameter(typeof(object), "instance");
10     var valueParam = Expression.Parameter(typeof(TProperty), "value");
11     //((T)instance)
12     var castExpression = Expression.Convert(instanceParam, propertyInfo.ReflectedType);
13     //((T)instance).Property
14     var propertyProperty = Expression.Property(castExpression, propertyInfo);
15     //((T)instance).Property = value
16     var assignExpression = Expression.Assign(propertyProperty, valueParam);
17     var lambdaExpression = Expression.Lambda<Action<object, TProperty>>(assignExpression, instanceParam, valueParam);
18     return lambdaExpression.Compile();
19 }
20  
21 //使用
22 var action = BuildSetPropertyAction<int>(IntPropertyInfo);
23 action(ProgramInstance, IntValue);

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

标签:style   blog   http   io   color   ar   os   使用   sp   

原文地址:http://www.cnblogs.com/XingchenStudio/p/4104352.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!