Эффективность статического конструктора, и почему мы не можем указать 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

и

1) Почему разница во времени между struct с beforefieldinit а тот, что без? (Я представьте, что JITer делает что-то дополнительное в цикле for, однако я понятия не имею, как просмотреть выход JITer, чтобы увидеть, что.

2) Почему разработчики компилятора a) не сделали все структуры beforefieldinit быть по умолчанию и b) не давать разработчикам возможность явно указать это поведение? Конечно, это is если вы не можете, как я не смог найти способ.


Edit:

1. я изменил код, по существу, запуск каждого цикла во второй раз, ожидая улучшения, но это было не так много:

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 будет знать, что конструктор типа уже вызван и не испускает код для этого при компиляции второго цикла.

в дополнение к Мотти ответ: код дает лучшие результаты, из-за разницы в преобразование в формат JIT, на преобразование в формат JIT от DoSecondLoop не испускает статическую проверку ctor, потому что она обнаружила, что это было сделано ранее в DoFirstLoop, заставляя каждый цикл работать с одинаковой скоростью. (~3 секунды)

1 ответов


при первом доступе к вашему типу необходимо выполнить статический ctor (генерируемый явно или неявно).

когда компилятор JIT компилирует IL в собственные инструкции, он проверяет, был ли выполнен статический ctor для этого типа, и если нет, выдает собственный код, который проверяет (снова), был ли выполнен статический ctor, а если нет, выполняет его.

jitted код кэшируется для будущих вызовов того же метода (и именно поэтому код должен был проверьте еще раз, был ли выполнен статический ctor).

дело в том, что если jitted-код, который проверяет статический ctor, находится в цикле, этот тест будет происходить на каждой итерации.

когда beforefieldinit присутствует, компилятору JIT разрешено оптимизировать код, путем тестирования статического вызова ctor перед входом в цикл. Если его нет, такая оптимизация недопустима.

компилятор C# автоматически решает для нас, когда испускать этот атрибут. В настоящее время (C# 4) испускает его только в том случае, если код явно не определяет статический конструктор. Идея заключается в том, что если вы сами определили статический ctor, время может быть более важным для вас, и ststic ctor не должен выполняться раньше времени. Это может быть или не быть правдой, но вы не можете изменить это поведение.

вот ссылка на часть моего онлайн-учебника .NET, которая подробно объясняет это:http://motti.me/c1L

кстати, я не рекомендую использование статических ctors на структурах, потому что Верьте или нет, они не гарантированы для выполнения! Это не было частью вопроса, поэтому я не буду уточнять, но см. Это для более подробной информации, если вас интересует:http://motti.me/c1I (я касаюсь темы около 2: 30 в видео).

надеюсь, это поможет!