C#单例实现性能

标签 c# performance singleton

我在学习 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/

相关文章:

iphone - 从 NSKeyedArchiver 加载单例状态

c# - C# 结构应该如何通过网络发送到 C++ 客户端?

c# - 如何在 ASP.NET Core MVC 的 ViewComponent 中传递超过 4 个参数

c# - 将 lambda 表达式用于事件处理程序

c++ - 如何在不检查每个元素的情况下立即检查结构的零值?

java - 使用双重检查阻塞方法实例化单例

.net - 单例与否

c# - 如何从 MasterPage 触发 UpdatePanel 更新?

Net 2.1 的 C# 双端队列

Java - 没有可用的堆栈跟踪