微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

2)为什么编译器设计者a)不能使beforefield之前的所有结构都是认的,而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秒)

解决方法

第一次访问您的类型时,必须执行静态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左右触摸视频主题).

我希望这有帮助!

原文地址:https://www.jb51.cc/csharp/92008.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐