Ответ 1
Чтобы посмотреть, что делает компилятор С# для вас, вам нужно посмотреть на IL. Если вы хотите увидеть, как это влияет на JIT-код, вам нужно посмотреть на собственный код, как описано Scott Chamberlain. Имейте в виду, что JIT-код будет зависеть от архитектуры процессора, версии CLR, способа запуска процесса и, возможно, других вещей.
Обычно я начинал с IL, а затем потенциально смотрел код JIT.
Сравнение IL с использованием ildasm
может быть несколько сложным, так как оно включает метку для каждой команды. Вот две версии вашего метода, скомпилированные с оптимизацией (без использования оптимизации) (с использованием компилятора С# 5), с посторонними метками (и инструкциями nop
), чтобы сделать их максимально удобными для сравнения:
Оптимизированное
.method public hidebysig static uint32
CalculateCheckSum(string str) cil managed
{
// Code size 46 (0x2e)
.maxstack 2
.locals init (char[] V_0,
uint32 V_1,
char V_2,
char[] V_3,
int32 V_4)
ldarg.0
callvirt instance char[] [mscorlib]System.String::ToCharArray()
stloc.0
ldc.i4.0
stloc.1
ldloc.0
stloc.3
ldc.i4.0
stloc.s V_4
br.s loopcheck
loopstart:
ldloc.3
ldloc.s V_4
ldelem.u2
stloc.2
ldloc.1
ldloc.2
add
stloc.1
ldloc.s V_4
ldc.i4.1
add
stloc.s V_4
loopcheck:
ldloc.s V_4
ldloc.3
ldlen
conv.i4
blt.s loopstart
ldloc.1
ldc.i4 0x100
rem.un
ret
} // end of method Program::CalculateCheckSum
Неоптимизированный
.method public hidebysig static uint32
CalculateCheckSum(string str) cil managed
{
// Code size 63 (0x3f)
.maxstack 2
.locals init (char[] V_0,
uint32 V_1,
char V_2,
uint32 V_3,
char[] V_4,
int32 V_5,
bool V_6)
ldarg.0
callvirt instance char[] [mscorlib]System.String::ToCharArray()
stloc.0
ldc.i4.0
stloc.1
ldloc.0
stloc.s V_4
ldc.i4.0
stloc.s V_5
br.s loopcheck
loopstart:
ldloc.s V_4
ldloc.s V_5
ldelem.u2
stloc.2
ldloc.1
ldloc.2
add
stloc.1
ldloc.s V_5
ldc.i4.1
add
stloc.s V_5
loopcheck:
ldloc.s V_5
ldloc.s V_4
ldlen
conv.i4
clt
stloc.s V_6
ldloc.s V_6
brtrue.s loopstart
ldloc.1
ldc.i4 0x100
rem.un
stloc.3
br.s methodend
methodend:
ldloc.3
ret
}
Примечание:
- Оптимизированная версия использует меньшее количество локальных пользователей. Это может позволить JIT более эффективно использовать регистры.
- Оптимизированная версия использует
blt.s
, а неclt
, а затемbrtrue.s
, проверяя, следует ли снова повторять цикл (это причина для одного из дополнительных локалей). - Неоптимизированная версия использует дополнительную локальную память для хранения возвращаемого значения перед возвратом, предположительно для облегчения отладки.
- Неоптимизированная версия имеет безусловную ветвь перед ее возвратом.
- Оптимизированная версия короче, но я сомневаюсь, что она достаточно короткая, чтобы быть встроенной, поэтому я подозреваю, что это неуместно.