本文主要讨论一下SingleTon的几种不同的实现的性能差异,当然这个差异本身是非常小的,即使使用了性能最差的方式,也比反射的性能要好上很多倍。
首先,来看一下几种常用的线程安全的SingleTon方案。
1、静态字段直接new自身实例,最简单的一种,其实也是我最喜欢的一种(类本身也是BeforeFieldInit的)。
1.1、静态字段,通过类型构造new自身实例(说白了,就是把类上的BeforeFieldInit去掉了,主要测试一下BeforeFieldInit对性能有多少优化)。
2、简单的Lazy初始化,就是类似:
3、利用MethodImpl的Lazy SingleTon:
4、安全的双检锁:
5、利用内部类的:
基本上主流就是这5种。
另外,还有一个安全的双检锁的个人改进版:
下面开始测试部分。
先额外做一个基础循环代价测试:
然后开始主体:
类似测试代码省略。。。
这里Count=10000000时,在我电脑上的测试结果是:
BasicLoopTime takes 16690310 ticks.
BeforeFieldInitSingleTon takes 30335900 ticks.
NonBeforeFieldInitSingleTon takes 123670239 ticks.
SimpleLazySingleTon takes 2722872152 ticks.
MethodImplLazySingleTon takes 2641823996 ticks.
SafeDoubleCheckLockSingleTon takes 243376056 ticks.
SafeDoubleCheckLockBetterSingleTon takes 195844019 ticks.
InternalClassSingleTon takes 35904596 ticks.
最快的当然是第一种方式,消耗的时间仅仅是基础循环时间的2倍不到。(非BeforeFieldInit版多花了3倍的时间。。。)
第二快的是内部类方式,兼顾了速度和Lazy。
然后是双检锁,当然改进版稍稍快一点,但是还是比非BeforeFieldInit版慢。
最后的两位难兄难弟是简单的lock+if的Lazy方式和基于MethodImpl的Lazy方式,消耗时间是第一种方式的90倍左右。
首先,来看一下几种常用的线程安全的SingleTon方案。
1、静态字段直接new自身实例,最简单的一种,其实也是我最喜欢的一种(类本身也是BeforeFieldInit的)。
1.1、静态字段,通过类型构造new自身实例(说白了,就是把类上的BeforeFieldInit去掉了,主要测试一下BeforeFieldInit对性能有多少优化)。
2、简单的Lazy初始化,就是类似:
public static SimpleLazySingleTon Instance
{
get
{
lock (_syncRoot)
{
if (_instance == null)
_instance = new SimpleLazySingleTon();
return _instance;
}
}
}
{
get
{
lock (_syncRoot)
{
if (_instance == null)
_instance = new SimpleLazySingleTon();
return _instance;
}
}
}
3、利用MethodImpl的Lazy SingleTon:
public static CompilerServicesLazySingleTon Instance
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
if (_instance == null)
_instance = new CompilerServicesLazySingleTon();
return _instance;
}
}
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
if (_instance == null)
_instance = new CompilerServicesLazySingleTon();
return _instance;
}
}
4、安全的双检锁:
public static SafeDoubleCheckLockSingleTon Instance
{
get
{
if (_instance == null)
{
lock (_syncRoot)
{
if (_instance == null)
{
SafeDoubleCheckLockSingleTon instance = new SafeDoubleCheckLockSingleTon();
Thread.MemoryBarrier();
_instance = instance;
}
}
}
return _instance;
}
}
{
get
{
if (_instance == null)
{
lock (_syncRoot)
{
if (_instance == null)
{
SafeDoubleCheckLockSingleTon instance = new SafeDoubleCheckLockSingleTon();
Thread.MemoryBarrier();
_instance = instance;
}
}
}
return _instance;
}
}
5、利用内部类的:
private sealed class InternalClass
{
public static InternalClassSingleTon Instance = new InternalClassSingleTon();
}
public static InternalClassSingleTon Instance
{
get
{
return InternalClass.Instance;
}
}
{
public static InternalClassSingleTon Instance = new InternalClassSingleTon();
}
public static InternalClassSingleTon Instance
{
get
{
return InternalClass.Instance;
}
}
基本上主流就是这5种。
另外,还有一个安全的双检锁的个人改进版:
public static SafeDoubleCheckLockBetterSingleTon Instance
{
get
{
if (_instance == null)
CreateInternal();
return _instance;
}
}
private static void CreateInternal()
{
lock (_syncRoot)
{
if (_instance == null)
{
SafeDoubleCheckLockBetterSingleTon instance = new SafeDoubleCheckLockBetterSingleTon();
Thread.MemoryBarrier();
_instance = instance;
}
}
}
{
get
{
if (_instance == null)
CreateInternal();
return _instance;
}
}
private static void CreateInternal()
{
lock (_syncRoot)
{
if (_instance == null)
{
SafeDoubleCheckLockBetterSingleTon instance = new SafeDoubleCheckLockBetterSingleTon();
Thread.MemoryBarrier();
_instance = instance;
}
}
}
下面开始测试部分。
先额外做一个基础循环代价测试:
static void BasicLoopTime()
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < Count; i++)
;
sw.Stop();
Console.WriteLine("{0} takes {1} ticks.", "BasicLoopTime", sw.ElapsedTicks);
}
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < Count; i++)
;
sw.Stop();
Console.WriteLine("{0} takes {1} ticks.", "BasicLoopTime", sw.ElapsedTicks);
}
然后开始主体:
static void BeforeFieldInitSingleTonTest()
{
Stopwatch sw = Stopwatch.StartNew();
object result;
for (int i = 0; i < Count; i++)
result = BeforeFieldInitSingleTon.Instance.Value;
sw.Stop();
Console.WriteLine("{0} takes {1} ticks.", "BeforeFieldInitSingleTon", sw.ElapsedTicks);
}
{
Stopwatch sw = Stopwatch.StartNew();
object result;
for (int i = 0; i < Count; i++)
result = BeforeFieldInitSingleTon.Instance.Value;
sw.Stop();
Console.WriteLine("{0} takes {1} ticks.", "BeforeFieldInitSingleTon", sw.ElapsedTicks);
}
类似测试代码省略。。。
这里Count=10000000时,在我电脑上的测试结果是:
BasicLoopTime takes 16690310 ticks.
BeforeFieldInitSingleTon takes 30335900 ticks.
NonBeforeFieldInitSingleTon takes 123670239 ticks.
SimpleLazySingleTon takes 2722872152 ticks.
MethodImplLazySingleTon takes 2641823996 ticks.
SafeDoubleCheckLockSingleTon takes 243376056 ticks.
SafeDoubleCheckLockBetterSingleTon takes 195844019 ticks.
InternalClassSingleTon takes 35904596 ticks.
最快的当然是第一种方式,消耗的时间仅仅是基础循环时间的2倍不到。(非BeforeFieldInit版多花了3倍的时间。。。)
第二快的是内部类方式,兼顾了速度和Lazy。
然后是双检锁,当然改进版稍稍快一点,但是还是比非BeforeFieldInit版慢。
最后的两位难兄难弟是简单的lock+if的Lazy方式和基于MethodImpl的Lazy方式,消耗时间是第一种方式的90倍左右。