Есть ли атрибут, который я могу использовать, чтобы сообщить компилятору, что метод всегда должен быть оптимизирован, даже если глобальный /o+
переключатель компилятора не установлен?
Причина, по которой я спрашиваю, заключается в том, что я играю с идеей динамического создания метода на основе кода IL существующего метода; манипуляции, которые я хочу сделать, достаточно просты, когда код оптимизирован, но значительно усложняются в неоптимизированном коде из-за дополнительных инструкций, генерируемых компилятором.
РЕДАКТИРОВАТЬ: более подробную информацию о неоптимизации, которые меня беспокоят ...
Рассмотрим следующую реализацию факториальной функции:
static long FactorialRec(int n, long acc)
{
if (n == 0)
return acc;
return FactorialRec(n - 1, acc * n);
}
(Примечание: я знаю, что есть лучшие способы вычисления факториала, это всего лишь пример)
IL, сгенерированный с включенной оптимизацией, довольно прост:
IL_0000: ldarg.0
IL_0001: brtrue.s IL_0005
IL_0003: ldarg.1
IL_0004: ret
IL_0005: ldarg.0
IL_0006: ldc.i4.1
IL_0007: sub
IL_0008: ldarg.1
IL_0009: ldarg.0
IL_000A: conv.i8
IL_000B: mul
IL_000C: call UserQuery.FactorialRec
IL_0011: ret
Но неоптимизированная версия совсем другая
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: ceq
IL_0005: ldc.i4.0
IL_0006: ceq
IL_0008: stloc.1
IL_0009: ldloc.1
IL_000A: brtrue.s IL_0010
IL_000C: ldarg.1
IL_000D: stloc.0
IL_000E: br.s IL_001F
IL_0010: ldarg.0
IL_0011: ldc.i4.1
IL_0012: sub
IL_0013: ldarg.1
IL_0014: ldarg.0
IL_0015: conv.i8
IL_0016: mul
IL_0017: call UserQuery.FactorialRec
IL_001C: stloc.0
IL_001D: br.s IL_001F
IL_001F: ldloc.0
IL_0020: ret
Он имеет только одну точку выхода в конце. Возвращаемое значение хранится в локальной переменной.
Почему это проблема? Я хочу динамически генерировать метод, который включает в себя оптимизацию хвостового вызова. Оптимизированный метод может быть легко изменен путем добавления префикса tail.
перед рекурсивным вызовом, поскольку после вызова нет ничего, кроме ret
. Но с неоптимизированной версией я не очень уверен ... результат рекурсивного вызова сохраняется в локальной переменной, затем появляется бесполезная ветвь, которая просто переходит к следующей инструкции, локальная переменная загружается и возвращается. Поэтому у меня нет простого способа проверить, что рекурсивный вызов действительно является последней инструкцией, поэтому я не уверен, что можно применить оптимизацию хвостового вызова.