静态构造函数的性能以及为什么我们不能指定beforefieldinit

时间:2011-11-16 18:04:19

标签: c# .net performance clr

我使用以下两种结构来发现速度差异:

public struct NoStaticCtor
{
    private static int _myValue = 3;
    public static int GetMyValue() { return _myValue; }
}

public struct StaticCtor
{
    private static int _myValue;
    public static int GetMyValue() { return _myValue; }
    static StaticCtor()
    {
        _myValue = 3;
    }
}

class Program
{
    static void Main(string[] args)
    {
        long numTimes = 5000000000; // yup, 5 billion
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (long i = 0; i < numTimes; i++)
        {
            NoStaticCtor.GetMyValue();
        }
        sw.Stop();
        Console.WriteLine("No static ctor: {0}", sw.Elapsed);

        sw.Restart();
        for (long i = 0; i < numTimes; i++)
        {
            StaticCtor.GetMyValue();
        }
        sw.Stop();
        Console.WriteLine("with static ctor: {0}", sw.Elapsed);
    }
}

产生结果:

Release (x86), no debugger attached:
No static ctor: 00:00:05.1111786
with static ctor: 00:00:09.9502592

Release (x64), no debugger attached:
No static ctor: 00:00:03.2595979
with static ctor: 00:00:14.5922220

编译器为NoStaticCtor生成一个静态构造函数,该构造函数与StaticCtor中明确声明的构造函数相同。我知道,当没有明确定义静态构造函数时,编译器只会发出beforefieldinit

他们生成几乎相同的il代码,除了一个区别,用beforefieldinit声明结构,这是我觉得区别的地方,因为我知道它确定何时类型构造函数是虽然我无法弄清楚为什么会有这样的差异。它假设每次迭代都不是调用类型构造函数,因为类型构造函数只能被调用一次。 1

所以,

1)为什么结构与beforefieldinit和没有结构的结构之间存在时差? (我想JITer在for循环中做了一些额外的事情,但是,我不知道如何查看JITer的输出以查看内容。

2)为什么编译器设计者a)不使所有结构beforefieldinit成为默认结构,b)不让开发人员能够明确指定该行为?当然,这个假设你不能,因为我找不到方法。


编辑:

1 I modified the code,基本上是第二次运行每个循环,期待一个改进,但它并不多:

No static ctor: 00:00:03.3342359
with static ctor: 00:00:14.6139917
No static ctor: 00:00:03.2229995
with static ctor: 00:00:12.9524860
Press any key to continue . . .

我之所以这样做,是因为我,也许,但不太可能,JITer实际上每次迭代都会调用类型构造函数。在我看来,JITer会知道类型构造函数已被调用,并且在编译第二个循环时不会发出代码来执行此操作。

除了Motti的回答: This code产生更好的结果,因为JITing的差异,DoSecondLoop的JITing不会发出静态ctor检查,因为它检测到它是先前在DoFirstLoop中完成的,导致每个循环到以相同的速度执行。 (~3秒)

1 个答案:

答案 0 :(得分:11)

第一次访问您的类型时,必须执行静态ctor(无论是显式生成还是隐式生成)。

当JIT编译器将IL编译为本机指令时,它会检查该类型的静态ctor是否已执行,如果不是,则发出本机代码(再次)检查静态ctor是否已执行,如果不执行,则执行它。 / p>

jitted代码被缓存以供将来调用相同的方法(这就是为什么代码必须再次检查是否执行了静态ctor)。

问题是,如果检查静态ctor的jitted代码是循环的,那么每次迭代都会进行此测试。

当存在beforefieldinit时,允许JIT编译器通过在进入循环之前测试静态ctor调用来优化代码。如果它不存在,则不允许进行此优化。

C#编译器会自动决定何时发出此属性。它目前(C#4)只有在代码没有明确定义静态构造函数时才会发出它。它背后的想法是,如果你自己定义一个静态的ctor,时间可能对你来说更重要,并且ststic ctor不应该提前执行。这可能是也可能不是,但你不能改变这种行为。

以下是我在线.NET教程部分的链接,详细解释了这一点:http://motti.me/c1L

顺便说一句,我不建议在结构上使用静态ctors,因为信不信由你,它们不能保证执行!这不是问题的一部分,所以我不会详细说明,但如果你感兴趣的话,可以看看这个更详细:http://motti.me/c1I(我在大约2:30点击视频进入视频)。

我希望这有帮助!