2012-03-13 64 views
15

是否有一個屬性可用於告訴編譯器必須始終優化一個方法,即使全局/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之外的通話之後沒有任何內容。但是對於未優化的版本,我不太確定......遞歸調用的結果存儲在本地變量中,然後有一個無用的分支跳轉到下一條指令,本地變量被加載並返回。所以我沒有簡單的方法來檢查遞歸調用是否是最後的指令,所以我不能確定是否可以應用尾部調用優化。

+1

AFAIK,no - 這是不可能的 – 2012-03-13 10:56:01

+1

JIT編譯器將始終優化每個方法。 – Steven 2012-03-13 11:01:39

+0

@Steven,如果你不告訴它(例如使用'MethodImplAttribute'中的'NoOptimization'標誌)。但無論如何,我的問題是關於編譯器優化,而不是JIT優化,因爲我對生成的IL代碼感興趣。 – 2012-03-13 11:17:34

回答

2

如果您將用作動態方法模板的方法相對簡單 - 並且不依賴於其他方法。然後把它放在它自己的程序集中,併爲那個程序集打開優化。

就原始問題而言,因爲MSIL是一種基於堆棧的語言。而規格保證堆棧狀態在ret聲明,你可以100%確定你可以添加一個尾部前綴沒有問題。但是,實際上也不太可能增加任何好處,因爲我沒有真正看到JIT使用尾部前綴來實際優化最終的jitter代碼。

+0

Downvoter謹慎指出哪些不正確? – 2012-03-15 18:45:45

+0

相關:http://stackoverflow.com/questions/491376 - 顯然'.tail'在x64中進行了優化,但不是x86 – 2013-04-30 21:25:45

0

是否有無論如何你可以使用Microsoft.CSharp.CSharpCodeProvider動態生成原始方法代碼?

如果您控制方法編譯,您可以在使用CompilerOptions調用編譯器時設置選項。

+0

這不會幫助;我不想動態生成代碼(不是我原意的代碼) – 2012-03-14 23:51:01

0

只要您使用C#,您永遠無法確定是否獲得了尾部呼叫優化。

尤其是即使有call ... ret JITter也不保證尾部呼叫。因此,依靠尾部呼叫優化(避免堆棧溢出)的IMO C#代碼簡單地被破壞了。在C#尾部調用優化純粹是一種性能優化。

使用可靠地發出尾部呼叫的語言,或重寫您的方法,使其不需要尾部呼叫。

+0

我知道C#不適合用於尾部調用,我只是在做一個概念驗證......不要擔心,我不會打算在生產代碼中使用這個) – 2012-03-14 23:52:37