2013-06-29 205 views
2

他們告訴我們不要使用異常來控制我們的程序流,因爲拋出異常很慢。我從來沒有聽到任何解釋,爲什麼拋出異常這麼慢。爲什麼拋出異常這麼慢?

所以,問題是:

什麼拋出異常的機理是什麼,是特別活動牽涉其中可能有性能影響?

編輯:

一些澄清:我想聽到什麼額外的工作是由操作系統需要處理拋出異常。在用戶模式和內核模式之間是否存在一些昂貴的切換?或者,也許構建異常對象是昂貴的?或者,也許有什麼東西與切換程序流我缺少什麼?我的問題是編程語言不可知(我希望如此,但證明我錯了)。然而,如果你需要一些錨點,那麼我最感興趣的是與這個主題相關的.NET內部。

EDIT2:

我對異常性能沒有任何問題。我只是想了解這個機制的內部。

編輯3:

使我的問題更清楚。

+2

你指的是哪種語言的執行方式,誰說它的異常處理很慢? –

+0

我指的是拋出一般情況下的異常,我想每個人都會這樣說:我的學術老師,我的同事,我的老闆,每個人都在互聯網:) – 0lukasz0

+0

可能重複[.NET異常有多慢?](http:/ /stackoverflow.com/questions/161942/how-slow-are-net-exceptions) – Paparazzi

回答

6

異常處理需要一些複雜性和「魔力」。 Seconding @ Joni的迴應主要成本是收集堆棧跟蹤。當拋出一個異常時,運行時必須沿着堆棧的激活記錄尋找兼容的異常處理程序,最後執行該程序的每一步。所有這些都必須在運行時發生;它不能被編譯器修復。在像C++這樣的語言中,析構函數必須被執行。

異常處理本質上是帶外「特殊」處理模式。加速正常執行的東西,例如高速緩存,並不適用。 (我想這裏的參考地點會更糟)。這個處理可以被優化,但是由於exc處理被認爲是「特殊的」,所以它沒有被重視。

+0

最後,塊和析構函數不會引發拋出異常的開銷,而是使用返回值並手動調用callstack。這些最後的塊和析構函數將會執行。 – goji

3

異常在應用程序級別創建;沒有對它們的操作系統支持。沒有什麼特別的理由說明爲什麼拋出和捕獲一個異常應該比任何其他非局部控制傳輸慢,比如調用或從函數返回。

什麼使得例外情況比返回錯誤代碼更「慢」取決於編程環境的細節所要求的額外工作。例如在Java中,引發異常的最慢部分是填充堆棧跟蹤。

2

使用異常進行流量控制是一個壞主意的主要原因是我應該能夠附加一個調試器並告訴它打破異常...並且它在異常情況下中斷。如果代碼使用流量控制的異常,並且在正常運行情況下它會一直中斷,那麼調試非常難以排除異常情況等等,但是最終可能會錯過一個真正的例外情況。

如果我編寫測試驗證積極的功能,我應該能夠使用附加的調試器運行它們,並且它不會捕獲。如果你不能這樣做,你可能會濫用例外。

一個例子:比方說,我有代碼通過ID獲取項目。我的代碼確定地發現,該ID的項目不存在。我應該拋出NotFoundException?這是一個爭論點 - 我會說不 - 沒有任何例外/錯誤發生在這裏。你的代碼正確地發現沒有任何該ID存在,沒有發現錯誤發生。這些都是我看到異常被濫用並導致異常被拋出到非特殊情況下的情況。

2

你被誤導了,但實際代碼的結果可能不會改變。 .NET中的異常處理系統實際上相當快,這意味着它的性能與其他錯誤處理選項相當。然而,就像在另一個答案中提到的那樣,您只需要適當地使用例外 - 即僅適用於在正常運行應用程序期間不會發生的特殊情況。原因如下:

  • 分析涉及異常的代碼流比不涉及異常的代碼更復雜,所以你的「正常」代碼應該避免拋出異常。
  • 每次拋出異常時,Visual Studio調試器都會報告。它只是輸出窗口中的一行,但如果你濫用例外,那麼這個奇妙的功能很快就會變成一場噩夢。
  • 當引發特定類型的異常時,Visual Studio有能力中斷。如果您一直正確地使用異常(針對其預期用途,並且僅用於指示該類型的實際異常),則此功能提供了一種快速調試應用程序中與錯誤處理(恢復,報告等)有關的錯誤的方法。

當涉及到處理實際的異常情況時,異常處理實際上比其他事情(例如返回錯誤代碼)提供了性能優勢。當涉及到分支預測和提示時,JIT可以假設代碼將從不拋出一個異常,允許它生成有效使用處理器的任何可用分支預測功能的代碼,以避免包含錯誤的代碼的分支開銷處理功能,但不積極處理錯誤。

相關問題