2012-03-13 14 views
15

C'è un attributo che posso usare per dire al compilatore che un metodo deve essere sempre ottimizzato, anche se lo switch del compilatore globale /o+ non è impostato?Posso forzare il compilatore a ottimizzare un metodo specifico?

Il motivo per cui lo chiedo è perché sto giocando con l'idea di creare dinamicamente un metodo basato sul codice IL di un metodo esistente; la manipolazione che voglio fare è ragionevolmente facile quando il codice è ottimizzato, ma diventa significativamente più difficile nel codice non ottimizzato, a causa delle istruzioni extra generate dal compilatore.


EDIT: ulteriori dettagli sulle non-ottimizzazioni che mi preoccupano ...

Consideriamo la seguente implementazione della funzione fattoriale:

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

(Nota: So che ci sono modi migliori per calcolare il fattoriale, questo è solo un esempio)

Il IL generato con ottimizzazioni abilitate i s molto semplice:

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   

Ma la versione non ottimizzata è molto diversa

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   

È stato progettato per avere un solo punto di uscita, alla fine. Il valore da restituire è memorizzato in una variabile locale.

Perché questo è un problema? Voglio generare dinamicamente un metodo che include l'ottimizzazione delle chiamate tail. Il metodo ottimizzato può essere facilmente modificato aggiungendo il prefisso tail. prima della chiamata ricorsiva, poiché non vi è nulla dopo la chiamata tranne ret. Ma con la versione non ottimizzata, non ne sono così sicuro ... il risultato della chiamata ricorsiva è memorizzato in una variabile locale, quindi c'è un ramo inutile che salta all'istruzione successiva, la variabile locale viene caricata e restituita. Quindi non ho un modo semplice per verificare che la chiamata ricorsiva sia davvero l'ultima istruzione, quindi non posso essere sicuro che l'ottimizzazione della chiamata di coda possa essere applicata.

+1

AFAIK, no, questo non è possibile –

+1

Il compilatore JIT ottimizzerà sempre ogni metodo. – Steven

+0

@Steven, non se lo dici a non (ad esempio con il flag 'NoOptimization' nel' MethodImplAttribute'). Comunque, la mia domanda riguarda le ottimizzazioni del compilatore, non l'ottimizzazione JIT, dal momento che sono interessato al codice IL che viene generato. –

risposta

2

Se il metodo che si utilizza come modello per il metodo dinamico è relativamente semplice e senza dipendenze da altri metodi. Quindi inseriscilo nel proprio assieme e attiva l'ottimizzazione solo per quell'assembly.

Per quanto riguarda il problema originale, poiché MSIL è un linguaggio basato su stack. E le specifiche garantiscono lo stato dello stack nell'istruzione ret e puoi essere sicuro al 100% di poter aggiungere un prefisso di coda senza problemi. Tuttavia, è anche improbabile che aggiunga qualche beneficio in quanto non ho mai visto il JIT usare il prefisso di coda per ottimizzare effettivamente il codice finalmente jitted.

+0

Assistenza per il downnoter per indicare cosa non è corretto? –

+0

Correlati: http://stackoverflow.com/questions/491376 - Apparentemente '.tail' è ottimizzato in x64 ma non x86 –

0

Esiste comunque la possibilità di generare il codice del metodo originale in modo dinamico utilizzando Microsoft.CSharp.CSharpCodeProvider?

Se si controlla la compilazione del metodo, è possibile impostare le opzioni quando si chiama il compilatore utilizzando CompilerOptions.

+0

Ciò non aiuterà; Non voglio generare il codice dinamicamente (non il codice originale intendo) –

0

Non si può mai essere sicuri di ottenere un'ottimizzazione delle chiamate tail, a patto che si usi C#.

In particolare, anche con call ... ret, JITter non garantisce una coda. Quindi il codice IMO C# che si basa sull'ottimizzazione delle chiamate tail (per evitare lo stack overflow) è semplicemente rotto. In C# l'ottimizzazione della chiamata di coda è puramente un ottimizzazione delle prestazioni.

Utilizzare una lingua in cui le chiamate tail vengono emesse in modo affidabile o riscrivere il metodo in modo che non siano necessarie chiamate tail.

+0

So che C# non è adatto per le chiamate di coda, stavo solo facendo una dimostrazione del concetto ... non ti preoccupare, non lo faccio intendi utilizzare questo nel codice di produzione;) –