ExpressionTree,Emit,反射
ExpressionTree,Emit,反射
https://www.cnblogs.com/7tiny/p/9861166.html
【前言】
前几日心血来潮想研究着做一个Spring框架,自然地就涉及到了Ioc容器对象创建的问题,研究怎么高性能地创建一个对象。第一联想到了Emit,兴致冲冲写了个Emit创建对象的工厂。在做性能测试的时候,发现居然比反射Activator.CreateInstance方法创建对象毫无优势可言。继而又写了个Expression Tree的对象工厂,发现和Emit不相上下,比起系统反射方法仍然无优势可言。
第一时间查看了园内大神们的研究,例如:
Leven 的 探究.net对象的创建,质疑《再谈Activator.CreateInstance(Type type)方法创建对象和Expression Tree创建对象性能的比较》
Will Meng 的 再谈Activator.CreateInstance(Type type)方法创建对象和Expression Tree创建对象性能的比较(更新版)
详细对比了后发现,上述大佬们的对比都是使用无参构造函数做的性能对比。
于是,我也用Expression Tree写了个无参构造函数的Demo,对比之下发现,无参构造Expression Tree实现方式确实比反射Activator.CreateInstance方法性能要高很多,但是如果想要兼容带参的对象创建,在参数判断,方法缓存上来说,耗费了很多的时间,性能并不比直接反射调用好,下面放出测试的代码,欢迎博友探讨,雅正。
【实现功能】
我们要实现一个创建对象的工厂。
new对象,Expression Tree实现(参数/不考虑参数),Emit+Delegate(考虑参数)实现方式做对比。
【实现过程】
准备好测试的对象:
准备两个类,ClassA,ClassB,其中ClassA有ClassB的参数构造,ClassB无参构造。
复制代码
1 public class ClassA
2 {
3 public ClassA(ClassB classB) { }
4 public int GetInt() => default(int);
5 }
6 public class ClassB
7 {
8 public int GetInt() => default(int);
9 }
复制代码
1.最简单不考虑参数的 Expression Tree方式创建对象(无带参构造函数)
复制代码
1 public class ExpressionCreateObject
2 {
3 private static Func func;
4 public static T CreateInstance() where T : class
5 {
6 if (func == null)
7 {
8 var newExpression = Expression.New(typeof(T));
9 func = Expression.Lambda<Func>(newExpression).Compile();
10 }
11 return func() as T;
12 }
13 }
复制代码
2.有参数处理的Expression Tree方式创建对象(带参构造函数,且针对参数的委托进行了本地缓存)
复制代码
1 public class ExpressionCreateObjectFactory
2 {
3 private static Dictionary<string, Func<object[], object>> funcDic = new Dictionary<string, Func<object[], object>>();
4 public static T CreateInstance() where T : class
5 {
6 return CreateInstance(typeof(T), null) as T;
7 }
8
9 public static T CreateInstance(params object[] parameters) where T : class
10 {
11 return CreateInstance(typeof(T), parameters) as T;
12 }
13
14 static Expression[] buildParameters(Type[] parameterTypes, ParameterExpression paramExp)
15 {
16 List list = new List();
17 for (int i = 0; i < parameterTypes.Length; i++)
18 {
19 //从参数表达式(参数是:object[])中取出参数
20 var arg = BinaryExpression.ArrayIndex(paramExp, Expression.Constant(i));
21 //把参数转化成指定类型
22 var argCast = Expression.Convert(arg, parameterTypes[i]);
23
24 list.Add(argCast);
25 }
26 return list.ToArray();
27 }
28
29 public static object CreateInstance(Type instanceType, params object[] parameters)
30 {
31
32 Type[] ptypes = new Type[0];
33 string key = instanceType.FullName;
34
35 if (parameters != null && parameters.Any())
36 {
37 ptypes = parameters.Select(t => t.GetType()).ToArray();
38 key = string.Concat(key, "_", string.Concat(ptypes.Select(t => t.Name)));
39 }
40
41 if (!funcDic.ContainsKey(key))
42 {
43 ConstructorInfo constructorInfo = instanceType.GetConstructor(ptypes);
44
45 //创建lambda表达式的参数
46 var lambdaParam = Expression.Parameter(typeof(object[]), "args");
47
48 //创建构造函数的参数表达式数组
49 var constructorParam = buildParameters(ptypes, lambdaParam);
50
51 var newExpression = Expression.New(constructorInfo, constructorParam);
52
53 funcDic.Add(key, Expression.Lambda<Func<object[], object>>(newExpression, lambdaParam).Compile());
54 }
55 return funcDickey;
56 }
57 }
复制代码
3.有参数处理的 Emit+Delegate 方式创建对象(带参构造函数,且针对参数Delegate本地缓存)
复制代码
1 namespace SevenTiny.Bantina
2 {
3 internal delegate object CreateInstanceHandler(object[] parameters);
4
5 public class CreateObjectFactory
6 {
7 static Dictionary<string, CreateInstanceHandler> mHandlers = new Dictionary<string, CreateInstanceHandler>();
8
9 public static T CreateInstance() where T : class
10 {
11 return CreateInstance(null);
12 }
13
14 public static T CreateInstance(params object[] parameters) where T : class
15 {
16 return (T)CreateInstance(typeof(T), parameters);
17 }
18
19 public static object CreateInstance(Type instanceType, params object[] parameters)
20 {
21 Type[] ptypes = new Type[0];
22 string key = instanceType.FullName;
23
24 if (parameters != null && parameters.Any())
25 {
26 ptypes = parameters.Select(t => t.GetType()).ToArray();
27 key = string.Concat(key, "", string.Concat(ptypes.Select(t => t.Name)));
28 }
29
30 if (!mHandlers.ContainsKey(key))
31 {
32 CreateHandler(instanceType, key, ptypes);
33 }
34 return mHandlerskey;
35 }
36
37 static void CreateHandler(Type objtype, string key, Type[] ptypes)
38 {
39 lock (typeof(CreateObjectFactory))
40 {
41 if (!mHandlers.ContainsKey(key))
42 {
43 DynamicMethod dm = new DynamicMethod(key, typeof(object), new Type[] { typeof(object[]) }, typeof(CreateObjectFactory).Module);
44 ILGenerator il = dm.GetILGenerator();
45 ConstructorInfo cons = objtype.GetConstructor(ptypes);
46
47 if (cons == null)
48 {
49 throw new MissingMethodException("The constructor for the corresponding parameter was not found");
50 }
51
52 il.Emit(OpCodes.Nop);
53
54 for (int i = 0; i < ptypes.Length; i++)
55 {
56 il.Emit(OpCodes.Ldarg_0);
57 il.Emit(OpCodes.Ldc_I4, i);
58 il.Emit(OpCodes.Ldelem_Ref);
59 if (ptypes[i].IsValueType)
60 il.Emit(OpCodes.Unbox_Any, ptypes[i]);
61 else
62 il.Emit(OpCodes.Castclass, ptypes[i]);
63 }
64
65 il.Emit(OpCodes.Newobj, cons);
66 il.Emit(OpCodes.Ret);
67 CreateInstanceHandler ci = (CreateInstanceHandler)dm.CreateDelegate(typeof(CreateInstanceHandler));
68 mHandlers.Add(key, ci);
69 }
70 }
71 }
72 }
73 }
复制代码
【系统测试】
我们编写单元测试代码对上述几个代码段进行性能测试:
1.无参构造函数的单元测试
复制代码
1 [Theory]
2 [InlineData(1000000)]
3 [Trait("description", "无参构造各方法调用性能对比")]
4 public void PerformanceReportWithNoArguments(int count)
5 {
6 Trace.WriteLine($"#{count} 次调用:");
7
8 double time = StopwatchHelper.Caculate(count, () =>
9 {
10 ClassB b = new ClassB();
11 }).TotalMilliseconds;
12 Trace.WriteLine($"‘New’耗时 {time} milliseconds");
13
14 double time2 = StopwatchHelper.Caculate(count, () =>
15 {
16 ClassB b = CreateObjectFactory.CreateInstance();
17 }).TotalMilliseconds;
18 Trace.WriteLine($"‘Emit 工厂’耗时 {time2} milliseconds");
19
20 double time3 = StopwatchHelper.Caculate(count, () =>
21 {
22 ClassB b = ExpressionCreateObject.CreateInstance();
23 }).TotalMilliseconds;
24 Trace.WriteLine($"‘Expression’耗时 {time3} milliseconds");
25
26 double time4 = StopwatchHelper.Caculate(count, () =>
27 {
28 ClassB b = ExpressionCreateObjectFactory.CreateInstance();
29 }).TotalMilliseconds;
30 Trace.WriteLine($"‘Expression 工厂’耗时 {time4} milliseconds");
31
32 double time5 = StopwatchHelper.Caculate(count, () =>
33 {
34 ClassB b = Activator.CreateInstance();
35 //ClassB b = Activator.CreateInstance(typeof(ClassB)) as ClassB;
36 }).TotalMilliseconds;
37 Trace.WriteLine($"‘Activator.CreateInstance’耗时 {time5} milliseconds");
38
39
40 /**
41 #1000000 次调用:
42 ‘New’耗时 21.7474 milliseconds
43 ‘Emit 工厂’耗时 174.088 milliseconds
44 ‘Expression’耗时 42.9405 milliseconds
45 ‘Expression 工厂’耗时 162.548 milliseconds
46 ‘Activator.CreateInstance’耗时 67.3712 milliseconds
47 * */
48 }
复制代码
通过上面代码测试可以看出,100万次调用,相比直接New对象,Expression无参数考虑的实现方式性能最高,比系统反射Activator.CreateInstance的方法性能要高。
这里没有提供Emit无参的方式实现,看这个性能测试的结果,预估Emit无参的实现方式性能会比系统反射的性能要高的。
2.带参构造函数的单元测试
复制代码
1 [Theory]
2 [InlineData(1000000)]
3 [Trait("description", "带参构造各方法调用性能对比")]
4 public void PerformanceReportWithArguments(int count)
5 {
6 Trace.WriteLine($"#{count} 次调用:");
7
8 double time = StopwatchHelper.Caculate(count, () =>
9 {
10 ClassA a = new ClassA(new ClassB());
11 }).TotalMilliseconds;
12 Trace.WriteLine($"‘New’耗时 {time} milliseconds");
13
14 double time2 = StopwatchHelper.Caculate(count, () =>
15 {
16 ClassA a = CreateObjectFactory.CreateInstance(new ClassB());
17 }).TotalMilliseconds;
18 Trace.WriteLine($"‘Emit 工厂’耗时 {time2} milliseconds");
19
20 double time4 = StopwatchHelper.Caculate(count, () =>
21 {
22 ClassA a = ExpressionCreateObjectFactory.CreateInstance(new ClassB());
23 }).TotalMilliseconds;
24 Trace.WriteLine($"‘Expression 工厂’耗时 {time4} milliseconds");
25
26 double time5 = StopwatchHelper.Caculate(count, () =>
27 {
28 ClassA a = Activator.CreateInstance(typeof(ClassA), new ClassB()) as ClassA;
29 }).TotalMilliseconds;
30 Trace.WriteLine($"‘Activator.CreateInstance’耗时 {time5} milliseconds");
31
32
33 /**
34 #1000000 次调用:
35 ‘New’耗时 29.3612 milliseconds
36 ‘Emit 工厂’耗时 634.2714 milliseconds
37 ‘Expression 工厂’耗时 620.2489 milliseconds
38 ‘Activator.CreateInstance’耗时 588.0409 milliseconds
39 * */
40 }
复制代码
通过上面代码测试可以看出,100万次调用,相比直接New对象,系统反射Activator.CreateInstance的方法性能最高,而Emit实现和ExpressionTree的实现方法就要逊色一筹。
【总结】
通过本文的测试,对反射创建对象的性能有了重新的认识,在.netframework低版本中,反射的性能是没有现在这么高的,但是经过微软的迭代升级,目前最新版本的反射调用性能还是比较客观的,尤其是突出在了针对带参数构造函数的对象创建上,有机会对内部实现做详细分析。
无参构造无论是采用Expression Tree缓存委托还是Emit直接实现,都无需额外的判断,也并未使用反射,性能比系统反射要高是可以预见到的。但是加入了各种参数的判断以及针对不同参数的实现方式的缓存之后,性能却被反射反超,因为参数的判断以及缓存时Key的生成,Map集合的存储键值判断等都是有耗时的,综合下来,并不比反射好。
系统的性能瓶颈往往并不在反射或者不反射这些创建对象方法的损耗上,经过测试可以发现,即便使用反射创建,百万次的调用耗时也不到1s,但是百万次的系统调用往往耗时是比较长的,我们做测试的目的仅仅是为了探索,具体在框架的实现中,会着重考虑框架的易用性,容错性等更为关键的部分。
声明:并不是对园内大佬有啥质疑,个人认为仅仅是对以往测试的一种测试用例的补充,如果对测试过程有任何异议或者优化的部分,欢迎评论区激起波涛~!~~
【源码地址】
本文源代码地址:https://github.com/sevenTiny/SevenTiny.Bantina/blob/master/10-Code/Test.SevenTiny.Bantina/CreateObjectFactoryTest.cs
或者直接clone代码查看项目:https://github.com/sevenTiny/SevenTiny.Bantina
【版权声明】
本文为七小站主原创作品,转载请注明出处:http://www.cnblogs.com/7tiny/ 且在文章页面明显位置给出原文链接。
ExpressionTree,Emit,反射的更多相关文章
- C#利用Emit反射实现AOP,以及平台化框架封装思路
C#利用Emit反射实现AOP,以及平台化框架封装思路 这是前两天扒的一段动态代理AOP代码,用的Emit反射生成子类来实现代理模式,在这里做个小笔记,然后讨论一下AOP框架的实现思路. 首先是主函数 ...
- ExpressionTree——让反射性能向硬编码看齐
缘起 最近又换了工作.然后开心是以后又能比较频繁的关注博客园了.办离职手续的这一个月梳理了下近一年自己写的东西,然后就有了此文以及附带的代码. 反射 关于反射,窃以为,他只是比较慢.在这个前提下,个人 ...
- 什么是Emit,什么是反射,二者区别到底是什么?(转)
Emit的准确定义,我们看看微软给出的答案 System.Reflection.Emit 命名空间包含{ 允许编译器或工具发出元数据和发出 Microsoft 中间语言 (MSIL) ,并可选择在磁盘 ...
- C# 使用Emit实现动态AOP框架 (一)
目 录 C# 使用Emit实现动态AOP框架 (一) C# 使用Emit实现动态AOP框架 (二) C# 使用Emit实现动态AOP框架 (三) C# 使用Emit实现动态AOP框架 进阶篇之异常处 ...
- Dapper.NET——轻量ORM
Dapper.NET使用 http://www.cnblogs.com/yankliu-vip/p/4182892.html 本文目录 Dapper.NET使用 1.为什么选择Dapper 2.以Da ...
- 搭建一套自己实用的.net架构(3)【ORM-Dapper+DapperExtensions】
现在成熟的ORM比比皆是,这里只介绍Dapper的使用(最起码我在使用它,已经运用到项目中,小伙伴们反馈还可以). 优点: 1.开源.轻量.小巧.上手容易. 2.支持的数据库还蛮多的, Mysql,S ...
- Dapper.Net 应用
Dapper应用 1.Dapper是什么 Dapper是一款轻量级ORM工具.如果你在小的项目中,使用Entity Framework.NHibernate 来处理大数据访问及关系映射,未免有点杀鸡用 ...
- Dapper
前一段做一个技术分享关于dapper的,现在再总结一下,也好长时间没有更新博客了--,用到的东西 Dapper.AutoFac .AutoMapper.FluentValidation: 下面说一下D ...
- 分享自己的超轻量级高性能ORM数据访问框架Deft
Deft 简介 Deft是一个超轻量级高性能O/R mapping数据访问框架,简单易用,几分钟即可上手. Deft包含如下但不限于此的特点: 1.按照Transact-SQL的语法语义风格来设计,只 ...
- Dapper学习笔记(1)-开始
Dapper是一款开源的轻量级ORM工具,源代码下载地址为https://github.com/StackExchange/dapper-dot-net,其具有以下特点: 1.Dapper是一个轻型的 ...
随机推荐
- CPU与GPU区别 通俗易懂
转:https://blog.csdn.net/xiaolang85/article/details/51500340 有网友在网上提问:“为什么现在更多需要用的是 GPU 而不是 CPU,比如挖矿甚 ...
- uvm的sequence
1,每个sequence都有一个body任务.当一个sequence启动后,会自动执行sequence的body任务,所以在sequence的class中,一定要有一个名为body的task. 此外, ...
- React之JSX语法
1. JSX的介绍 JSX(JavaScript XML)——一种在React组件内部构建标签的类XML语法.react在不使用JSX的情况下一样可以工作,然而使用JSX可以提高组件的可读性,因此 ...
- H3C交换机配置镜像端口
配置步骤 进入配置模式:system-view: 创建本地镜像组:mirroring-group 1 local 为镜像组配置源端口:mirroring-group 1 mirroring-port ...
- Spring AOP(6)-- XML配置
applicationContext-xml.xml <?xml version="1.0" encoding="UTF-8"?><beans ...
- Mybatis导入原生配置文件
在Spring-Mybatis中导入Mybatis原生配置文件 在sqlSessionFactory Bean中设置设置configLocation属性 <property name=" ...
- 牛的障碍Cow Steeplechase
题目描述 Farmer John has a brilliant idea for the next great spectator sport: Cow Steeplechase! As every ...
- Java IO操作——数据操作流DataOutputStream和DataInputStream的使用
学习目标 掌握DataOutputStream和DataInputStream的作用 可以使用DataOutputStream和DataInputStream写入和读入数据 数据操作流 在io包中, ...
- Java Collections Framework Java集合框架概览
Java SE documents -- The Collections Framework http://docs.oracle.com/javase/8/docs/technotes/guides ...
- BZOJ 3329 Xorequ:数位dp + 矩阵快速幂
传送门 题意 现有如下方程:$ x \oplus 3x = 2x $ 其中 $ \oplus $ 表示按位异或. 共 $ T $ 组数据,每组数据给定正整数 $ n $,任务如下: 求出小于等于 $ ...