Почему заказ JIT влияет на производительность?

Почему порядок, в котором методы C #в.NET 4.0 просто -за -времени компиляции, влияет на скорость их выполнения? Например, рассмотрим два эквивалентных метода :

public static void SingleLineTest()
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    int count = 0;
    for (uint i = 0; i < 1000000000; ++i) {
        count += i % 16 == 0 ? 1 : 0;
    }
    stopwatch.Stop();
    Console.WriteLine("Single-line test --> Count: {0}, Time: {1}", count, stopwatch.ElapsedMilliseconds);
}

public static void MultiLineTest()
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    int count = 0;
    for (uint i = 0; i < 1000000000; ++i) {
        var isMultipleOf16 = i % 16 == 0;
        count += isMultipleOf16 ? 1 : 0;
    }
    stopwatch.Stop();
    Console.WriteLine("Multi-line test  --> Count: {0}, Time: {1}", count, stopwatch.ElapsedMilliseconds);
}

. Единственная разница заключается во введении локальной переменной, которая влияет на сгенерированный ассемблерный код и производительность цикла. Почему это так, вопрос сам по себе .

Возможно, еще более странным является то, что на x86 (, но не на x64 )порядок вызова методов оказывает влияние на производительность примерно на 20%. Вызывайте такие методы...

static void Main()
{
    SingleLineTest();
    MultiLineTest();
}

...и SingleLineTestбыстрее. (Скомпилируйте с использованием конфигурации выпуска x86, убедившись, что параметр «Оптимизировать код» включен, и запустите тест вне VS2010. )Но поменяйте порядок...

static void Main()
{
    MultiLineTest();
    SingleLineTest();
}

...и оба метода требуют одинакового времени (почти, но не совсем, пока MultiLineTestдо ). (При выполнении этого теста полезно добавить дополнительные вызовы SingleLineTestи MultiLineTest, чтобы получить дополнительные выборки. Сколько и в каком порядке не имеет значения, за исключением того, какой метод вызывается первым.)

Наконец, чтобы продемонстрировать, что порядок JIT важен, сначала оставьте MultiLineTest, но сначала принудительно выполните JIT для SingleLineTest...

static void Main()
{
    RuntimeHelpers.PrepareMethod(typeof(Program).GetMethod("SingleLineTest").MethodHandle);
    MultiLineTest();
    SingleLineTest();
}

Теперь SingleLineTestснова быстрее.

Если вы отключите «Подавить оптимизацию JIT при загрузке модуля» в VS2010, вы можете поставить точку останова в SingleLineTestи увидеть, что ассемблерный код в цикле одинаков независимо от порядка JIT; однако ассемблерный код в начале метода различается. Но какое это имеет значение, когда основная часть времени проводится в цикле, вызывает недоумение.

Пример проекта , демонстрирующий такое поведение , находится на github.

Неясно, как такое поведение влияет на -реальные приложения. Одна из проблем заключается в том, что это может сделать настройку производительности неустойчивой, в зависимости от того, в каком порядке методы вызываются первыми. Проблемы такого рода было бы трудно обнаружить с помощью профилировщика. После того, как вы нашли горячие точки и оптимизировали их алгоритмы, было бы трудно узнать без большого количества предположений и заранее проверить, возможно ли дополнительное ускорение с помощью методов JIT.

Обновление:См. также запись Microsoft Connect по этой проблеме.

34
задан Community 23 May 2017 в 11:53
поделиться