Могу ли я заставить компилятор оптимизировать определенный метод? - PullRequest
15 голосов
/ 13 марта 2012

Есть ли атрибут, который я могу использовать, чтобы сообщить компилятору, что метод всегда должен быть оптимизирован, даже если глобальный /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. Но с неоптимизированной версией я не очень уверен ... результат рекурсивного вызова сохраняется в локальной переменной, затем появляется бесполезная ветвь, которая просто переходит к следующей инструкции, локальная переменная загружается и возвращается. Поэтому у меня нет простого способа проверить, что рекурсивный вызов действительно является последней инструкцией, поэтому я не уверен, что можно применить оптимизацию хвостового вызова.

Ответы [ 3 ]

2 голосов
/ 15 марта 2012

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

Что касается первоначальной проблемы, поскольку MSIL является языком, основанным на стеке. И спецификации гарантируют состояние стека в операторе ret, вы можете быть на 100% уверены, что можете добавить хвостовой префикс без проблем. Тем не менее, это также вряд ли принесет какую-либо пользу, так как я не видел, чтобы JIT использовал префикс tail для фактической оптимизации окончательно объединенного кода.

0 голосов
/ 15 марта 2012

Вы никогда не можете быть уверены, что вы получите оптимизацию оконечного вызова, если вы используете C #.

В частности, даже с call ... ret JITter не гарантирует хвостовой вызов.Поэтому код IMO C #, основанный на оптимизации хвостового вызова (во избежание переполнения стека), просто не работает.В C # оптимизация хвостовых вызовов - это просто оптимизация производительности.

Используйте язык, в котором хвостовые вызовы генерируются надежно, или переписайте свой метод так, чтобы он не нуждался в хвостовых вызовах.

0 голосов
/ 15 марта 2012

Можно ли в любом случае генерировать исходный код метода динамически с помощью Microsoft.CSharp.CSharpCodeProvider?

Если вы управляете компиляцией метода, вы можете установить параметры при вызове компилятора, используя CompilerOptions .

...