.Net4.0用表达式树构建委托改善反射性能
来源:互联网 发布:php数据采集器 编辑:程序博客网 时间:2024/05/17 22:44
转自: http://www.cnblogs.com/lemontea/archive/2013/02/04/2891281.html
最近搞一个系统时由于在比较关键地方用到反射了,所以要关注了一下反射的性能问题。
.Net4.0反射性能改善
看老赵的文章,老赵得到的结果是这样的:
00:00:00.0125539 (Directly invoke)00:00:04.5349626 (Reflection invoke)00:00:00.0322555 (Dynamic executor)
而我把代码搞下来自己运行得到这样的结果:
这里不是说机器性能造成绝对的时间,而是差距比例完全不一样,想了一阵想起了老赵当时应该是基于.Net3.5,果断把程序的目标框架切换到.Net3.5,结果如下:
三者的差距仍然有些不一样,老赵那边的直接调用与动态执行同一数量级的结果还是没有。但发现了另一些信息。反射和直接调用方法.Net4.0比.Net3.5有非常大的改善,特别是反射,性能提升了好几倍。反而构建表达式树动态调用的方式性能比.Net3.5差了一点。但是相对反射还是有差距,按照这个比例,写写表达式树还是值得的。00:00:00.0018801 (Directly invoke)00:00:02.4288876 (Reflection invoke)00:00:00.0141537 (Dynamic executor)
改善老赵的DynamicMethodExecutor
老赵的那篇的文章的思路是使用DynamicMethodExecutor来构造一个万能的委托Func<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)");}
结果:
00:00:00.0188422 (Dynamic executor(object))(JeffreyZhao)00:00:00.0210869 (Dynamic executor(int))(JeffreyZhao)00:00:00.0142841 (Dynamic executor(object))(zhangweiwen)00:00:00.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 < 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 < Times; i++) { IntPropertyInfo.SetValue(ProgramInstance, IntValue, null); } watch2.Stop(); Console.WriteLine(watch2.Elapsed + " (Reflection Set IntProperty)");}private static void DynamicSetValueType(){ var action = BuildSetPropertyAction<Program, int>(IntPropertyInfo); var watch1 = new Stopwatch(); watch1.Start(); for (var i = 0; i < 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 < 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 < Times; i++) { ObjectPropertyInfo.SetValue(ProgramInstance, ObjectValue, null); } watch2.Stop(); Console.WriteLine(watch2.Elapsed + " (Reflection Set ObjectProperty)");}private static void DynamicSetReferenceType(){ var action = BuildSetPropertyAction<Program, object>(ObjectPropertyInfo); //action(ProgramInstance, ObjectValue); var watch1 = new Stopwatch(); watch1.Start(); for (var i = 0; i < Times; i++) { action(ProgramInstance, ObjectValue); } watch1.Stop(); Console.WriteLine(watch1.Elapsed + " (Dynamic Set ObjectProperty)");}
结果如下:
Test Set Value:00:00:00.0003237 (Directly Set IntProperty)00:00:00.3160570 (Reflection Set IntProperty)00:00:00.0132668 (Dynamic Set IntProperty)-----00:00:00.0028183 (Directly Set ObjectProperty)00:00:00.2937783 (Reflection Set ObjectProperty)00:00:00.0150118 (Dynamic Set ObjectProperty)
虽然跟直接赋值不能比,但比反射快大概30倍。
全部代码,希望对大家有帮助.
- .Net4.0用表达式树构建委托改善反射性能
- 利用表达式树构建委托改善反射性能
- 使用表达式树提升反射性能
- 反射 表达式树 DLR 性能,效率 对比
- PivotTable_OWC+AnalysisService+Asp.net4.0构建OLAP应用的解决方案
- Lambda表达式--NET4
- 性能改善
- 改善网站性能和改善数据库性能
- 构建表达式树
- 表达式树的构建
- 构建表达式树
- 用PHP的反射实现委托模式
- 用反射修改一个私有委托
- 用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树
- 用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树
- 基于ASP.NET4.0、ExtJs技术构建酒店管理系统(送源码)
- 基于ASP.NET4.0、ExtJs技术构建酒店管理系统(更新完毕)
- 表达式树创建对像,反射等性能对比,收集资料
- ios开发——长按按出两个UIAlertView的解决办法
- 结构体指针的定义和引用
- 被玩坏的UGC模式还将持续多久?
- MyEclipse8.5的优化
- HDU 2896 病毒侵袭 Trie图
- .Net4.0用表达式树构建委托改善反射性能
- yslow V2 准则详细讲解
- andortid让字体有下划线功能如:忘记密码格式
- Android之使用Android-query框架进行开发(一)
- npm
- Redis 配置文件详细说明
- 索引写法
- python 导入挂起
- Keystone验证过程