Почему компилятор .NET JIT решит не включать или отключать вызовы для пустых статических методов, которые не имеют побочных эффектов?
Я думаю, что наблюдаю за компилятором .NET JIT, не вставляя или не оптимизируя вызовы на пустые статические методы, которые не имеют побочных эффектов, что несколько удивительно, учитывая некоторые бесплатные онлайн-ресурсы.
Моя среда - это Visual Studio 2013 на x64, Windows 8.1,.NET Framework 4.5.
Учитывая эту простую тестовую программу (https://ideone.com/2BRCpC)
class Program
{
static void EmptyBody()
{
}
static void Main()
{
EmptyBody();
}
}
Сборка с оптимизацией вышеуказанной программы создает следующий MSIL для Main
и EmptyBody
:
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 6 (0x6)
.maxstack 8
IL_0000: call void Program::EmptyBody()
IL_0005: ret
} // end of method Program::Main
.method private hidebysig static void EmptyBody() cil managed
{
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method Program::EmptyBody
Неудивительно, что MSIL содержит вызов от Main
до EmptyBody
, так как компилятор С# не ожидает встроенных или оптимизированных подобных вызовов. Тем не менее, я думал, что компилятор JIT затем включит или оптимизирует этот вызов. Но это, похоже, не происходит.
Если я запустил вышеуказанную программу и перешел в отладчик в Main
, сгенерированная сборка:
00572621 mov ebp,esp
00572623 cmp dword ptr ds:[4320B84h],0
0057262A je 00572631
0057262C call 73E6AF20
00572631 call dword ptr ds:[4321578h]
Указатель инструкции сразу устанавливается в последнюю строку в 00572631, что является вызовом EmptyBody
. Выйдя в EmptyBody
, создается сгенерированная сборка
00BD2651 mov ebp,esp
00BD2653 cmp dword ptr ds:[4B00B84h],0
00BD265A je 00BD2661
00BD265C call 73E6AF20
00BD2661 nop
00BD2662 pop ebp
00BD2663 ret
Указатель инструкции сразу устанавливается в строку nop
на 00BD2661, что ничего не делает, и я не могу догадаться, почему он сгенерирован в первую очередь.
Учитывая, что два вышеописанных фрагмента сборки содержат один и тот же заголовок с 4 командами, я предполагаю, что только плавная панель входа в систему, где установлен стек и такая настройка. Я очень хочу научиться знать, что будут делать эти повторяющиеся инструкции:
00BD2653 cmp dword ptr ds:[4B00B84h],0
00BD265A je 00BD2661
00BD265C call 73E6AF20
Во всяком случае, главный вопрос: почему компилятор JIT создает сборку, которая вызывает пустой статический метод EmptyBody
?
Ответы
Ответ 1
После копания немного дальше, оказывается, я могу ответить на этот вопрос сам. Как объяснено в http://blogs.msdn.com/b/vancem/archive/2006/02/20/535807.aspx, наблюдение за разборкой оптимизированной сборки выпуска под отладчиком по умолчанию будет влиять на компилятор JIT.
Снимите отметку с этих
- 'Запретить оптимизацию JIT при загрузке модуля
- 'Включить только мой код
в разделе VS > Инструменты > Отладкa > Общие, отобразит "реальный" результат компиляции JIT, который для вызова EmptyBody
в моем Main
выше:
004C2620 ret
Значение того, что вызов EmptyBody
полностью удален, что и ожидалось, и мир по-прежнему остается счастливым и несколько предсказуемым местом для жизни:)