C#Singleton实现性能

时间:2017-02-24 16:32:55

标签: c# performance singleton

我正在学习C#,在我退出编程很长一段时间后(早在90年代,曾经在C和汇编程序中编程),所以我对C#和OOP一般都是新手。

在研究Singleton模式及其实现时,我偶然发现了这种奇怪的行为。我看到有些人使用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;
        }
    }
}

Singleton2 Class with!= in if

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;
        }
    }
}

3 个答案:

答案 0 :(得分:2)

编译程序时,您可以看到Singleton2有一些额外的指令,但如果它看到实例存在,它会直接分支到最后。这样可以提高你所看到的速度。

以下是两个类的实例方法的IL代码:

Singleton1:

.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

Singleton2:

.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工具(解释型语言反汇编程序)生成的。

答案 1 :(得分:1)

这是我多年来使用过的Singleton基类:

public class SingletonBase<T> where T : class
{
    static SingletonBase()
    {
    }

    public static readonly T Instance = 
        typeof(T).InvokeMember(typeof(T).Name, 
                                BindingFlags.CreateInstance | 
                                BindingFlags.Instance |
                                BindingFlags.Public |
                                BindingFlags.NonPublic, 
                                null, null, null) as T;
}

答案 2 :(得分:0)

在案例1中,您几乎每次都要执行goto跳过对operator new的调用。在情况2中,如果您尚未初始化变量,则仅执行goto,即一次。简单地说 - 案例2每次通话的工作量较少。