#c# #optimization #jit #cil
#c# #оптимизация #джит #cil
Вопрос:
Будет ли следующий пустой if-блок оптимизирован?
public class C
{
private bool foo = false;
public void M()
{
if(foo) {}
}
}
SharpLab (master 5 декабря 2020 года, выпуск) указывает, что компилятор не оптимизирует if-блок:
.method public hidebysig instance void M () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld bool C::foo
IL_0006: pop
IL_0007: ret
}
Должен ли компилятор не видеть, что ldfld
followed by pop
не имеет никакого эффекта, и три инструкции ( ldarg.0
тоже) не нужно отправлять?
Я не могу думать о возможных побочных эффектах, которые могли бы возникнуть при отсутствии излучения ldfld
(как это может быть при вызове метода внутри if-блока).
Кроме того, JIT ASM, который генерирует SharpLab, также, похоже, не оптимизирует пустой if-блок:
C.M()
L0000: movzx eax, byte ptr [ecx 4]
L0004: ret
Правильно ли я понимаю, что JIT все равно оптимизирует пустой if-блок?
- Если да, не могли бы вы, пожалуйста, объяснить, почему компилятор не может этого сделать?
- Если нет, то почему это не так?
Заранее благодарю вас!
Комментарии:
1. Вы создаете эту цель как release или debug? Я думаю, что при отладке определенных оптимизаций у меня не происходит.
Ответ №1:
Кроме того, JIT ASM, который генерирует SharpLab, также, похоже, не оптимизирует пустой if-блок:
C.M() L0000: movzx eax, byte ptr [ecx 4] L0004: ret
Фактический if
блок был оптимизирован, если бы он существовал, он выглядел бы сравнимо с этим:
C.M()
L0000: movzx eax, byte ptr [ecx 4]
L0004: test eax, eax
L0006: je short L0008
L0008: ret
Но этого не произошло, JIT-компилятор, по-видимому, понял, что это будет бесполезно.
Все еще существует только загрузка логического поля.
Комментарии:
1. Спасибо! Я плохо разбираюсь в сборке и был слишком сосредоточен не только на JIT ASM
ret
, поэтому я пришел к неправильному выводу… однако знаете ли вы, почему загрузка логического поля все еще существует? Как насчет вашей (теперь отредактированной) существующей точки загрузки, котораяnull
не может быть передана методу?2. @ThomasFlinkow фиктивная загрузка вставляется в вызывающий объект, если это необходимо (например, как это , где
mov eax, [rdx]
ничего, кроме запуска NRE, не происходит), так что, в конце концов, это не так. Как бы это ни было скучно, я должен объяснить это тем, что компилятор JIT немного тупой.3. Оптимизация загрузки может привести к заметным изменениям в поведении. Если вы используете
call
метод вместоcallvirt
on и передаетеnull
asthis
, исходный код все равно будет вызывать исключение NullReferenceException при загрузке, в то время как без него метод возвращался бы нормально. Может быть интересно посмотретьstatic
, ведут ли себя поля одинаково.4. @IllidanS4supportsMonica изначально это была гипотеза, но в вызывающий объект вставляется фиктивная загрузка, поэтому, если
this
этоnull
так, метод даже не вызывается, и ему не нужно аварийно завершать работу5. Я думаю, что компилятор настолько туп.