2010-02-07 55 views
30

幾天前,在寫溢出的this question的回答時,我對C#編譯器感到有些驚訝,他沒有按照我預期的那樣做。請看下面的代碼片斷:C#編譯器不會優化不必要的轉換

第一:

object[] array = new object[1]; 

for (int i = 0; i < 100000; i++) 
{ 
    ICollection<object> col = (ICollection<object>)array; 
    col.Contains(null); 
} 

第二:

object[] array = new object[1]; 

for (int i = 0; i < 100000; i++) 
{ 
    ICollection<object> col = array; 
    col.Contains(null); 
} 

在兩個片段之間的代碼唯一的區別是鑄造到ICollection的<對象>。由於object []明確實現了ICollection對象>接口,因此我期望這兩個片段可以編譯爲相同的IL,因此是相同的。然而,在對它們進行性能測試時,我發現後者的速度是前者的6倍。

比較兩個片段的IL後,我注意到兩種方法都是相同的,除了castclass IL指令在第一個片段中。

感到驚訝,我現在想知道爲什麼C#編譯器不是'聰明'在這裏。事情並不像看起來那麼簡單,那麼爲什麼C#編譯器在這裏有點天真?

+0

你使用了哪些編譯器選項? – Richard 2010-02-07 12:58:57

+0

以發佈模式編譯(優化代碼開啓)。 – Steven 2010-02-07 13:16:12

回答

32

我的猜測是你在優化器中發現了一個小錯誤。數組中有各種特殊代碼。感謝您的關注。

+18

只有埃裏克可以發佈一個答案,說「哎呀,我們做了一個噓聲」,並收到10個upvotes。優秀! ;) – Aaronaught 2010-02-07 19:05:37

4

這是一個粗略的猜測,但我認爲它是關於數組與其泛型IEnumerable的關係。

在.NET Framework 2.0版中, 陣列類實現 System.Collections.Generic.IList, 了System.Collections.Generic.ICollection, 和 System.Collections.Generic.IEnumerable 通用接口。 實現在運行時提供給陣列 ,因此不是 工具對文檔構建 可見。其結果是,通用 接口不出現在陣列 類 聲明語法,並沒有參考 主題爲接口的成員,只有鑄造數組 通用接口類型是 訪問(明確 接口實現)。關鍵 當您將 陣列轉換爲其中一個接口時,要注意的事項是 ,即添加,插入或刪除元素的成員 NotSupportedException。

請參閱MSDN Article

目前還不清楚這是否與.NET 2.0+相關,但在這種特殊情況下,如果編譯器只在運行時才變得有效,編譯器無法優化表達式,這是非常有意義的。

+1

你的猜測並不是那麼糟糕,因爲這似乎只發生在數組中。當你改變行「object [] array = new object [1];」到「Collection array = new Collection ();」編譯器成功地優化了拋出。然而,我並不完全滿意,因爲雖然文章中提到「在運行時向數組提供實現」,但C#編譯器實際上知道T []實現ICollection 。否則,聲明「ICollection col = array;」不會編譯。我們仍然不知道這裏到底發生了什麼。 – Steven 2010-02-07 14:45:38

2

這看起來不僅僅是在編譯器中錯過了壓制演員的機會。它會工作,如果你這樣寫:

ICollection<object> col = array as ICollection<object>; 

這表明它過於保守,因爲強制轉換可以拋出異常。但是,當您投射到非通用ICollection時,它確實有效。我認爲他們忽略了它。

這裏有一個更大的優化問題,JIT編譯器不適用循環不變吊裝優化。它應該重寫如下代碼:

object[] array = new object[1]; 
ICollection<object> col = (ICollection<object>)array; 
for (int i = 0; i < 100000; i++) 
{ 
    col.Contains(null); 
} 

這是C/C++代碼生成器中的標準優化,例如。儘管如此,JIT優化器無法在發現這種可能的優化所需的分析類型上耗費大量的週期。幸運的是,優化的託管代碼仍然是可調試的。而且C#程序員仍然有責任編寫高性能代碼。

+0

在談論JIT時,JIT也沒有優化拋出。 JIT沒有這樣做是非常明顯的,正如你已經說過的那樣:「JIT優化器不能消耗很多週期」。 – Steven 2010-02-08 08:24:09