簡體   English   中英

C#優化和副作用

[英]C# optimizations and side effects

C#編譯器或JITter可以進行優化會產生明顯的副作用嗎?

我已經離開了一個例子。

var x = new Something();
A(x);
B(x);

當調用A(x) x ,保證在A的末尾保持活動狀態 - 因為B使用相同的參數。 但如果B被定義為

public void B(Something x) { }

然后優化器可以消除B(x) ,然后可能需要GC.KeepAlive(x)調用。

這個優化實際上可以由JITter完成嗎?

除了堆棧跟蹤更改之外,是否存在可能具有可見副作用的其他優化?

如果您的函數B不使用參數x,那么消除它並提前收集x 沒有任何可見的副作用。

要成為“可見的副作用”,它們必須對程序可見,而不是對調試器或對象查看器等外部工具可見。

當調用A(x)x時,保證在A的末尾保持活動狀態 - 因為B使用相同的參數。

這句話是錯誤的。 假設方法A總是拋出異常。 抖動可能知道B永遠不會到達,因此x可以立即釋放。 假設方法A在最后一次引用x之后進入無條件無限循環; 再次,抖動可以知道通過靜態分析,確定x將永遠不會被再次引用,並安排它被清理。 我不知道抖動是否實際執行了這些優化; 他們似乎很狡猾,但他們是合法的。


這種優化(即,盡早清除未在任何地方使用的參考)實際上可以由JITter完成嗎?

是的,在實踐中,它已經完成。 這不是一個可觀察到的副作用。

這可以通過規范的第3.9節來證明,為方便起見我引用它:

如果對象或其任何部分無法通過任何可能的繼續執行來訪問,除了運行析構函數之外,該對象被認為不再使用,並且它有資格進行銷毀。 C#編譯器和垃圾收集器可以選擇分析代碼以確定將來可以使用對對象的哪些引用。 例如,如果范圍內的局部變量是對象的唯一現有引用,但該過程中當前執行點的任何可能的繼續執行中從不引用該局部變量,則垃圾收集器可能(但是不要求將對象視為不再使用。


C#編譯器或JITter可以進行優化會產生明顯的副作用嗎?

您的問題在規范的第3.10節中得到了解答,為方便起見,我在此引用:

執行C#程序,以便在關鍵執行點保留每個執行線程的副作用。

副作用定義為易失性字段的讀取或寫入,對非易失性變量的寫入,對外部資源的寫入以及拋出異常。

必須保留這些副作用的順序的關鍵執行點是對volatile字段,鎖定語句以及線程創建和終止的引用。

執行環境可以自由更改C#程序的執行順序,但受以下限制:

數據依賴性保留在執行的線程中。 也就是說,計算每個變量的值,就好像線程中的所有語句都以原始程序順序執行一樣。

保留初始化排序規則。

關於易失性讀取和寫入,保留了副作用的順序。

此外,執行環境不需要評估表達式的一部分,如果它可以推斷出不使用該表達式的值並且不產生所需的副作用(包括由調用方法或訪問volatile字段引起的任何副作用)。

當程序執行被異步事件(例如另一個線程拋出的異常)中斷時,不能保證可觀察的副作用在原始程序順序中可見。

倒數第二段是我相信你最關心的一段; 也就是說,在影響可觀察的副作用方面,允許運行時的優化是什么? 允許運行時執行任何不影響可觀察副作用的優化。

請注意,特別是數據依賴性僅保留執行的線程中。 從另一個執行線程觀察時, 保證保留數據依賴性。

如果這不能回答您的問題, 請提出更具體的問題 特別是,如果您不考慮上面給出的定義以匹配您對“可觀察到的副作用”的定義,則需要仔細而精確地定義“可觀察到的副作用”以更詳細地回答您的問題。

在你的問題中包括B只是混淆了這個問題。 鑒於此代碼:

var x = new Something();
A(x);

假設A(x)是托管代碼,則調用A(x)維護對x的引用,因此垃圾收集器在A返回之前不能收集x 或者至少在A不再需要之前。 JITer完成的優化(缺少錯誤)不會過早收集x

您應該通過“可見的副作用”來定義您的意思。 人們希望JITer優化至少會產生使代碼更小或更快的副作用。 那些“可見嗎?” 或者你的意思是“不受歡迎?”

Eric Lippert已經開始了一個關於重構的偉大系列,這使我相信C#編譯器和JITter確保不會引入副作用。 第1部分第2部分目前在線。

暫無
暫無

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

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