簡體   English   中英

MS C#編譯器和非優化代碼

[英]MS C# compiler and non-optimized code

注意:我在發布的示例中發現了一些錯誤 - 編輯修復它

如果不啟用優化,官方C#編譯器會做一些有趣的事情。

例如,一個簡單的if語句:

int x;
// ... //
if (x == 10)
   // do something

如果優化,會變成以下內容:

ldloc.0
ldc.i4.s 10
ceq
bne.un.s do_not_do_something
// do something
do_not_do_something:

但如果我們禁用優化,它會變成這樣:

ldloc.0
ldc.i4.s 10
ceq
ldc.i4.0
ceq
stloc.1
ldloc.1
brtrue.s do_not_do_something
// do something
do_not_do_something:

我無法理解這一點。 為什么所有額外的代碼,似乎在源中不存在? 在C#中,這相當於:

int x, y;
// ... //
y = x == 10;
if (y != 0)
   // do something

有誰知道為什么這樣做?

我不完全理解問題的重點。 聽起來你問“為什么編譯器在優化開關關閉時產生未經優化的代碼?” 有點回答自己。

但是,我會捅它。 我認為問題實際上就是“什么樣的設計決策會導致編譯器發出聲明,存儲和加載本地#1,哪些可以被優化掉?”

答案是因為未經優化的codegen被設計為清晰,明確,易於調試,並鼓勵抖動生成不會積極收集垃圾的代碼。 我們實現所有這些目標的方法之一是為堆棧中的大多數值生成局部化 ,甚至是臨時值。 我們來看一個更復雜的例子。 假設你有:

Foo(Bar(123), 456)

我們可以將其生成為:

push 123
call Bar - this pops the 123 and pushes the result of Bar
push 456
call Foo

這是好的,有效的和小的,但它不符合我們的目標。 它清晰明確,但調試起來並不容易,因為垃圾收集器可能會變得激進。 如果Foo由於某種原因實際上沒有對其第一個參數執行任何操作,則允許GC在Foo運行之前回收Bar的返回值。

在未經優化的構建中,我們將生成更像的東西

push 123
call Bar - this pops the 123 and pushes the result of Bar
store the top of the stack in a temporary location - this pops the stack, and we need it back, so
push the value in the temporary location back onto the stack
push 456
call Foo

現在,抖動有一個很大的暗示,即“嘿抖動, 即使Foo不使用它,也要在當地保持活力一段時間

這里的一般規則是“在未優化的構建中使所有臨時值中的局部變量”。 你去吧; 為了評估“if”語句,我們需要評估一個條件並將其轉換為bool。 (當然條件不需要是bool類型;它可以是一個可以隱式轉換為bool的類型,或者是一個實現操作符true / operator false對的類型。)未經優化的代碼生成器被告知“積極地轉換所有臨時值進入當地人“,這就是你得到的。

我想在這種情況下,我們可以壓制那些在“if”語句中的條件,但這聽起來像是為我工作而沒有客戶利益 因為只要你的手臂確實有客戶利益,我就有一堆工作,我不打算改變未經優化的代碼生成器,它生成未優化的代碼,完全按照預期的方式生成。

我真的沒有看到這個問題,所有優化的代碼都是優化單個引用的本地(stloc ldloc組合)。

它出現在調試版本中的原因是,在使用它之前,您可以看到賦值給本地的值。

編輯:我現在看到另一個額外的ceq

更新2:

我看到發生了什么。 由於布爾值表示為0和!0,調試版本進行第二次比較。 OTOH,優化器可能可以證明代碼的安全性。

未經優化的代碼實際上就像:

int x, _local; // _local is really bool

_local = (x == 10) == 0;  // ceq is ==, not <, not sure why you see that
if (_local)  // as in C, iow _local != 0 implied
{
  ...
}

對於具體答案,您需要等待C#編譯器團隊中的某個人或與該組關系密切的人員詳細解釋此案例。

然而,這只是一般的代碼生成,其中常見的例程用來處理許多不同的情況下,對特定語句等的神器if你的情況。

在某些情況下,這種泛化導致功能性但通常不是最佳的代碼。 這就是為什么存在優化傳遞以對生成的代碼進行各種優化以移除冗余代碼,循環展開,窺視孔優化,代碼共享等的原因。

在調試模式下編譯時看到不太理想的代碼的其他原因是支持調試器,例如,可能會在代碼中插入NOP指令以便在調試器中運行時提供斷點,但在發布版本中刪除。

暫無
暫無

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

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