再看ExpressionTree,Emit,反射创建对象性能对比


【前言】

  我们要实现一个创建对象的工厂。

  new对象,Expression Tree实现(参数/不考虑参数),Emit+Delegate(考虑参数)实现方式做对比。

【实现过程】

 1     public class ExpressionCreateObject
 2     {
 3         private static Func<object> 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.Lambdaobject>>(newExpression).Compile();
10             }
11             return func() as T;
12         }
13     }

  2.有参数处理的Expression Tree方式创建对象(带参构造函数,且针对参数的委托进行了本地缓存)

 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 mHandlers[key](parameters);
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 [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.带参构造函数的单元测试

  通过本文的测试,对反射创建对象的性能有了重新的认识,在.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

出处:https://www.cnblogs.com/7tiny/p/9861166.html

Ioc