簡體   English   中英

C#編譯器會優化此代碼嗎?

[英]Will the C# compiler optimize this code?

我經常遇到這種情況。 乍一看,我認為,“這是糟糕的編碼; 我正在執行一個方法兩次並且必然會得到相同的結果。“但是在想到這一點時,我不得不懷疑編譯器是否像我一樣聰明並且可以得出相同的結論。

var newList = oldList.Select(x => new Thing {
    FullName = String.Format("{0} {1}", x.FirstName, x.LastName),
    OtherThingId = x.GetOtherThing() != null : x.GetOtherThing().Id : 0 // Might call x.GetOtherThing() twice?
});

編譯器的行為是否依賴於GetOtherThing方法的內容? 說它看起來像這樣(有點類似於我現在的真實代碼):

public OtherThing GetOtherThing() {
    if (this.Category == null) return null;
    return this.Category.OtherThings.FirstOrDefault(t => t.Text == this.Text);
}

這將禁止對這些對象來自的任何商店進行非常糟糕的異步更改,如果連續運行兩次,肯定會返回相同的內容。 但如果它看起來像這樣(為了論證而荒謬的例子):

public OtherThing GetOtherThing() {
    return new OtherThing {
        Id = new Random().Next(100)
    };
}

連續兩次運行將導致創建兩個不同的對象,並且很可能具有不同的ID。 編譯器在這些情況下會做什么? 它是否像我在第一個清單中所展示的那樣低效?

自己做一些工作

我運行了與第一個代碼清單非常相似的東西,並在GetOtherThing實例方法中放置了一個斷點。 斷點被擊中一次。 所以,看起來結果確實是緩存的。 在第二種情況下會發生什么,該方法可能每次返回不同的東西? 編譯器會不正確地優化? 我發現結果有什么警告嗎?

編輯

那個結論無效。 請參閱@ usr的答案下的評論。

這里有兩個編譯器需要考慮:將C#轉換為IL的C#編譯器,以及將IL轉換為機器代碼的IL編譯器 - 稱為抖動,因為它恰好發生在時間上。

Microsoft C#編譯器肯定沒有這樣的優化。 方法調用生成為方法調用,故事結束。

允許抖動執行您描述的優化, 前提是無法檢測到這種情況 例如,假設你有:

y = M() != 0 ? M() : N()

static int M() { return 1; }

允許抖動將此程序轉換為:

y = 1 != 0 ? 1 : N()

或者就此而言

y = 1;

抖動是否這樣做是一個實現細節; 如果你關心的話,你將不得不向專家詢問抖動是否確實會執行此優化。

同樣,如果你有

static int m;
static int M() { return m; }

那么抖動可以將其優化成

y = m != 0 ? m : N()

甚至進入:

int q = m;
y = q != 0 ? q : N();

因為只要該字段不是易失性的,就允許抖動連續轉換兩個字段讀取而不插入單個字段讀取。 無論它是否這樣做都是一個實現細節; 問一個抖動的開發人員。

但是,在后一個例子中,抖動不能忽略第二次調用,因為它有副作用。

我運行了與第一個代碼清單非常相似的東西,並在GetOtherThing實例方法中放置了一個斷點。 斷點被擊中一次。

這是非常不可能的。 在調試時幾乎所有優化都會被關閉,這樣就可以更容易地進行調試。 正如福爾摩斯從未說過的那樣 ,當你消除不可能的事情時,最可能的解釋是原來的海報是錯誤的。

如果您無法區分,編譯器只能應用優化。 在您的“隨機”示例中,您可以清楚地區分出來。 它不能以這種方式“優化”。 它會違反C#規范。 事實上,該規范並未談及優化問題。 它只是說你應該觀察程序做什么。 在這種情況下,它指定應繪制兩​​個隨機數。

在第一個示例中,可以應用此優化。 它在實踐中永遠不會發生。 以下是一些令人困難的事情:

  • 查詢操作的數據可以通過您的虛函數調用來更改,或者您的lambda( t => t.Text == this.Text )可以更改列表。 非常陰險。
  • 它可以被另一個線程改變。 我不確定.NET內存模型對此有何看法。
  • 它可以通過反思來改變。
  • 必須證明計算總是返回相同的值。 你會如何證明這一點? 您需要分析可能運行的所有代碼。 包括虛擬調用和依賴於數據的控制流。

所有這些都必須適用於非內聯方法和跨程序集。

C#編譯器無法執行此操作,因為它無法查看mscorlib。 修補程序版本可能隨時更改mscorlib。

JIT是一個糟糕的JIT(唉),它針對編譯速度進行了優化(唉)。 它沒有這樣做。 如果您懷疑當前的JIT是否會進行一些高級優化,那么可以肯定它不會。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM