Вопрос

Кто-нибудь может объяснить эти несколько строк MSIL?Почему он перемещает значение из стека вычислений в локальную переменную только для того, чтобы немедленно переместить его обратно и вернуть?

Следующий код MSIL загружает один аргумент (строку), вызывает метод, который возвращает bool, а затем возвращает это значение bool.Чего я не понимаю, так это почему он вызывает stloc.0 для сохранения возвращаемого значения метода в локальной переменной, затем выполняет явную безусловную передачу управления в самую следующую помеченную строку (кажется ненужным), только для того, чтобы переместить значение обратно в стек вычислений, прежде чем возвращать его.

.maxstack 1
.locals init ([0] bool CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: call bool FuncNameNotImporant::MethodNameNotImporant(string)
L_0007: stloc.0 
L_0008: br.s L_000a
L_000a: ldloc.0 
L_000b: ret 

Мое лучшее предположение о том, почему он это делает, - выполнить какую-то проверку типа, чтобы убедиться, что значение в стеке вычислений на самом деле является логическим значением, прежде чем возвращать его.Но я понятия не имею о явном переходе к самой следующей строке;Я имею в виду, разве это все равно не попало бы туда?Исходный код C # для метода представляет собой всего одну строку, которая возвращает результат метода.

Это было полезно?

Решение

Если вы откроете эту функцию в отладчике с кодом, скомпилированным в режиме отладки:

bool foo(string arg)
{
    return bar(arg);
}

Есть 3 точки останова, которые вы можете установить:

  1. В открывающей фигурной скобке функции.
  2. На строке "возврат".
  3. В закрывающей фигурной скобке функции.

Установка точки разрыва в открывающей фигурной скобке означает "разрыв при вызове этой функции get".Вот почему в начале метода есть инструкция no-op.Когда точка останова установлена в открывающей фигурной скобке, отладчик фактически устанавливает ее в режиме no-op.

Установка точки останова на закрывающей фигурной скобке означает "прерывание при завершении работы этой функции".Для того чтобы это произошло, функция должна иметь единственную инструкцию возврата в своем IL, где может быть установлена точка останова.Компилятор позволяет это, используя временную переменную для хранения возвращаемого значения и преобразуя

return retVal;

в

$retTmp = retVal;
goto exit;

а затем вводим следующий код в нижней части метода:

exit:
return $ret;

Кроме того, в режиме отладки компиляторы плохо относятся к генерируемому ими коду.В основном они делают что-то вроде:

GenerateProlog();
foreach (var statement in statements)
{
    Generate(statement);
}
GenerateEpilog();

В вашем случае вы видите:

return foo(arg);

переводится на:

; //this is a no-op
bool retTemp = false;
retTemp = foo(arg);
goto exit;
exit:
return retTemp;

Если бы компилятор выполнял "оптимизацию скользящего окна", он мог бы взглянуть на этот код и понять, что в нем была некоторая избыточность.Однако компиляторы обычно не делают этого в режиме отладки.Оптимизация компилятора может выполнять такие действия, как исключение переменных и изменение порядка инструкций, что затрудняет отладку.Поскольку целью отладочной сборки является включение отладки, было бы нехорошо включать оптимизацию.

В сборке релиза код не будет выглядеть подобным образом.Это потому, что компилятор не вводит специальный код для включения точек останова в открывающих и закрывающих фигурных скобках, что просто оставляет для компиляции следующее:

return bar(arg);

В конечном итоге это выглядит довольно просто.

Однако следует отметить одну вещь: я не думаю, что компилятор C # выполняет большую оптимизацию скользящего окна, даже в розничных сборках.Это потому, что большая часть этих оптимизаций зависит от базовой архитектуры процессора и поэтому выполняется JIT-компилятором.Выполнение оптимизаций, даже тех, которые не зависят от процессора, в компиляторе C # может препятствовать способности JIT оптимизировать код (он ищет шаблоны, которые генерируются неоптимизированной генерацией кода, и если он видит сильно оптимизированный IL, он может запутаться).Поэтому обычно управляемые компиляторы кода их не выполняют.Он выполняет некоторые "дорогостоящие действия" (которые JIT не хочет выполнять во время выполнения), такие как обнаружение неисправного кода и анализ переменных в реальном времени, но они не решают проблемы, решаемые оптимизацией скользящего окна.

Другие советы

Вы компилируете в режиме отладки или выпуска?В режиме выпуска я получаю:

.method private hidebysig static bool Test1(string arg) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: call bool FuncNameNotImportant::MethodNameNotImportant(string)
    L_0006: ret 
}

Ветвление, которое вы видите, вероятно, предназначено для поддержки отладчика.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top