В комментарии к this answer (который предлагает использовать операторы бит-сдвига над целым умножением/делением, для производительности), я спросил, будет ли это на самом деле быстрее. В глубине моего сознания есть идея, что на уровне уровень, что-то будет достаточно умным, чтобы выработать то, что >> 1
и / 2
- это та же операция. Однако теперь мне интересно, действительно ли это так, и если да, то на каком уровне это происходит.Есть ли способ увидеть собственный код, созданный theJITter для данного C#/CIL?
Программа испытаний производит следующий сравнительный CIL (с optimize
на) в течение двух методов, которые соответственно разделяют и сдвига их аргумент:
IL_0000: ldarg.0
IL_0001: ldc.i4.2
IL_0002: div
IL_0003: ret
} // end of method Program::Divider
против
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: shr
IL_0003: ret
} // end of method Program::Shifter
Таким образом, C# компилятор излучающий div
или shr
инструкции, не будучи умными. Теперь я хотел бы видеть фактический ассемблер x86, который производит JITter, но я понятия не имею, как это сделать. Возможно ли это?
редактировать добавить
ВыводыСпасибо за ответы, приняли один из nobugz, потому что в нем содержится ключевая информация о параметре отладчика. Что в конечном счете работал для меня:
- Переключить Освободить конфигурацию
- В
Tools | Options | Debugger
выключить «Подавить оптимизацию JIT на загрузке модуля» (т.е. мы хотим позволяют оптимизировать JIT) - То же место, выключить «Включить Just My Code» (т.е. мы хотим отладить все код)
- Поместите
Debugger.Break()
заявление где-то - построить узел
- Запустите exe-файл, и когда он ломается, отладки с использованием существующего VS например
- Теперь Демонтажные окно показывает фактическое x86, который будет выполнен
Результаты были поучительным, мягко говоря, - Оказывается, JITter действительно может сделать арифметику! Здесь отредактированы образцы из окна «Разборка». Различные методы -Shifter
делят по степеням двух, используя >>
; различные -Divider
методы делят на целых числах, используя /
Console.WriteLine(string.Format("
{0}
shift-divided by 2: {1}
divide-divided by 2: {2}",
60, TwoShifter(60), TwoDivider(60)));
00000026 mov dword ptr [edx+4],3Ch
...
0000003b mov dword ptr [edx+4],1Eh
...
00000057 mov dword ptr [esi+4],1Eh
Оба статический делит на 2, не только были встраиваемые методы, но фактические расчеты были сделаны по джиттеру
Console.WriteLine(string.Format("
{0}
divide-divided by 3: {1}",
60, ThreeDivider(60)));
00000085 mov dword ptr [esi+4],3Ch
...
000000a0 mov dword ptr [esi+4],14h
То же с статический деление на 3.
Console.WriteLine(string.Format("
{0}
shift-divided by 4: {1}
divide-divided by 4 {2}",
60, FourShifter(60), FourDivider(60)));
000000ce mov dword ptr [esi+4],3Ch
...
000000e3 mov dword ptr [edx+4],0Fh
...
000000ff mov dword ptr [esi+4],0Fh
И статически-разделить на 4.
Лучшего:
Console.WriteLine(string.Format("
{0}
n-divided by 2: {1}
n-divided by 3: {2}
n-divided by 4: {3}",
60, Divider(60, 2), Divider(60, 3), Divider(60, 4)));
0000013e mov dword ptr [esi+4],3Ch
...
0000015b mov dword ptr [esi+4],1Eh
...
0000017b mov dword ptr [esi+4],14h
...
0000019b mov dword ptr [edi+4],0Fh
Это встраиваемое, а затем вычислил все эти статические подразделения!
Но что, если результат не является статическим? Я добавил код для чтения целого числа из Консоли. Это то, что она производит для подразделений на том, что:
Console.WriteLine(string.Format("
{0}
shift-divided by 2: {1}
divide-divided by 2: {2}",
i, TwoShifter(i), TwoDivider(i)));
00000211 sar eax,1
...
00000230 sar eax,1
Так, несмотря на CIL быть другим, джиттер знает, что деление на 2, правый сдвиг на 1.
Console.WriteLine(string.Format("
{0}
divide-divided by 3: {1}", i, ThreeDivider(i)));
00000283 IDIV EAX, ECX
И он знает, что вам нужно разделить разделить на 3.
Console.WriteLine(string.Format("
{0}
shift-divided by 4: {1}
divide-divided by 4 {2}",
i, FourShifter(i), FourDivider(i)));
000002c5 sar eax,2
...
000002ec sar eax,2
И KNO WS, что разделительный на 4 правом сдвигая 2.
Наконец (лучше еще раз!)
Console.WriteLine(string.Format("
{0}
n-divided by 2: {1}
n-divided by 3: {2}
n-divided by 4: {3}",
i, Divider(i, 2), Divider(i, 3), Divider(i, 4)));
00000345 sar eax,1
...
00000370 idiv eax,ecx
...
00000395 sar esi,2
Это встраиваемый метод и разработал лучший способ делать вещи, основываясь на statically- доступные аргументы. Ницца.
Так что да, где-то в стеке между C# и x86, что-то является достаточно умен, чтобы понять, что >> 1
и / 2
одинаковы. И все это еще больше усугубило мое мнение о том, что добавление компилятора C#, JITter и CLR делает целым рядом более крутым r, чем любые небольшие трюки, которые мы можем попробовать как смиренные приложения-программисты :)
Можете ли вы опубликовать свои выводы на благо всех нас? спасибо :) – flesh