回答:
私は言語実装の専門家ではありません(そのため、これを簡単に理解してください)が、最大のコストの1つは、スタックをほどいて、スタックトレース用に保存することです。私はこれが例外がスローされたときにのみ起こると思います(しかし、私は知りません)、そうなら、これは例外がスローされるたびにまともなサイズの隠しコストになります...別のコードでは、多くのことが起こっています。
EXCEPTIONAL動作の例外を使用している限り、問題はないと思います(そのため、プログラムを通過する通常の予想されるパスではありません)。
ここで3つのポイントを作成します。
まず、コードに実際にtry-catchブロックを含めることによるパフォーマンスの低下はほとんどまたはまったくありません。これをアプリケーションで使用しないようにする場合は、考慮すべきではありません。パフォーマンスヒットは、例外がスローされたときにのみ効果を発揮します。
他の人が述べたスタックの巻き戻し操作などに加えて例外がスローされると、スタックトレースなどの例外クラスのメンバーにデータを入力するために、ランタイム/リフレクションに関連する一連の事柄が発生することに注意してください。オブジェクトやさまざまな型メンバーなど
これは、例外を再スローする場合の一般的なアドバイスがthrow;
、例外を再度スローしたり、新しい例外を構築したりするのではなく、スタック情報がすべて収集されるのと同じ理由の1つだと思います。それがすべて保存されているスロー。
throw new Exception("Wrapping layer’s error", ex);
例外がスローされないときにtry / catch / finallyを使用するオーバーヘッド、または例外を使用してプロセスフローを制御するオーバーヘッドについて尋ねていますか?後者は、ダイナマイトの棒を使用して幼児の誕生日のろうそくを照らすのにいくらか似ており、関連するオーバーヘッドは次の領域に分類されます。
通常はアプリケーションのワーキングセットにない非常駐コードおよびデータにアクセスする例外がスローされるため、追加のページフォールトが予想されます。
上記の両方の項目は、通常「コールド」コードとデータにアクセスするため、メモリに負荷がかかっている場合は、ハードページフォールトが発生する可能性があります。
コストの実際の影響に関しては、これはその時点でコードで他に何が起こっているかによって大きく異なる可能性があります。Jon Skeetはここで良い要約を持っています、そしていくつかの役に立つリンクがあります。例外がパフォーマンスを著しく損なうところまでたどり着くと、パフォーマンスだけでなく例外の使用に関しても問題が発生するという彼の発言に同意する傾向があります。
私の経験では、最大のオーバーヘッドは、実際に例外をスローしてそれを処理することです。私はかつて、次のようなコードを使用して、誰かがオブジェクトを編集する権利を持っているかどうかを確認するプロジェクトに取り組みました。このHasRight()メソッドは、プレゼンテーションレイヤーのあらゆる場所で使用され、多くの場合、数百のオブジェクトに対して呼び出されました。
bool HasRight(string rightName, DomainObject obj) {
try {
CheckRight(rightName, obj);
return true;
}
catch (Exception ex) {
return false;
}
}
void CheckRight(string rightName, DomainObject obj) {
if (!_user.Rights.Contains(rightName))
throw new Exception();
}
テストデータベースがテストデータでいっぱいになると、新しいフォームを開くときなどに、非常に目に見える速度低下が発生します。
だから私はそれを次のようにリファクタリングしました-それは-後のクイック 'nダーティ測定によれば-約2桁速い
bool HasRight(string rightName, DomainObject obj) {
return _user.Rights.Contains(rightName);
}
void CheckRight(string rightName, DomainObject obj) {
if (!HasRight(rightName, obj))
throw new Exception();
}
つまり、通常のプロセスフローで例外を使用すると、例外なしで同様のプロセスフローを使用するよりも約2桁遅くなります。
一般に受け入れられている理論に反して、try
/ catch
はパフォーマンスに大きな影響を与える可能性があり、それは例外がスローされるかどうかです!
前者は、長年にわたって、マイクロソフトのMVPによるブログの記事のカップルでカバーされている、と私はあなたがそれらを簡単に見つけることができる信頼まだStackOverflowのは気にそんなにについての内容私のようにそれらのいくつかにリンクを提供しますので、フィラーの証拠:
try
catch
finally
ピーター・リッチーによる / /(およびパート2)のパフォーマンスへの影響は、try
/catch
/がfinally
無効にする最適化を探ります(そして、標準からの引用とともにこれについてさらに詳しく説明します)Parse
対TryParse
対ConvertTo
イアン・ハフによっては、「例外処理が非常に遅い」とピッチングでこの点を実証することを露骨に述べてInt.Parse
とInt.TryParse
お互いに...と主張している誰にもTryParse
用途がtry
/catch
舞台裏では、これはいくつかの光を当てるべき!/ を使用した場合と使用しない場合の逆アセンブルされたコードの違いを示すこの回答もあります。try
catch
コード生成で露骨に観察できるオーバーヘッドがあることは明らかであり、そのオーバーヘッドはMicrosoftの価値を認める人々によって認められているように見えます!それでも私はインターネットを繰り返しています ...
はい、わずかなコード行に対して数十の追加のMSIL命令があり、無効にされた最適化さえカバーしていないので、技術的にはマイクロ最適化です。
私は数年前に回答を投稿しましたが、プログラマーの生産性(マクロ最適化)に焦点を合わせたため、削除されました。
ここでは数ナノ秒の節約がないため、これは残念なことです。CPU時間は、人間が手作業で最適化した累積時間の多くを補う可能性があります。あなたの上司はどちらにもっとお金を払うのですか:あなたの時間の1時間、またはコンピュータが実行されている1時間?どの時点でプラグを抜いて、より高速なコンピュータを購入する時が来たと認めますか?
明らかに、コードだけでなく、優先順位を最適化する必要があります。私の最後の答えでは、2つのコードスニペットの違いを利用しました。
try
/ を使用するcatch
:
int x;
try {
x = int.Parse("1234");
}
catch {
return;
}
// some more code here...
try
/ を使用しないcatch
:
int x;
if (int.TryParse("1234", out x) == false) {
return;
}
// some more code here
プロファイリング/最適化(上記でカバー)でない場合は、時間を浪費する可能性が高いメンテナンス開発者の観点から検討します。これは、try
/のcatch
問題がなければ不要である可能性が高いため、スクロールする必要があります。ソースコード...それらの1つには、4行のボイラープレートガベージが追加されています。
クラスに導入されるフィールドが増えるにつれ、このボイラープレートのガベージはすべて(ソースコードと逆アセンブルされたコードの両方で)合理的なレベルを超えて蓄積されます。フィールドごとに4つの余分な行があり、それらは常に同じ行です...繰り返しを避けることを教えられていませんか?自作の抽象化の背後にtry
/を隠すことはできると思いますcatch
が、それでは例外を回避することもできます(つまり、を使用Int.TryParse
)。
これは複雑な例でもありません。try
/に新しいクラスをインスタンス化する試みを見てきましたcatch
。コンストラクター内のすべてのコードは、コンパイラーによって自動的に適用される特定の最適化から失格になる可能性があることを考慮してください。コンパイラーは指示されたとおりに実行しているのではなく、コンパイラーが遅いという理論を生み出すためのより良い方法は何ですか?
上記のコンストラクターによって例外がスローされ、その結果いくつかのバグがトリガーされると想定すると、貧しいメンテナンス開発者はそれを追跡する必要があります。gotoの悪夢のスパゲッティコードとは異なり、try
/ はスタックを同じメソッドの他の部分だけでなく他のクラスやメソッドにも移動catch
できるため、3次元で混乱を引き起こす可能性があるため、これはそれほど簡単な作業ではないかもしれません。、すべてがメンテナンス開発者によって監視されますが、難しい方法です!しかし、「後藤は危険だ」と言われていますね。
最後に、try
/にcatch
はメリットがあります。つまり、最適化を無効にするように設計されています。もしそうなら、それはデバッグ支援です!それはそれがそのために設計されたものであり、それがそれとして使用されるべきものです...
それも良い点だと思います。これは、マルチスレッドアプリケーションの安全で正気なメッセージパッシングアルゴリズムを無効にする可能性のある最適化を無効にし、可能な競合状態をキャッチするために使用できます;)これが、try / catchを使用すると考えられる唯一のシナリオです。それにも選択肢があります。
どのような最適化を行うtry
、catch
とfinally
無効?
AKA
はどのようにtry
、catch
そしてfinally
デバッグの助けとして役立ちますか?
それらは書き込みバリアです。これは標準から来ています:
12.3.3.13 try-catchステートメント
次の形式のステートメントstmtの場合:
try try-block catch ( ... ) catch-block-1 ... catch ( ... ) catch-block-n
- 明確な割り当て状態Vの冒頭のtry-ブロックがの明確な割り当て状態と同じであるVの冒頭のstmt。
- 明確な割り当て状態Vの冒頭にキャッチブロック-I (任意のため、私は)の明確な割り当て状態と同じであるVの冒頭のstmt。
- 明確な割り当て状態Vのエンドポイントでのstmtは(および場合のみ)あれば間違いなく割り当てられているVは間違いのエンドポイントに割り当てられているのtryブロックおよびすべてのキャッチブロック-iのすべてのための(I 1からn個)。
言い換えると、各try
ステートメントの始めに:
try
ステートメントに入る前に可視オブジェクトに行われたすべての割り当ては完了している必要があります。これには、開始時にスレッドロックが必要であり、競合状態のデバッグに役立ちます。try
ステートメントの前に確実に割り当てられていた未使用の変数割り当てを削除する同様のストーリーが各catch
ステートメントに当てはまります。あなたの内と仮定try
(のは、言わせ声明(またはコンストラクタまたはそれが呼び出す機能など)を使用すると、そのそれ以外の場合は無意味な変数に割り当てるgarbage=42;
)、コンパイラはその文、それはプログラムの観察可能な行動にどのように関係のないどんなにを排除することはできません。割り当ては、ブロックに入る前に完了する必要がありますcatch
。
それは価値があるものについてfinally
は、同様に低下する物語を伝えます:
12.3.3.14 Try-finallyステートメント
次の形式のtryステートメントstmtの場合:
try try-block finally finally-block
•の明確な割り当て状態V の冒頭のtry-ブロックがの明確な割り当て状態と同じであるVの冒頭のstmt。
•の明確な割り当て状態Vの初めに最終的にブロックがの明確な割り当て状態と同じであるVの冒頭のstmt。
•の明確な割り当て状態Vのエンドポイントでのstmtは間違いなく場合(および場合のみ)が割り当てられ、次のいずれかのOのVは間違いのエンドポイントに割り当てられているのtryブロック O V間違いのエンドポイントに割り当てられ、最終的にブロック (のような制御フロー転送した場合のgoto文が)行われ以内に始まることを試み、ブロック、および外の両端のtryブロック、そしてVはまた、間違いなくその上で割り当てられて考えられていますfinally-blockのエンドポイントにvが確実に割り当てられている場合の制御フロー転送。(これは唯一の場合ではありません。vがこの制御フロー転送で別の理由で確実に割り当てられている場合でも、確実に割り当てられていると見なされます。)
12.3.3.15 try-catch-finallyステートメント
以下のための明確な代入解析のtry - キャッチ - 最後に、フォームの声明:
try try-block catch ( ... ) catch-block-1 ... catch ( ... ) catch-block-n finally finally-block
声明であるかのように行われている試み - 最終的に囲む声明のtry - キャッチステートメントを:
try { try try-block catch ( ... ) catch-block-1 ... catch ( ... ) catch-block-n } finally finally-block
当時、これについて質問する人が多かったので、しばらく前に記事を書きました。これとテストコードは、http://www.blackwasp.co.uk/SpeedTestTryCatch.aspxにあります。
結論としては、try / catchブロックにはわずかなオーバーヘッドがありますが、無視できるほど小さいためです。ただし、数百万回実行されるループでtry / catchブロックを実行している場合は、可能であればブロックをループの外側に移動することを検討してください。
try / catchブロックの主要なパフォーマンス問題は、実際に例外をキャッチするときです。これは、アプリケーションに顕著な遅延を追加する可能性があります。もちろん、問題が発生した場合、ほとんどの開発者(および多くのユーザー)は、一時停止をこれから発生する例外として認識します。ここで重要なのは、通常の操作で例外処理を使用しないことです。名前が示すように、それらは例外的であり、それらがスローされないようにできる限りのことを行う必要があります。正しく機能しているプログラムの予想されるフローの一部として使用しないでください。
私は、作られたブログエントリ昨年のこのテーマについてを。見てみな。結論として、例外が発生しなければ、tryブロックのコストはほとんどかかりません。私のラップトップでは、例外は約36μsでした。それは予想よりも少ないかもしれませんが、これらの結果は浅いスタック上にあることに注意してください。また、最初の例外は本当に遅いです。
try
/ catch
あわやあわやすぎ?)、しかし、あなたはまた、測定結果を提供する、件名にブログを書かれている言語仕様といくつかのMSのMVPと議論しているようですあなたのアドバイスに反して...私が行った研究は間違っているという提案に私はオープンですが、それが何を言っているかを確認するためにあなたのブログエントリを読む必要があります。
try-catch
ブロックとtryparse()
メソッドを対象としていますが、概念は同じです。
コンパイラエラーメッセージ、コード分析警告メッセージ、およびルーチンで受け入れられる例外(特に、1つの場所でスローされ、別の場所で受け入れられる例外)のないコードを記述、デバッグ、および保守する方がはるかに簡単です。コードがより簡単になるため、平均してコードの記述が改善され、バグが少なくなります。
私にとって、そのプログラマーと品質のオーバーヘッドは、プロセスフローにtry-catchを使用することに対する第一の議論です。
例外によるコンピューターのオーバーヘッドは、それと比較して取るに足らないものであり、通常、アプリケーションが実際のパフォーマンス要件を満たす能力に関してはごくわずかです。
私はHafthorのブログ投稿が本当に好きです。このディスカッションに2セントを追加するために、データレイヤーで1種類の例外(DataAccessException)のみをスローさせるのは常に簡単だと言いたいのです。このようにして、私のビジネスレイヤーはどのような例外を予期し、それをキャッチします。次に、他のビジネスルールに応じて(つまり、ビジネスオブジェクトがワークフローに参加している場合など)、新しい例外(BusinessObjectException)をスローするか、再スローせずに続行します。
必要なときにいつでもtry..catchを使用して、賢く使用することをためらわないでください!
たとえば、このメソッドはワークフローに参加しています...
コメント?
public bool DeleteGallery(int id)
{
try
{
using (var transaction = new DbTransactionManager())
{
try
{
transaction.BeginTransaction();
_galleryRepository.DeleteGallery(id, transaction);
_galleryRepository.DeletePictures(id, transaction);
FileManager.DeleteAll(id);
transaction.Commit();
}
catch (DataAccessException ex)
{
Logger.Log(ex);
transaction.Rollback();
throw new BusinessObjectException("Cannot delete gallery. Ensure business rules and try again.", ex);
}
}
}
catch (DbTransactionException ex)
{
Logger.Log(ex);
throw new BusinessObjectException("Cannot delete gallery.", ex);
}
return true;
}
今日のコンパイラは一般的なケース、つまり例外が発生しない場合にオーバーヘッドを追加しないことを、Michael L. Scottによるプログラミング言語プラグマティクスで読むことができます。したがって、すべての作業はコンパイル時に行われます。ただし、実行時に例外がスローされると、コンパイラーはバイナリー検索を実行して正しい例外を見つける必要があり、これはユーザーが新しくスローするたびに発生します。
しかし、例外は例外であり、このコストは完全に許容できます。例外なしで例外処理を実行して、代わりにリターンエラーコードを使用しようとすると、おそらくすべてのサブルーチンにifステートメントが必要になり、これにより実際にリアルタイムのオーバーヘッドが発生します。ifステートメントがいくつかのアセンブリー命令に変換され、サブルーチンに入るたびに実行されることがわかっています。
私の英語についてすみません、それがあなたを助けることを願っています。この情報は引用された本に基づいています。詳細については、8.5例外処理を参照してください。
使用する必要のない場所で使用した場合の、try / catchブロックの最大コストの1つを分析してみましょう。
int x;
try {
x = int.Parse("1234");
}
catch {
return;
}
// some more code here...
そして、これがtry / catchのないものです:
int x;
if (int.TryParse("1234", out x) == false) {
return;
}
// some more code here
意味のない空白を数えずに、これら2つの同等のコードがバイト単位でほぼ正確に同じ長さであることに気づくかもしれません。後者には4バイト少ないインデントが含まれます。それは悪いことですか?
傷害に侮辱を加えるために、学生は入力がintとして解析されることができる間、ループすることに決めます。try / catchを使用しない場合の解決策は次のようになります。
while (int.TryParse(...))
{
...
}
しかし、try / catchを使用する場合、これはどのように見えますか?
try {
for (;;)
{
x = int.Parse(...);
...
}
}
catch
{
...
}
try / catchブロックはインデントを無駄にする魔法のような方法ですが、失敗した理由もわかりません!明らかな例外エラーで停止するのではなく、深刻な論理的欠陥を越えてコードが実行され続けるときに、デバッグをしている人がどのように感じるか想像してみてください。try / catchブロックは、怠惰な人間のデータ検証/衛生管理です。
小さなコストの1つは、try / catchブロックが実際に特定の最適化を無効にすることです。http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx。それも良い点だと思います。これは、マルチスレッドアプリケーションの安全で正気なメッセージパッシングアルゴリズムを無効にする可能性のある最適化を無効にし、可能な競合状態をキャッチするために使用できます;)これが、try / catchを使用すると考えられる唯一のシナリオです。それにも選択肢があります。
Int.Parse
した場合の大幅なパフォーマンス向上の分析ですInt.TryParse
。