2009-07-10 79 views
0

我一直在考慮嵌套的try/catch語句,並開始考慮在哪些條件下(如果有的話)JIT可以執行編譯後的IL的優化或簡化。.NET JIT是否優化嵌套的try/catch語句?

爲了說明,請考慮異常處理程序的以下功能等效表示。

// Nested try/catch 
try 
{ 
    try 
    { 
    try 
    { 
     foo(); 
    } 
    catch(ExceptionTypeA) { } 
    } 
    catch(ExceptionTypeB) { } 
} 
catch(ExceptionTypeC) { } 

// Linear try/catch 
try 
{ 
    foo(); 
} 
catch(ExceptionTypeA) { } 
catch(ExceptionTypeB) { } 
catch(ExceptionTypeC) { } 

假定沒有額外的變量引用或嵌套try語句的堆棧幀中的函數調用,可以在JIT斷定該堆棧幀可被摺疊到線性例子?

現在下面的例子怎麼樣?

void Try<TException>(Action action) 
{ 
    try 
    { 
    action(); 
    } 
    catch (TException) { } 
} 

void Main() 
{ 
    Try<ExceptionC>(Try<ExceptionB>(Try<ExceptionA>(foo))); 
} 

我不認爲有任何方式的JIT內聯委託調用,所以這個例子不能降低到以前的一個。然而,如果foo()引發ExceptionC,與線性示例相比,此解決方案性能較差嗎?我懷疑從委託調用中拆除堆棧幀會有額外的成本,即使幀中包含的額外數據很少。

+1

您是否看過前兩種情況下的IL? – womp 2009-07-10 07:13:09

回答

8

值得注意的是,在第一種情況下,他們只是只有功能相當,當你在catch塊中什麼都不做時。否則,考慮一下:

try 
{ 
    foo(); 
} 
catch (IOException) 
{ 
    throw new ArgumentException(); // Bubbles up to caller 
} 
catch (ArgumentException) 
{ 
    Console.WriteLine("Caught"); 
} 

VS

try 
{ 
    try 
    { 
     foo(); 
    } 
    catch (IOException) 
    { 
     throw new ArgumentException(); // Caught by other handler 
    } 
} 
catch (ArgumentException) 
{ 
    Console.WriteLine("Caught"); 
} 
在這種情況下,差別是顯而易見的

現在,但如果catch塊調用一些任意的方法中,JIT是如何打算知道可能被拋出?最好謹慎。

這給我們留下了JIT對空白捕捉塊進行優化的選擇 - 這種做法首先受到強烈阻礙。我不希望JIT花費時間來檢測錯誤的代碼並使其運行得更快一些 - 如果確實有任何性能差異的話。

+0

謝謝你的分析喬恩。在我看來,那些空着的捕手處理人員正在進行無投擲操作。但是,您的分析仍然適用,因爲OutOfMemoryException和ThreadAbortExcetpion之類的異常可能由於foo()外部的原因而發生。 – 2009-07-10 14:30:57

4

我對try/catch/finally區域對性能的理解是這些區域對代碼的正常執行是透明的。也就是說,如果你的代碼沒有拋出任何異常來捕獲,那麼try/catch/finally區域就會對代碼執行性能產生影響。

但是,當引發異常時,運行時會從引發它的站點開始步進堆棧,檢查元數據表以查看有問題的站點是否包含在任何關鍵的try塊中。如果找到了一個(並且它有一個符合條件的catch塊或finally塊),那麼會識別相關的處理程序並執行分支到這一點。

從性能的角度來看,提升和處理異常的過程是昂貴的。程序員不應該使用異常作爲一種信號或控制程序流程的方式,而不是在特殊情況下(雙關意圖)。

+0

+1,用於解釋try-catch塊的運行時成本 – 2009-07-13 07:34:17