2016-12-06 105 views
4

網上有很多文章列出了在執行一段代碼之前C#JIT所做的優化。例如this post MSDN上談到:C#JIT編譯器是否優化了空檢查?

常量摺疊,常量和複製傳播,普通的子表達式 消除,循環不變,死存儲和死碼 消除代碼運動,寄存器分配,方法內聯,循環展開 (帶小體的小環)。

我的問題是:JIT編譯器是否也處理無用的空檢查?我無法找到任何來源來處理這個問題。

在同一篇文章我讀:

,因爲C#語言規範確保了空 對象引用任何調用拋出一個NullReferenceException,每次調用站點必須 確保實例不爲空。這是通過取消引用 實例引用完成的;如果它爲空,它將產生一個故障,即 變成此異常。

因此,假設我寫一段代碼是這樣的:再次

if (person != null) 
{ 
    Console.WriteLine(person.Name); 
} 

person.Name調用第二空檢查是克利裏沒用,編譯器可以將其刪除。或不?

,我讀了在Java中,這是已經完成(很多hereherehere之間的一些消息來源)。

如果C#也這樣做,你知道一些來源或文件談論?

如果C#沒有這樣做,你知道爲什麼嗎? Java JIT沒有遇到的.NET環境中實現這樣的功能是否存在固有的困難?

+2

僅作爲正常優化的副作用,如死碼消除。這應該是非常罕見的。一般來說,抖動優化器對執行空指針檢查的代碼沒有特別的瞭解,並將指針視爲易失性。在任何具有垃圾收集器的虛擬機中非常重要,該收集器在壓縮堆時任意修改指針。值得注意的是,C#編譯器本身可以消除檢查,當你使用elvis操作符('?.')時它會這樣做。 –

回答

3

Null檢查優化由編譯器(Roslyn,而不是抖動)in several cases,當它完全保存做到這一點。

例如,當您使用?(埃爾維斯算子)。

IL_0006: stloc.0    // Pop a value from stack into local variable 0 
IL_0007: ldloc.0    // Load local variable 0 onto stack 
IL_0008: brtrue.s IL_000c  // Branch to target if value is non-zero (true), short form 
IL_000a: br.s IL_0013   // Branch to target, short form 
IL_000c: ldloc.0    // Load local variable 0 onto stack 
IL_000d: call instance void Foo::Bar() // Call method indicated on the stack with arguments 

又如這樣的代碼:

new Bar().Foo(); 

編譯器生成用於此call指令和不callvirt(這意味着,上this沒有空校驗)

在其他情況下,則不能確定this不會爲空。

無論如何,空檢查真的很快。