Почему порядок, в котором методы 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 по этой проблеме.