C# · 12月 24, 2021

c# – 静态构造函数性能以及为什么我们无法指定beforefieldinit

我使用以下两个结构来发现速度上的差异: 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.1111786with static ctor: 00:00:09.9502592Release (x64),no debugger attached:No static ctor: 00:00:03.2595979with static ctor: 00:00:14.5922220

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

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

所以,

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

2)为什么编译器设计者a)不能使beforefield之前的所有结构都是默认的,而b)不能让开发人员明确指定该行为?当然,这是假设你不能,因为我还没有找到办法.

编辑:

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

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

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

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

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

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

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

问题是,如果检查静态ctor的jitted代码处于循环中,则此测试将在每次迭代中进行.

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

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

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

顺便说一句,我不建议在结构上使用静态ctors,因为信不信由你,它们不能保证执行!这不是问题的一部分,所以我不会详细说明,但如果你感兴趣的话,请看更多细节:http://motti.me/c1I(我在2:30左右触摸视频主题).

我希望这有帮助!