5

パフォーマンスが悪いため、通常のエラー処理に例外を使用するべきではないとよく言われます。私の推測では、パフォーマンスの低下は、新しい例外オブジェクトのインスタンス化、スタック トレースの生成などを行う必要があることが原因であると考えられます。では、なぜ軽量の例外を用意しないのでしょうか? 次のようなコードは論理的に健全です。

string ageDescription = "Five years old";
try {
    int age = int.Parse(ageDescription);
}
catch (Exception) {
    // Couldn't parse age; handle parse failure
}

それでもTryParse、例外のオーバーヘッドを回避するために代わりに使用することをお勧めします。しかし、例外がスレッドの開始時に初期化された単なる静的オブジェクトである場合、例外をスローするコードは、エラー コード番号とおそらくエラー文字列を設定するだけです。スタック トレースも、新しいオブジェクトのインスタンス化もありません。これは「軽量の例外」であるため、例外を使用するためのオーバーヘッドが大幅に削減されます。そのような軽量の例外がないのはなぜですか?

4

6 に答える 6

4

例外オブジェクトのインスタンス化は、全体の中で最小の問題です。真のパフォーマンス キラーは、制御フローがプログラムの実行を停止し、スローされた例外をキャッチできる可能性のあるハンドラー (catch ブロック) のコール スタックを検索し、正しいハンドラー (およびその finally ブロック) を実行する必要があることです。 、指示されたときに例外を再スローし、適切な場所、つまり最後のハンドラーの後でプログラムの実行を続行します。あなたの「軽量」例外の考えは、例外オブジェクトを作成して保存する必要があり、現在可能になっているタイプによる例外フィルタリングを防ぐため、スレッドの作成を遅くすることさえあります。

TryParse を使用すると、単純な条件句でこれらすべてを回避できます。また、実際に記述するコードが少なくなり、読みやすく、推論しやすくなります。

例外は例外的なケースのためのものであり、そのようなシナリオでは、ログ/デバッガーに多くの有用な情報を提供します.

于 2012-12-08T19:44:41.137 に答える
4

パフォーマンスが低下するのは、新しい Exception オブジェクトを作成しているためだけではありません。その多くは、例外が発生したときに実行する必要があるスタックの条件付き巻き戻しに関係しています。

たとえば、さまざまな種類の例外をキャッチする例外ハンドラーがある場合に実行する必要がある作業について考えてみてください。呼び出し先から呼び出し元に巻き戻されるスタックの各ポイントで、言語は型チェックを実行して、例外を処理できるかどうかだけでなく、最も適切なハンドラーを確認する必要があります。それ自体がかなりの量のオーバーヘッドです。

本当に軽量にしたい場合は、関数から結果を返す必要があります。これが Int32.TryParse() の機能です。スタックの巻き戻しも型チェックもありません。簡単に最適化できる単純な条件だけです。

編集:注意すべき興味深い点の 1 つは、C# が Java の後に作成されたことです。Java には、チェック例外throwsキーワードなど、例外処理を C# よりも複雑にする興味深い構造がいくつかあります。ちょっと面白い読み物。私は (個人的に) C# にこの「機能」が含まれていなかったことを嬉しく思います。私の推測では、パフォーマンスを向上させるために例外ハンドラーを分岐させたのでしょう。私が理解しているように、現実の世界では、多くの開発者throws exceptionが関数宣言で指定することになります。

于 2012-12-08T19:54:00.507 に答える
3

int.TryParseあなたのケースで使用する必要があります。また、例外をスローしてキャッチするよりも、いくつかの条件をテストする方が高速で読みやすいです。通常の検証ではなく、例外的な状況に対して例外を使用します。

于 2012-12-08T19:39:08.270 に答える
3

例外の問題は、例外自体を生成することだけではありません。正直なところ、それは最も時間のかかる部分でもありません。(例外が作成された後に) 例外をスローすると、各スコープ レベルを通過するスタックをアンワインドする必要があり、そのスコープがこの例外をキャッチする try/catch ブロックであるかどうかを判断し、例外を更新してそのセクションを通過したことを示します。スタックのそのセクションを破棄します。そしてもちろん、finally実行する必要があるかもしれないすべてのブロックがあります。それException自体に保存する情報を少なくしても、それを単純化することはできません。

于 2012-12-08T19:44:10.463 に答える
2

「重い」例外によって提供されるユーティリティは非常に(笑)便利だからです。デバッガーをヤンクアウトするよう人々に頼まなくても、スタック トレースのようなものを C ランドにダンプできる機能がどれほど頻繁に必要だったかはわかりません。

事後 (つまり、例外がオンデマンドでキャッチされた後) にスタック トレースのようなものを生成することは実行不可能です。障害点に関する情報が必要です。そのため、障害が発生した時点でデータを収集する必要があります。

「新しいオブジェクトのインスタンス化」については、他の高価な例外機能 (スタックの巻き戻し、スタック トレース、複数の関数出口点など) と比較して非常に安価であるため、心配する必要はありません。

于 2012-12-08T19:42:26.333 に答える
1

パフォーマンスTryParseのため、代わりに使用することはお勧めしません。Parse解析が失敗する可能性がある場合 (たとえば、ユーザーが生成した入力であるため)、解析の失敗は例外的ではなく、予期されることです。名前が示すように、例外は例外的な状況のためのものです。もっと早くに発見されるべきだったのに、そうではなかったので、あまりにも予想外だったので、実際には続行できませんでした。

関数がオブジェクトを期待しているのに代わりnullに渡された場合、この場合に何をするのが正しいかは、メソッドの設計者次第です。パラメータがデフォルトのオプションのオーバーライドである場合、プログラムは続行してデフォルトを使用するか、パラメータを無視できます。しかし、それ以外の場合、プログラムは単純にArgumentNullException.

例外を使用するかどうかを決定する際に、パフォーマンスはまったく考慮すべきではありません。それは意図と目的の問題です。2 つの整数を加算するよりも何倍も遅いことは確かですが、古い Core 2 Duo でも 1 秒あたり 50,000 の例外をスローできます。例外の使用がボトルネックになる場合は、それらを正しい方法で使用していません。

于 2012-12-08T20:09:52.760 に答える