2012-03-13 13 views
15

¿Hay algún atributo que pueda usar para decirle al compilador que siempre se debe optimizar un método, incluso si el interruptor del compilador global /o+ no está configurado?¿Puedo forzar al compilador a optimizar un método específico?

La razón que pido es porque estoy jugando con la idea de crear dinámicamente un método basado en el código IL de un método existente; la manipulación que quiero hacer es razonablemente fácil cuando el código está optimizado, pero se vuelve significativamente más difícil en el código no optimizado, debido a las instrucciones adicionales generadas por el compilador.


EDIT: más detalles sobre los no optimizaciones que me molestan ...

Vamos a considerar la siguiente implementación de la función factorial:

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

(Nota: Sé que hay son mejores formas de calcular el factorial, esto es solo un ejemplo)

IL generado con optimizaciones habilitadas i Es bastante sencillo:

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   

Pero la versión no optimizada es muy diferente

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   

Está diseñado para tener un solo punto de salida, al final. El valor a devolver se almacena en una variable local.

¿Por qué es esto un problema? Quiero generar dinámicamente un método que incluya la optimización de la cola de llamadas. El método optimizado se puede modificar fácilmente agregando el prefijo tail. antes de la llamada recursiva, ya que no hay nada después de la llamada, excepto ret. Pero con la versión no optimizada, no estoy tan seguro ... el resultado de la llamada recursiva se almacena en una variable local, luego hay una rama inútil que simplemente salta a la siguiente instrucción, la variable local se carga y se devuelve. Así que no tengo una manera fácil de verificar que la llamada recursiva realmente sea la última instrucción, por lo que no puedo estar seguro de que se pueda aplicar la optimización de la llamada final.

+1

AFAIK, no - eso no es posible –

+1

El compilador JIT siempre optimizará todos los métodos. – Steven

+0

@Steven, no si le dices que no lo haga (por ejemplo, con el indicador 'NoOptimization' en' MethodImplAttribute'). Pero de todos modos, mi pregunta es acerca de las optimizaciones del compilador, no la optimización JIT, ya que estoy interesado en el código IL que se genera. –

Respuesta

2

Si el método que va a utilizar como plantilla para el método dinámico es relativamente simple - y sin dependencias de otros métodos. Luego, simplemente colóquelo en su propio ensamblaje y active la optimización solo para ese ensamblaje.

En cuanto al problema original, ya que MSIL es un lenguaje basado en pila. Y las especificaciones garantizan estado de pila en la declaración ret, puede estar 100% seguro de que puede agregar un prefijo de cola sin problema. Sin embargo, tampoco es probable que agregue ningún beneficio, ya que realmente no he visto que el JIT use el prefijo de la cola para optimizar realmente el código final.

+0

¿Cuidar a los deudores de señalar qué es incorrecto? –

+0

Relacionado: http://stackoverflow.com/questions/491376 - Aparentemente '.tail' está optimizado en x64 pero no en x86 –

0

¿Hay alguna forma que podría generar el código de método original dinámicamente usando Microsoft.CSharp.CSharpCodeProvider?

Si controlas la compilación método, puede configurar las opciones cuando se llama al compilador utilizando CompilerOptions.

+0

Eso no ayudará; No quiero generar el código de forma dinámica (me refiero al código original) –

0

Nunca puede estar seguro de obtener la optimización de la cola de cola, siempre que use C#.

En particular, incluso con call ... ret la fluctuación de fase no garantiza una llamada cola. Así que el código IMO C# que depende de la optimización de la cola de llamadas (para evitar el desbordamiento de la pila) simplemente se rompe. En C# tail, la optimización de llamadas es puramente una optimización del rendimiento.

utilizar un lenguaje en el que se emiten de forma fiable las llamadas de cola, o reescribir su método de modo que no necesita llamadas de cola.

+0

Sé que C# no es adecuado para llamadas finales, solo estaba haciendo una prueba de concepto ... no se preocupe, no lo hago tiene la intención de usar esto en el código de producción;) –

Cuestiones relacionadas