【转载】.Net中各种不同的对象创建方式的速度差异
2010-02-24 16:22 Virus-BeautyCode 阅读(386) 评论(1) 编辑 收藏 举报
本文章为本人个人博客相应文章的镜像:
原文地址: http://www.greatony.com/index.php/2010/02/06/speed-of-object-creation-in-dotnet/
在.Net中,微软给我们提供了很多不同的创建对象实例的方法,它们的速度又各有不同,以下一一列举。
使用new关键字
这在.Net中是最常见,也是速度最快的方式:
使用System.Activator类的CreateInstance方法动态创建
这里的CreateInstance指的是Activator的非泛型方法:
使用System.Activator类的CreateInstance<T>方法动态创建
这里的CreateInstance才是Activator的泛型方法:
使用泛型约束,使用new关键字创建对象(泛型方法)
首先需要创建一个泛型的方法:
2 {
3 return new T();
4 }
这里利用泛型约束where T: new(),保证了T类型是可以用无参构造器构造的,所以代码里面就可以直接使用new T()来创建对象:
使用泛型类的静态方法、泛型约束和new关键字创建
这里需要首先创建一个泛型类
2 {
3 public static T CreateInstance()
4 {
5 return new T();
6 }
7 }
然后使用如下代码创建实例:
使用泛型类的动态方法、泛型约束和new关键字
这里使用的是泛型类的实力方法,需要首先创建一个泛型类:
2 {
3 public T CreateInstance()
4 {
5 return new T();
6 }
7 }
使用的方法就是:
2 var instance = initializer.CreateInstance();
Ok,现在我一共提出了6种不同的创建对象的方式,大家可以猜猜这些创建对象的方式当中那个会比较快。
- 使用new关键字
- 使用System.Activator类的CreateInstance方法动态创建
- 使用System.Activator类的CreateInstance<T>方法动态创建
- 使用泛型约束,使用new关键字创建对象(泛型方法)
- 使用泛型类的静态方法、泛型约束和new关键字创建
- 使用泛型类的动态方法、泛型约束和new关键字
大家可以在评论里面给这些方法排个序,明天的文章我将会公布测试的结果
本文章为本人个人博客相应文章的镜像:
原文地址: http://www.greatony.com/index.php/2010/02/14/speed-of-object-creation-in-dotnet-ii/
OK,大家已经看到前面的文章了,本来说是第二天就发这篇文章的,但后来因为返回老家的原因,没有发成,特此补发。
测试环境:
- Lenovo ThinkPad T61
- CPU: Intel T7500
- Mem: 2GB
- Os: Windows Vista Ultimate (x86) sp1
测试内容:
创建100万个对象。
测试方式:
2 // create the instance
然后计算这段代码消耗的时间
测试结果:
- 使用new关键字 17ms
- 使用System.Activator类的CreateInstance方法动态创建 484ms
- 使用System.Activator类的CreateInstance<T>方法动态创建 1545ms
- 使用泛型约束,使用new关键字创建对象(泛型方法)1604ms
- 使用泛型类的静态方法、泛型约束和new关键字创建 1504ms
- 使用泛型类的动态方法、泛型约束和new关键字 1481ms
这里使用new关键字无疑是最快的,比较有趣的主要有以下2点:
- 使用System.Activator的非泛型方法比使用泛型方法快很多(超过200%)
- 使用泛型约束和new关键字创建的速度几乎和System.Activator的泛型方法的一样
那么,在下一篇文章里面,我将会探索和分析造成速度差异的原因,敬请期待。
本文章为本人个人博客相应文章的镜像:
原文地址: http://www.greatony.com/index.php/2010/02/14/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),代码如下
2 {
3 return CreateInstance(type, false);
4 }
那么这个CreateInstance(Type, Boolean)的实现,是这样的:
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
将这段代码简化一下,就是:
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),这个函数的实现非常有意思,我先把代码贴出来:
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 try
22 {
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里面干了什么事情。
照例先贴代码吧:
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<T>()干了什么事情。
第二段 从System.Activator.CreateInstance<T>()开始
这里,我们先看看他的实现:
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<T>()相当于调用了CreateInstanceSlow方法(但是没有缓存机制),这应该就是CreateInstance<T>比CreateInstance(Type)慢的主要原因,我们回顾一下这两个方法的时间消耗:
System.Activator.CreateInstance(Type):
- 缓存机制时间消耗
- RuntimeTypeHandler.Allocate()内存分配的时间消耗
- 调用构造器委托初始化数据的时间消耗
这里不考虑缓存失败,调用CreateInstanceSlow的情况,因为这个只会发生一次。
System.Activator.CreateInstance(Type):
- 调用RuntimeTypeHandler.CreateInstance的时间消耗
在下一篇文章中,我会对这两个函数的性能差异做进一步的分析