1. 首页 > 电脑教程 > .Net中各种不同的对象创建方式的速度差异(四)

.Net中各种不同的对象创建方式的速度差异(四)

在这片文章中,我们暂时放一放Activator.CreateInstance(Type)和Activator.CreateInstance()之间的性能差异,去探索一下,为什么使用泛型约束的速度和CreateInstance()差不多(用屁股都能猜到应该是直接调用了CreateInstance())。

首先我们写一个小程序来验证我们的猜想:

1 using System;2 3 namespace GenericNew4 {5 public class Program6 {7 public static void Main(string[] args)8 {9 CreateInstance();10 new Program();11 }12 13 public static T CreateInstance() where T: new()14 {15 return new T();16 }17 }18 }

编译了以后用reflector来查看编译的结果:

1 public static T CreateInstance() where T: new()2 {3 return new T();4 }5

看起来没有什么问题啊,跟写的时候一样美妙,也没有见到System.Activator.CreateInstance()的踪影。

那么,让我们切换到msil模式看看:

1 .method public hidebysig static !!T CreateInstance<.ctor T>() cil managed2 {3 .maxstack 24 .locals init (5 [0] !!T local,6 [1] !!T local2)7 L_0000: nop 8 L_0001: ldloca.s local29 L_0003: initobj !!T10 L_0009: ldloc.1 11 L_000a: box !!T12 L_000f: brfalse.s L_001c13 L_0011: ldloca.s local214 L_0013: initobj !!T15 L_0019: ldloc.1 16 L_001a: br.s L_002117 L_001c: call !!0 [mscorlib]System.Activator::CreateInstance()18 L_0021: stloc.0 19 L_0022: br.s L_002420 L_0024: ldloc.0 21 L_0025: ret 22 }

恩,发现[mscorlib]System.Activator::CreateInstance()了,通过分析这段代码,我们会发现,其实ms对于这个的性能还是做了一定的优化的,这段代码:

试图采用值类型的初始化方法初始化对象,并对该对象进行装箱操作装箱失败的情况下,调用System.Activator.CreateInstance()来创建对象

最后再返回创建好的对象。

那么是不是.net本来就是通过这种方式来创建对象的呢?

我们来对比一下new Program()这条语句的编译结果:

1 newobj instance void GenericNew.Program::.ctor()

那么为什么c#编译器没有采用这个指令来创建泛型类的实例呢?

我们需要参考一下msdn上对于newobj的用法的定义:

http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.newobj(VS.85).aspx

在这里,我们看到,newobj指令的使用方法是 newobj

也就是说,它的参数是构造器,而非类型本身; 原因就在于,.Net允许有不同参数的构造器存在,只有指定了构造器,clr才知道要使用哪个构造器来初始化对象。

而,对于一个泛型类,在编译器是无法知道它的泛型参数的构造器信息的,自然就没有办法使用newobj指令了。

这就是事实的真相,在下一篇文章中,我们将去探究,事实的终极真相。

posted @ 2010-02-24 12:58 TonyHuang 阅读(14) | 评论 (0) | 编辑

.Net中各种不同的对象创建方式的速度差异(三) 本文章为本人个人博客相应文章的镜像:

原文地址: http://www.greatony.com/index.php/2010/02/20/speed-of-object-creation-in-dotnet-iii/

从前面的文章,我们发现以下两点有趣的东西:

使用System.Activator的非泛型方法比使用泛型方法快很多(超过200%)使用泛型约束和new关键字创建的速度几乎和System.Activator的泛型方法的一样

在这篇文章里,我将会这两个问题做一个进一步的探究,我使用的工具就是鼎鼎大名的.Net反编译工具:Reflector,欢迎读者跟我一起探讨造成这个现象的原因。

第一段 从System.Activator.CreateInstance(Type)开始

我们先用Reflector打开.net framework 3.5中的mscorlib.dll,看看这里面,微软是怎么实现的。

首先看看System.Activator.CreateInstance(Type),它直接调用了System.Activator.CreateInstance(Type, Boolean),代码如下

1 public static object CreateInstance(Type type)2 {3 return CreateInstance(type, false);4 }

那么这个CreateInstance(Type, Boolean)的实现,是这样的:

1 public static object CreateInstance(Type type, bool nonPublic)2 {3 if (type == null)4 {5 throw new ArgumentNullException("type");6 }7 RuntimeType underlyingSystemType = type.UnderlyingSystemType as RuntimeType;8 if (underlyingSystemType == null)9 {10 throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "type");11 }12 return underlyingSystemType.CreateInstanceImpl(!nonPublic);13 }14

将这段代码简化一下,就是:

1 public static object CreateInstance(Type type, bool nonPublic)2 {3 RuntimeType underlyingSystemType = type.UnderlyingSystemType as RuntimeType;4 return underlyingSystemType.CreateInstanceImpl(!nonPublic);5 }6

在RuntimeType的CreateInstanceImpl(bool isPublic)中,直接调用了CreateInstanceImpl(bool isPublic, bool skipVisibilityCheck, bool fillCache),这个函数的实现非常有意思,我先把代码贴出来:

1 internal object CreateInstanceImpl(bool publicOnly, bool skipVisibilityChecks, bool fillCache)2 {3 RuntimeTypeHandle typeHandle = this.TypeHandle;4 ActivatorCache cache = s_ActivatorCache;5 if (cache != null)6 {7 ActivatorCacheEntry entry = cache.GetEntry(this);8 if (entry != null)9 {10 if ((publicOnly && (entry.m_ctor != null)) && ((entry.m_hCtorMethodHandle.GetAttributes() & MethodAttributes.MemberAccessMask) != MethodAttributes.Public))11 {12 throw new MissingMethodException(Environment.GetResourceString("Arg_NoDefCTor"));13 }14 object obj2 = typeHandle.Allocate();15 if (entry.m_ctor != null)16 {17 if (!skipVisibilityChecks && entry.m_bNeedSecurityCheck)18 {19 MethodBase.PerformSecurityCheck(obj2, entry.m_hCtorMethodHandle, this.TypeHandle.Value, 0x10000000);20 }21 try22 {23 entry.m_ctor(obj2);24 }25 catch (Exception exception)26 {27 throw new TargetInvocationException(exception);28 }29 }30 return obj2;31 }32 }33 return this.CreateInstanceSlow(publicOnly, fillCache);34 }35

看起来非常复杂,其实他的实现也也就实现了一个缓存机制:

检查缓存中是否存在这个构造器的委托,如果有,就调用自己的typeHandler的Allocate()方法分配内存,然后调用构造器的委托初始化对象如果没有缓存,就调用CreateInstanceSlow(bool isPublic, bool fillCache)创建对象,并填充缓存

好吧继续再看看这个CreateInstanceSlow里面干了什么事情。

照例先贴代码吧:

1 private object CreateInstanceSlow(bool publicOnly, bool fillCache)2 {3 RuntimeMethodHandle emptyHandle = RuntimeMethodHandle.EmptyHandle;4 bool bNeedSecurityCheck = true;5 bool canBeCached = false;6 bool noCheck = false;7 this.CreateInstanceCheckThis();8 if (!fillCache)9 {10 noCheck = true;11 }12 object obj2 = RuntimeTypeHandle.CreateInstance(this, publicOnly, noCheck, ref canBeCached, ref emptyHandle, ref bNeedSecurityCheck);13 if (canBeCached && fillCache)14 {15 ActivatorCache cache = s_ActivatorCache;16 if (cache == null)17 {18 cache = new ActivatorCache();19 Thread.MemoryBarrier();20 s_ActivatorCache = cache;21 }22 ActivatorCacheEntry ace = new ActivatorCacheEntry(this, emptyHandle, bNeedSecurityCheck);23 Thread.MemoryBarrier();24 cache.SetEntry(ace);25 }26 return obj2;27 }28

这个函数写的很复杂,其实实现的东西很简单,其一是调用RuntimeTypeHandler.CreateInstance方法创建对象,然后再填充缓存,以加快下次创建对象的速度。

好了,我们现在已经非常接近事实的真相了。让我们从另外一个角度出发,看看CreateInstance()干了什么事情。

第二段 从System.Activator.CreateInstance()开始

这里,我们先看看他的实现:

1 public static T CreateInstance()2 {3 bool bNeedSecurityCheck = true;4 bool canBeCached = false;5 RuntimeMethodHandle emptyHandle = RuntimeMethodHandle.EmptyHandle;6 return (T) RuntimeTypeHandle.CreateInstance(typeof(T) as RuntimeType, true, true, ref canBeCached, ref emptyHandle, ref bNeedSecurityCheck);7 }8

我们忽然就看到了我们熟悉的身影:RuntimeTypeHandler.CreateInstance方法,终于殊途同归啊。。。

也就是说,System.Activator.CreateInstance()相当于调用了CreateInstanceSlow方法(但是没有缓存机制),这应该就是CreateInstance比CreateInstance(Type)慢的主要原因,我们回顾一下这两个方法的时间消耗:

System.Activator.CreateInstance(Type):

缓存机制时间消耗RuntimeTypeHandler.Allocate()内存分配的时间消耗调用构造器委托初始化数据的时间消耗

这里不考虑缓存失败,调用CreateInstanceSlow的情况,因为这个只会发生一次。

System.Activator.CreateInstance(Type):

调用RuntimeTypeHandler.CreateInstance的时间消耗

在下一篇文章中,我会对这两个函数的性能差异做进一步的分析

声明:希维路由器教程网提供的内容,仅供网友学习交流,如有侵权请与我们联系删除,谢谢。ihuangque@qq.com
本文地址:https://www.ctrlcv.com.cn/diannao/169348684411113.html