2012-03-13 2 views
15

Есть ли атрибут, который я могу использовать, чтобы сообщить компилятору, что метод всегда должен быть оптимизирован, даже если глобальный переключатель компилятора /o+ не установлен?Могу ли я заставить компилятор оптимизировать определенный метод?

Причина, по которой я прошу, состоит в том, что я играю с идеей динамического создания метода на основе кода IL существующего метода; манипуляция, которую я хочу сделать, достаточно прост, когда код оптимизирован, но становится значительно сложнее в неоптимизированном коде из-за дополнительных инструкций, сгенерированных компилятором.


EDIT: более подробно о не-оптимизаций, которые беспокоят меня ...

Рассмотрим следующую реализацию функции факториала:

static long FactorialRec(int n, long acc) 
{ 
    if (n == 0) 
     return acc; 
    return FactorialRec(n - 1, acc * n); 
} 

(Примечание: Я знаю, являются лучшими способами вычисления факториала, это просто пример)

ИЛ, сгенерированный с включенными оптимизациями i S довольно проста:

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

+1

AFAIK, нет - это невозможно –

+1

Компилятор JIT всегда будет оптимизировать каждый метод. – Steven

+0

@Steven, если вы не указали это (например, с флагом 'NoOptimization' в' MethodImplAttribute'). Но в любом случае, мой вопрос касается оптимизации компилятора, а не оптимизации JIT, поскольку меня интересует генерируемый IL-код. –

ответ

2

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

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

+0

Downvoter заботится о том, что является неправильным? –

+0

Связано: http://stackoverflow.com/questions/491376 - Очевидно, '.tail' оптимизирован в x64, но не x86 –

0

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

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

+0

Это не поможет; Я не хочу генерировать код динамически (не исходный код, который я имею в виду) –

0

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

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

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

+0

Я знаю, что C# не подходит для хвостовых вызовов, я просто делал доказательство концепции ... не волнуйтесь, я не намерены использовать это в производственном коде;) –

Смежные вопросы