我在学习 C# 的过程中,在我放弃编程很长时间之后(早在 90 年代早期,曾经使用 C 和汇编程序编程),所以我对 C# 和 OOP 总体来说还很陌生。
我在研究单例模式及其实现时偶然发现了这种奇怪的行为。我看到有些人使用 if (==),而其他人使用 if (!=) 来检查实例是否已经创建。我想知道两者之间是否存在任何显着的性能差异(从逻辑的角度来看,它们的工作方式完全相同),经过一些测试后我发现 != 比 == 快得多(在 12% 之间)和 22%!),我真的不明白为什么。
我知道这个实现不是线程安全的,顺便说一句,我写它只是为了尝试得到满足我好奇心的答案,所以请不要为此抨击我。 :)
那么,有人对此有答案吗?具体来说,我对为什么会这样感兴趣。这是我使用的代码:
测试代码:
using System;
using System.Diagnostics;
namespace SingletonSpeedTest
{
class Program
{
static void Main(string[] args)
{
bool wantToQuit = false;
while (!wantToQuit)
{
Console.Write("Enter number of cycles: ");
UInt64 cycles = Convert.ToUInt64(Console.ReadLine());
long avg1 = Singleton1.TestSingleton(cycles);
long avg2 = Singleton2.TestSingleton(cycles);
float perc = (float) (avg2 - avg1) / avg1 * 100;
Console.WriteLine("\nNumber of ticks in Singleton with == in if: " + avg1);
Console.WriteLine("Number of ticks in Singleton with != in if: " + avg2);
Console.WriteLine("Difference in percentage is " + perc + "%");
Console.Write("\nDo you want to quit? (y/n): ");
if (Console.ReadLine() == "y") wantToQuit = true;
}
}
}
}
Singleton1 Class with == in if:
using System;
using System.Diagnostics;
namespace SingletonSpeedTest
{
public sealed class Singleton1
{
private static Singleton1 instance = null;
private Singleton1() { }
public static Singleton1 Instance()
{
if (instance == null) instance = new Singleton1();
return instance;
}
public static long TestSingleton(UInt64 cycles)
{
Stopwatch sw = Stopwatch.StartNew();
for (UInt64 i = 0; i < cycles; i++)
{
Instance();
}
sw.Stop();
return sw.ElapsedTicks;
}
}
}
在 if 中使用 != 的 Singleton2 类
using System;
using System.Diagnostics;
namespace SingletonSpeedTest
{
public sealed class Singleton2
{
private static Singleton2 instance = null;
private Singleton2() { }
public static Singleton2 Instance()
{
if (instance != null) return instance;
return instance = new Singleton2();
}
public static long TestSingleton(UInt64 cycles)
{
Stopwatch sw = Stopwatch.StartNew();
for (UInt64 i = 0; i < cycles; i++)
{
Instance();
}
sw.Stop();
return sw.ElapsedTicks;
}
}
}
最佳答案
程序编译的时候可以看到Singleton2多了一些指令,但是看到实例存在就直接跳转到最后了。这给它带来了您所看到的速度提升。
这是两个类的实例方法的 IL 代码:
单例 1:
.method public hidebysig static class SingletonSpeedTest.Singleton1
Instance() cil managed
{
// Code size 33 (0x21)
.maxstack 2
.locals init ([0] bool V_0,
[1] class SingletonSpeedTest.Singleton1 V_1)
IL_0000: nop
IL_0001: ldsfld class SingletonSpeedTest.Singleton1 SingletonSpeedTest.Singleton1::'instance'
IL_0006: ldnull
IL_0007: ceq
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: brfalse.s IL_0017
IL_000d: newobj instance void SingletonSpeedTest.Singleton1::.ctor()
IL_0012: stsfld class SingletonSpeedTest.Singleton1 SingletonSpeedTest.Singleton1::'instance'
IL_0017: ldsfld class SingletonSpeedTest.Singleton1 SingletonSpeedTest.Singleton1::'instance'
IL_001c: stloc.1
IL_001d: br.s IL_001f
IL_001f: ldloc.1
IL_0020: ret
} // end of method Singleton1::Instance
单例2:
.method public hidebysig static class SingletonSpeedTest.Singleton2
Instance() cil managed
{
// Code size 37 (0x25)
.maxstack 2
.locals init ([0] bool V_0,
[1] class SingletonSpeedTest.Singleton2 V_1)
IL_0000: nop
IL_0001: ldsfld class SingletonSpeedTest.Singleton2 SingletonSpeedTest.Singleton2::'instance'
IL_0006: ldnull
IL_0007: cgt.un
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: brfalse.s IL_0015
IL_000d: ldsfld class SingletonSpeedTest.Singleton2 SingletonSpeedTest.Singleton2::'instance'
IL_0012: stloc.1
IL_0013: br.s IL_0023
IL_0015: newobj instance void SingletonSpeedTest.Singleton2::.ctor()
IL_001a: dup
IL_001b: stsfld class SingletonSpeedTest.Singleton2 SingletonSpeedTest.Singleton2::'instance'
IL_0020: stloc.1
IL_0021: br.s IL_0023
IL_0023: ldloc.1
IL_0024: ret
} // end of method Singleton2::Instance
这是使用 MS ILDASM 工具(解释性语言反汇编程序)生成的。
关于C#单例实现性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42443725/