Try-Catch は本当にプログラムのパフォーマンスに影響しますか?
今日、私は Try-Catch の使用について TL と議論していました。見た目を美しくするために、このメソッドのコードをすべて Try-Catch に入れるかどうかについてです。私はそれを当然のことだと思っていましたが、私は try-catch の実装メカニズムを詳しく調べたことがなく、説得力のある理由を与えることができません。今日、インターネット上で .net try-catch の分析を見つけたので共有します。
Try-Catch メカニズムとそのパフォーマンスへの影響を分析した投稿が数多くあります。
しかし、Try-Catch が、特にホスティング環境でシステム パフォーマンスを消費しすぎるという証拠はありません。庭のネチズンが StopWatch を使用して、Try-Catch を使用しないコードと比較して、さまざまな状況下で Try-Catch のコード実行時間インジケーターを分析したことを覚えています。結果はそれほど変わりませんでした。
次に、ILに基づいてTry-Catchを分析してみましょう。
● メカニズム分析
.Net の基本的な例外キャプチャおよび処理メカニズムは、try...catch...finally ブロックによって完成され、例外の監視、キャプチャ、処理がそれぞれ完了します。 try ブロックは 0 個以上の catch ブロックに対応でき、0 個または 1 つのfinally ブロックに対応できます。ただし、catch のない try は無意味であるように見えます。try が複数の catch に対応する場合、例外が検出された後、CLR は catch ブロックのコードを上から下に検索し、例外フィルターを通じて対応する例外をフィルターします。見つからない場合、CLR は呼び出しスタックに沿って上位レベルまで一致する例外を検索します。対応する例外がスタックの最上位に見つからない場合は、この時点でハンドルされない例外がスローされます。 catch ブロックは実行されません。したがって、try に最も近い catch ブロックが最初に走査されます。
次のコードがある場合:
try
{
Convert.ToInt32("Try");
}
catch (FormatException ex1)
{
string CatchFormatException = "CatchFormatException"
}
catch (NullReferenceException ex2)
{
string CatchNullReferenceException = "CatchNullReferenceException";
}
finally
{
stringFinally = "Finally"; }
対応するILは次のとおりです。 .EventArgs e) cil 管理
{
// コード サイズ 53 (0x35)
.maxstack 1
.locals init ([0] class [mscorlib]System.FormatException ex1,
[1] string CatchFormatException,
[2] class [mscorlib]System.NullReferenceException ex2、
[3] string CatchNullReferenceException、
[4] string 最後に)
IL_0000: nop
IL_0001: nop
IL_0002: ldstr "Try"
IL_0007: call int32 [mscorlib]System.Convert:: ToInt32(文字列)
IL_000c: ポップ
IL_000d: nop
IL_000e: Leave.s IL_0026
IL_0010: stloc.0
IL_0011: nop
IL_0012: ldstr "CatchFormatException"
IL_0017 : stloc.1
IL_0018 : いいえ
IL_0019: 出発します.s IL_0026
IL_001b : stloc.2
IL_001c: nop
IL_001d: ldstr "CatchNullReferenceException"
IL_0022: stloc.3
IL_0023: nop
IL_0024: Leave.s IL_0026
IL_0026: いいえ
IL_0027: .s を残します IL_0033
IL_0029 : nop
IL_002a: ldstr "最後に"
IL_002f: stloc.s 最後に
IL_0031: nop
IL_0032: endfinally
IL_0033: nop
IL_0034: ret
IL_0035:
// 例外カウント 3
.IL_0001 から IL_00 まで 10 回キャッチ [ mscorlib]System.FormatException ハンドラー IL_0010 ~ IL_001b
.try IL_0001 ~ IL_0010 catch [mscorlib]System.NullReferenceException ハンドラー IL_001b ~ IL_0026
.try IL_0001 ~ IL_0029 Final ハンドラー IL_0029 ~ IL_0033
} // メソッド Form1::Form1_Load の終了
最後の数行のコードは、IL が例外処理をどのように処理するかを明らかにします。最後の 3 行の各項目は例外処理節と呼ばれ、EHC は例外処理テーブルを形成し、EHT は ret return 命令によって通常のコードから分離されます。
FormatException が EHT で 1 位にランクされていることがわかります。
コードが正常に実行されるか、その逆の場合、CLR は EHT を走査します。
1. 例外がスローされた場合、CLR は例外をスローしたコードの「アドレス」に基づいて対応する EHC を見つけます (この例では、IL_0001 から IL_0010 が検出コードの範囲です)。 EHC と FormatException が最初にトラバースされ、これが適切な EHC です。
2. 返されたコードアドレスが IL_0001 ~ IL_0029 の範囲内にある場合、コードの実行が成功して返されたかどうかに関係なく、finally ハンドラー、つまり IL_0029 ~ IL_0033 のコードも実行されます。
実際、catch とfinally の走査作業は個別に実行されます。CLR が最初に行うことは、適切な catch ブロックが見つかると、対応するfinally とこのプロセスを走査することです。コンパイラは C# の try...catch...finally を IL の 2 レベルのネストに変換するため、少なくとも 2 回再帰的に実行されます。
もちろん、対応する catch ブロックが見つからない場合、CLR は最終的に直接実行され、すぐにすべてのスレッドを中断します。 Final ブロック内のコードは、try が例外を検出したかどうかに関係なく、必ず実行されます。
● 改善提案
上記から、次のように結論付けることができます。
"Try-Catch" が使用され、例外がキャッチされた場合、CLR が行うことは、例外処理テーブル内の Catch 項目を走査し、次に例外を走査することだけです。テーブルの Final 項目を再度処理する場合、ほとんどすべての時間が例外処理テーブルの走査に費やされます。例外がキャッチされない場合、CLR は例外処理テーブルの Final 項目のみを走査し、必要な時間は最小限になります。
「Try-Catch」トラバーサル後に対応する操作を実行するのにかかる時間は、特定のコードに従って決定されます。「Try-Catch」は監視とトリガーのみを引き起こし、コード時間のこの部分は「Try」としてカウントされるべきではありません。 -キャッチ」「消費。
したがって、パフォーマンスとコード レビューの両方を考慮することができます。一般に、次のガイドラインを持つことが推奨されます。
1. CLR に明確な例外情報を与えるようにし、例外をフィルターするために Exception を使用しないでください。
2. 記述しないようにしてください。 try...catch in ループ内で実行します
3. できる限り少ないコードを試し、必要に応じて複数の catch ブロックを使用し、スローされる可能性が最も高い例外タイプを try に最も近い位置に記述します
4. 実行しないでください。 Exception オブジェクトを 1 つ宣言するだけですが、それは処理しません。これを行うと、例外処理テーブルの長さが無駄に増加します。
5. パフォーマンス カウンター ユーティリティの「CLR Exceptions」を使用して例外を検出し、適切に最適化します。
6. メンバーの Try-Parse モードを使用します。例外がスローされた場合は、それを false に置き換えます。
結論、ただし Try-Catch は消費します。時間はかかりますが、プログラマがそれについて話す必要はありません。上記の分析を通じて、「Try-Catch」がパフォーマンスを消費する、またはパフォーマンスに影響を与えると言うよりも、「Try-Catch」は単なるものであると言ったほうがよいでしょう。他のコードと同様にパフォーマンスを消費する一般的なコードですが、そうではありません。 コード作成のレビューの考慮事項に関しては、できるだけ「Try-Catch」に注意を払う方がよいでしょう。