簡體   English   中英

算術溢出檢查如何在c#中工作

[英]How does arithmetic overflow checking work in c#

由於性能原因,似乎默認情況下c#中的算術溢出檢查已關閉(請參閱https://stackoverflow.com/a/108776/2406234等)。

但是,如果我打開它,使用/ checked標志或checked關鍵字,運行時實際上如何執行檢查? (我正在努力更好地理解這種“性能打擊”的全部內容。)

算術運算一直在硬件級別提供支持,以指示執行的操作是否導致溢出。 在許多情況下,這些信息將被忽略(並且通常會在非常低的級別忽略),但是可以在每次操作后檢查此結果,如果發生溢出,則拋出一個新的異常。 所有這些檢查,以及通過各種抽象層傳播這些信息,當然都有成本。

C#中的溢出檢查通過在CIL中使用(或不使用)溢出檢查來工作。

例如,考慮C#代碼:

public static int AddInts(int x, int y)
{
    return x + y;
}

沒有溢出檢查,它將被編譯為類似於:

.method public hidebysig static int32 AddInts(int32 x, int32 y) cil managed 
{
    .maxstack 2
    IL_0000: ldarg.0
    IL_0001: ldarg.1
    IL_0002: add
    IL_0003: ret
}

通過溢出檢查,它將被編譯為類似於:

.method public hidebysig static int32 AddInts(int32 x, int32 y) cil managed 
{
    .maxstack 2
    IL_0000: ldarg.0
    IL_0001: ldarg.1
    IL_0002: add.ovf
    IL_0003: ret
}

如您所見,CIL表單使用不同的溢出檢查和非溢出檢查形式的add ,這同樣適用於在C#中checkedunchecked影響行為的每個操作。 這在混合了大量已檢查和未檢查操作的代碼中會更方便,但絕大多數時候,大多數時候一個組合在一起檢查或在方法中一起取消選中,因此C#的方法是為程序集設置默認值在一個塊中重寫幾乎一直對人類編碼器來說更方便。

當CIL進行jitted時,會發生什么情況取決於它所處理的處理器。 可能的結果是使用類似的溢出檢查指令(這會導致溢出中斷,這可能用於產生.NET想要的異常)或者像x86的jo一樣的跳轉溢出指令。

我正在努力更好地了解這種“性能打擊”的全部內容。

確實, unchecked幾乎總是像checked一樣快,並且通常更快,因為忽略溢出可以比通過阻止溢出更頻繁地允許更有效的路徑。 簡單地說,檢查溢出有時需要另外一個動作,雖然在大多數時候是低速的非常快,並且做某事幾乎總是比什么都不做要慢。*因為在你知道溢出的情況下不可能發生,在不檢查那種不可能的情況下確實有一點性能上的好處。

但是,這應該被視為unchecked的次要功能。

主要特征是它改變了算術運算的含義

對於兩個32位整數(例如), unchecked(x + y)表示“添加x和y並將結果強制轉換為32位二進制補碼數”,而checked(x + y)表示“添加x和y”並返回導致的32位二進制補碼數。

因此,當unchecked(int.MaxValue + int.MaxValue)返回-2這是正確答案 ,而checked(int.MaxValue + int.MaxValue) 沒有正確答案 ,因此引發異常。

它們實際上是不同的操作,並且在很多情況下我們希望 -2是第一次返回。

因此,我們主要關注的是“如果我們超越這種類型的極限,那么正確答案是什么?”

  1. 丟棄位造成的答案是正確答案,因為我們將這些值視為位組:使用unchecked
  2. 我們將這些值視為表示某些計數或度量的整數,但它們的高度意味着某種錯誤,或者我們不准備處理的數量:使用已checked
  3. 我們將這些值視為表示某些計數或度量的整數,並且仍然可以針對這些值正確處理數學:使用longBigInteger而不是int這樣您就可以正確處理所有可能性。 (如果性能分析顯示它有很大幫助,也許只有int快速路徑)。
  4. 我們正在考慮將這些值作為整數,但我們要么確信它們永遠不會如此規模,要么可以忍受具有奇怪結果的那些因為它必須是“垃圾進入垃圾”的情況:嚴格地說這適合checked使用,但未unchecked將具有相同的結果,因此我們可以使用它來獲得輕微的性能提升。

人們處理每個問題的頻率取決於他們的編程方式。 可能大多數程序通常會處理第四種情況(你的程序多少次處理超過幾百萬的東西?),我們應該在語義上使用checked ,但它沒有真正的區別,所以我們不妨得到它unchecked性能提升。

第一種情況在某類案件中非常普遍,特別是相對較低級別的案件; 程序算法通常適用於大多數“業務邏輯”中的第四種情況,並且是圖書館正在處理的許多較低級別資源中的第一種情況。

當我們需要一些接近“真實世界”數學的算術並且int不能削減它時,那么我們通常仍然需要“真實”結果,所以我們在第三種情況的某些變體中。

真的,第一個做一些算術的案例偶爾會說“對不起,我無法處理這個”很少是一個理想的行為; OverflowException通常是一種異常,它告訴開發人員他們遇到問題而不是我們捕獲的那種,然后變成對他們有幫助的用戶的錯誤消息。 因此,大多數情況下,當我們有第一個案例時,我們認為我們有第四個案例,但我們錯了。

因此,它可能有用:

  1. 將所有符合第一種情況的代碼(絕對應該unchecked代碼)標記為unchecked即使這對項目默認值是多余的。
  2. 標記所有真正應該拋出OverflowException代碼,因為你會做一些有用的東西(罕見)作為checked
  3. 使用unchecked大部分時間,那個小的性能提升,但在使用checked通過單元測試與調試要么所有的時間,偶爾運行,或者如果你有一些奇怪的行為。 (這里需要謹慎的地方取決於申請)。

*也存在分支錯誤預測的可能性,盡管非溢出情況最有可能是最常見的情況,也是最常見的情況。 通常,分支錯誤預測在這里並不像在其他情況下那樣大,包括手動檢查溢出。

暫無
暫無

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

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