C ++の例外は本当に遅いですか


98

私はC ++での系統的エラー処理を見ていました。AndreiAlexandrescu、C ++の例外は非常に遅いと主張しています。

これはC ++ 98でも同じですか?


42
「C ++ 98例外」が「C ++ 03例外」または「C ++ 11例外」よりも速い/遅いかどうかを尋ねても意味がありません。それらのパフォーマンスは、コンパイラーがプログラムにそれらを実装する方法に依存し、C ++標準は、それらがどのように実装されるべきかについて何も述べていません。唯一の要件は、その動作が標準( "as-if"ルール)に従う必要があることです。
インシリコ

:質問関連(しかし実際には重複しない)stackoverflow.com/questions/691168/...
フィリップ

2
はい、それは非常に遅いですが、彼らは通常の操作のために投げやブランチとして使用すべきではない
BЈовић

同様の質問が見つかりました。
PaperBirdMaster

BЈовићの発言を明確にするために、例外を使用することは怖いものではありません。(潜在的に)時間のかかる操作が発生するのは、例外がスローされたときです。また、なぜC ++ 89について特に知りたいのかについても興味があります...最新バージョンはC ++ 11であり、例外の実行にかかる時間は実装定義であるため、「潜在的に」時間がかかる。
thecoshman

回答:


162

例外(Itanium ABI、VC ++ 64ビット)に現在使用されている主なモデルは、ゼロコストモデルの例外です。

ガードは、ガードを設定してすべての場所で例外の存在を明示的に確認することで時間を失う代わりに、例外をスローする可能性のあるポイント(プログラムカウンター)をハンドラーのリストにマップするサイドテーブルを生成するという考え方です。例外がスローされると、このリストが参照されて適切なハンドラー(存在する場合)が選択され、スタックが展開されます。

典型的なif (error)戦略と比較して:

  • 名前が示すように、ゼロコストモデルは、例外が発生しない場合は無料です。
  • if例外が発生した場合の約10倍/ 20倍のコスト

ただし、コストを測定するのは簡単ではありません。

  • サイドテーブルは一般的に冷たいので、メモリからのフェッチには時間がかかります
  • 適切なハンドラーの決定にはRTTIが含まれます。フェッチする多くのRTTI記述子、メモリ内に散在し、実行する複雑な操作(基本的dynamic_castに各ハンドラーのテスト)

そのため、ほとんどがキャッシュミスであり、純粋なCPUコードと比較して重要ではありません。

注:詳細については、TR18015レポートの5.4例外処理(pdf)の章を参照してください。

そのため、はい、例外は例外パスで低速ですが、その他の点ifでは一般的に明示的なチェック(戦略)よりも高速です。

注:Andrei Alexandrescuは、この「より速い」に疑問を呈しているようです。私は個人的に物事が両方の方向に振れるのを見ました、いくつかのプログラムは例外でより速く、他はブランチでより速いので、確かに特定の条件で最適化の損失があるようです。


それは重要ですか?

そうではないと主張します。プログラムは、パフォーマンスではなく(少なくとも、最初の基準としてではなく)読みやすさを念頭に置いて作成する必要があります。例外は、呼び出し側がその場で障害を処理できない、または望まない場合に使用され、スタックに渡されます。ボーナス:C ++ 11では、標準ライブラリを使用して、スレッド間で例外をマーシャリングできます。

これは微妙ですが、map::findスローすべきではないと主張していますが、nullであるため逆参照の試行が失敗した場合にスローするをmap::find返すことchecked_ptrで問題はありません。後者の場合、Alexandrescuが導入したクラスの場合のように、呼び出し元が選択します明示的なチェックと例外への依存の間。通常、発信者に責任を与えずに権限を与えることは、優れたデザインのしるしです。


3
+1 4つだけ追加します。(0)C ++ 11で追加された再スローのサポートについて。(1)C ++効率に関する委員会のレポートへの参照。(2)正確さに関するいくつかの注意事項(読みやすさを損なうなど)。(3)パフォーマンスについて、例外を使用しない場合(それは相対的なものです)に対して測定することについての
コメント

2
@ Cheersandhth.-Alf:(0)、(1)、(3)完了:ありがとう。正確性(2)については、読みやすさよりも優先されますが、例外が他のエラー処理戦略よりも正しいコードにつながるかどうかはわかりません(実行例外が作成する多くの目に見えないパスを忘れがちです)。
Matthieu M.

2
説明はローカルでは正しいかもしれませんが、例外の存在は、コンパイラーが行うことができる仮定と最適化にグローバルな影響を与えることに注意する価値があります。コンパイラーは常に小さなプログラムを通して見ることができるため、これらの含意には、「些細な反例がない」という問題があります。例外の有無にかかわらず、現実的な大規模なコードベースでプロファイリングすることは良い考えです。
Kerrek SB、2015

4
>ゼロコストモデルは、その名前が示すように、例外が発生しない場合は無料ですが、これは実際には最も詳細な詳細レベルには当てはまりません。小さくて微妙な場合でも、より多くのコードを生成すると、常にパフォーマンスに影響があります... OSが実行可能ファイルをロードするのに少し時間がかかる場合があります。そうしないと、iキャッシュミスが多くなります。また、スタック巻き戻しコードはどうですか?また、合理的な思考でそれを理解しようとする代わりに、効果を測定するために行うことができる実験についてはどうですか?
jheriko

2
@jheriko:私はあなたの質問のほとんどにすでに対応したと思います。読み込み時間に影響を与えない(コールドコードを読み込まない)、iキャッシュに影響を与えない(コールドコードがiキャッシュに入らない)などの1つの不足している質問に対処します。 「測定方法」=>スローされたすべての例外をへの呼び出しで置き換えるとabort、バイナリサイズのフットプリントを測定し、load-time / i-cacheが同様に動作することを確認できます。もちろん、次のいずれにもヒットしない方がよいabort...
マシューM.

60

質問が投稿されたとき、私は医者に行く途中で、タクシーを待っていたので、その時は短いコメントしかありませんでした。しかし、今はコメントし、賛成票と反対票を投じたので、自分の回答を追加した方がいいでしょう。場合でもマシューの答えは、すでにかなり良いです。


他の言語と比較して、C ++では例外が特に遅いですか?

申し立てについて

C ++での体系的なエラー処理を見ていました。AndreiAlexandrescu氏は、C ++の例外は非常に遅いと主張しています。」

それが文字通りアンドレイが主張するものであるならば、それから彼は一度も全く間違っていないとしても、非常に誤解を招くでしょう。発生/スローされた例外の場合、プログラミング言語に関係なく、言語の他の基本的な操作と比較して常に遅いです。主張されている主張が示すように、C ++だけでなく、C ++では他の言語よりも多くなります。

一般に、ほとんど言語に関係なく、複雑なデータ構造を処理するルーチンの呼び出しに変換されるため、他の言語機能よりも桁違いに遅い2つの基本的な言語機能は次のとおりです。

  • 例外のスロー、および

  • 動的メモリ割り当て。

C ++では幸いなことに、タイムクリティカルなコードで両方を回避できることがよくあります。

残念ながら、たとえC ++のデフォルトの効率がかなり近づいても、無料のランチほどのものはありません。:-)例外のスローと動的メモリ割り当てを回避することで得られる効率のために、C ++を単なる「より優れたC」として使用して、より低い抽象化レベルでコーディングすることにより、一般に達成されます。また、抽象度が低いほど、「複雑さ」が増します。

複雑さが増すほど、メンテナンスに費やす時間が増え、コードの再利用によるメリットがほとんどまたはまったくなくなります。これは、見積もりや測定が難しい場合でも、実際の金銭的コストです。つまり、C ++では、必要に応じて、プログラマの効率と実行の効率をトレードオフできます。実際にはコストではなくゲインのみを簡単に推定して測定できるため、そうするかどうかは、主にエンジニアリングと直感の決定です。


C ++例外スローパフォーマンスの客観的な指標はありますか?

はい、国際C ++標準化委員会はC ++パフォーマンスに関するテクニカルレポートTR18015を発行しています。


例外が「遅い」とはどういう意味ですか?

主に、ハンドラーの検索によりthrowint割り当てなどに比べて非常に長い時間がかかることを意味します。

TR18015がセクション5.4「例外」で説明しているように、2つの主要な例外処理実装戦略があります。

  • try例外がスローされたときにハンドラーの動的チェーンの検索が実行されるように、各ブロックが動的に例外キャッチを設定するアプローチ

  • スローされた例外のハンドラーを判別するために使用される静的ルックアップテーブルをコンパイラーが生成する方法。

最初の非常に柔軟で一般的なアプローチは、32ビットWindowsではほぼ強制されますが、64ビットランドと* nixランドでは、2番目にはるかに効率的なアプローチが一般的に使用されます。

また、そのレポートで説明しているように、各アプローチには、例外処理が効率に影響を与える3つの主要な領域があります。

  • try-ブロック、

  • 通常の機能(最適化の機会)、および

  • throw-式。

主に、動的ハンドラーアプローチ(32ビットWindows)では、例外処理はtryブロックに影響を与えます。これは、ほとんどの場合、言語に関係なく(これはWindowsの構造化例外処理スキームによって強制されるため)、静的テーブルアプローチでは、コストはほぼゼロですtry-ブロック。これについて議論することは、SOの答えとして実用的であるよりもはるかに多くのスペースと研究を必要とします。したがって、詳細についてはレポートを参照してください。

残念ながら、2006年のレポートは、2012年後半の時点ですでに少し古くなっており、私が知る限り、新しい比較可能なものはありません。

別の重要な視点は、例外使用によるパフォーマンスへの影響は、サポート言語機能の分離された効率とは非常に異なるということです。

「例外処理を検討する場合、それはエラーを処理する別の方法と対照的でなければなりません。」

例えば:

  • プログラミングスタイルの違いによるメンテナンスコスト(正確さ)

  • 冗長な呼び出しサイトif障害チェックと集中化try

  • キャッシュの問題(例:短いコードはキャッシュに収まる)

レポートには考慮すべき側面の異なるリストがありますが、実行効率についての確かな事実を取得する唯一の実用的な方法は、おそらく、例外を使用して、例外を使用せずに、同じプログラムを実装することです。それぞれの方法に慣れ、次にMEASURE


例外のオーバーヘッドを回避する良い方法は何ですか?

正確さはほとんどの場合、効率を上回ります。

例外なく、次のことが簡単に起こります。

  1. 一部のコードPは、リソースを取得したり、情報を計算したりするためのものです。

  2. 呼び出しコードCは、成功/失敗をチェックするはずですが、チェックしていません。

  3. Cに続くコードで、存在しないリソースまたは無効な情報が使用されているため、一般的な騒乱が発生しています。

主な問題はポイント(2)であり、通常の戻りコード方式では、呼び出しコードCは強制的にチェックされません。

このようなチェックを強制する2つの主要なアプローチがあります。

  • Pが失敗すると、例外が直接スローされます。

  • ここで、Pは、Cがその主な値を使用する前に検査する必要があるオブジェクトを返します(それ以外の場合は例外または終了)。

2番目のアプローチはAFAIKで、最初にBartonとNackmanの著書「Scientific and Engineering C ++:An Introduction with Advanced Techniques and Examples」で説明されFallowました。同様のクラスoptionalがBoostライブラリによって提供されています。またOptionalstd::vectorPOD以外の結果の場合に値キャリアとしてas を使用して、クラスを自分で簡単に実装できます。

最初のアプローチでは、呼び出しコードCは例外処理技術を使用する以外に選択肢はありません。ただし、2番目のアプローチでは、呼び出し元のコードC自体が、ifベースのチェックを行うか、一般的な例外処理を行うかを決定できます。したがって、2番目のアプローチは、プログラマーと実行時間の効率のトレードオフをサポートします。


例外パフォーマンスに対するさまざまなC ++標準の影響は何ですか?

「これはC ++ 98でも同じです」

C ++ 98は最初のC ++標準でした。例外については、例外クラスの標準階層を導入しました(残念ながらかなり不完全です)。パフォーマンスへの主な影響は、(C ++ 11で削除された)例外仕様の可能性でしたが、メインのWindows C ++コンパイラVisual C ++によって完全に実装されることはありませんでした。VisualC ++はC ++ 98例外仕様構文を受け入れますが、無視するだけです。例外仕様。

C ++ 03は、C ++ 98の単なる技術的修正でした。C ++ 03で本当に新しいのは、値の初期化だけです。例外はありません。

C ++ 11標準では、一般的な例外の仕様が削除され、noexceptキーワードに置き換えられました。

C ++ 11標準は、例外の格納と再スローのサポートも追加しました。これは、C言語のコールバック間でC ++例外を伝播するのに最適です。このサポートにより、現在の例外を格納する方法が効果的に制限されます。ただし、私が知る限り、これはパフォーマンスに影響を与えません。ただし、新しいコードでは、C言語のコールバックの両側で例外処理をより簡単に使用できる場合を除きます。


6
「例外は、プログラミング言語に関係なく、言語の他の基本操作と比較して常に遅い」...例外の使用を通常のフロー制御にコンパイルするように設計された言語を除いて。
Ben Voigt

4
「例外のスローには、割り当てとスタックの巻き戻しの両方が含まれます」。これも明らかに一般には当てはまりません。また、OCamlは反例です。ガベージコレクションされた言語では、デストラクタがないためlongjmp、ハンドラーに移動するだけなので、スタックをほどく必要はありません。
JD

2
@JonHarrop:おそらくあなたはPyhonが例外処理のためのfinally節を持っていることを知らないでしょう。これは、Pythonの実装にスタックの巻き戻しがあるか、Pythonではないことを意味します。(ファンタジー)について主張する主題をまったく知らないように見える。ごめんなさい。
乾杯とhth。-アルフ

2
@ Cheersandhth.-Alf:「Pyhonには例外処理のfinally節があります。これは、Pythonの実装にスタックの巻き戻しがあるか、Pythonではないことを意味します。」try..finallyコンストラクトは、スタック巻き戻しすることなく実施することができます。F#、C#、およびJavaはすべてtry..finally、スタックの巻き戻しを使用せずに実装されます。あなたlongjmpはハンドラーに行きます(すでに説明したように)。
JD

4
@JonHarrop:あなたはジレンマを装ったよう。しかし、これまでに説明したこととは無関係であり、これまでに否定的なサウンドのナンセンスの長いシーケンスを投稿しました。アンタゴニストとしてあなたはそれが「手段」というを明らかにするものを選択している、と私は確かので、私は、いくつかのあいまいな言い回しに同意するかしないために、あなたを信頼しなければならないん downvotingなど、すべてが無意味なナンセンス後にあなたを信頼します
乾杯とhth。-Alf

13

コードをアセンブリに変換またはベンチマークしない限り、パフォーマンスについて主張することはできません。

ここにあなたが見るものがあります:(quick-bench)

エラーコードは、発生率に影響されません。例外は、スローされない限り、少しオーバーヘッドがあります。それらを投げると、悲惨さが始まります。この例では、0%、1%、10%、50%、90%のケースでスローされます。例外が90%の時間でスローされる場合、コードは、例外が10%の時間でスローされる場合よりも8倍遅くなります。ご覧のとおり、例外は本当に遅いです。頻繁に投げられる場合は使用しないでください。アプリケーションにリアルタイム要件がない場合は、ごくまれにしか発生しないのであれば、遠慮なく投げてください。

あなたはそれらについて多くの矛盾した意見を見ます。しかし、最後に、例外は遅いですか?私は判断しません。ベンチマークを見てください。

C ++例外のパフォーマンスベンチマーク


12

コンパイラに依存します。

たとえば、GCCは例外を処理するときのパフォーマンスが非常に低いことで知られていましたが、過去数年間でかなり改善されました。

ただし、例外の処理は、名前が示すように、ソフトウェア設計の規則ではなく例外でなければならないことに注意してください。パフォーマンスに影響するほど多くの例外を1秒あたりにスローするアプリケーションがあり、これが通常の操作と見なされる場合は、別の方法で行うことを検討する必要があります。

例外は、不格好なエラー処理コードをすべて取り除くことでコードを読みやすくする優れた方法ですが、例外が通常のプログラムフローの一部になるとすぐに、追跡が難しくなります。a throwはほとんどgoto catch変装していることを忘れないでください。


-1現状の質問については、「これはまだC ++ 98にも当てはまりますか」という質問は、コンパイラには依存しません。また、この答えthrow new ExceptionはJava-ismです。原則として、ポインタをスローしないでください。
乾杯とhth。-Alf

1
98規格は、例外の実装方法を正確に規定していますか?
thecoshman

6
C ++ 98はISO標準であり、コンパイラではありません。それを実装するコンパイラはたくさんあります。
Philipp

3
@thecoshman:いいえ。C++標準では、何をどのように実装する必要があるかについては何も述べられていません(標準の「実装制限」の部分を除いて)。
in silico

2
@Insilicoを実行すると、(衝撃的に)例外がどのように実行されるかは、定義された(読み取り、コンパイラー固有の)実装定義であるという論理的な結論を導き出すことができます。
thecoshman

4

はい、しかしそれは問題ではありません。どうして?
これを読む:https :
//blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx

基本的には、アレクサンドルスクのような例外を使用することは(catchとして使用するため、50倍のスローダウンelse)が間違っていることを示しています。C ++ 22 :)のようにそれをしたいpplのために言われていることは、次のようなものを追加します
(これは基本的に既存のものからコンパイラがコードを生成するため、これはコア言語である必要があることに注意してください)

result = attempt<lexical_cast<int>>("12345");  //lexical_cast is boost function, 'attempt'
//... is the language construct that pretty much generates function from lexical_cast, generated function is the same as the original one except that fact that throws are replaced by return(and exception type that was in place of the return is placed in a result, but NO exception is thrown)...     
//... By default std::exception is replaced, ofc precise configuration is possible
if (result)
{
     int x = result.get(); // or result.result;
}
else 
{
     // even possible to see what is the exception that would have happened in original function
     switch (result.exception_type())
     //...

}

PSは、例外がそれほど遅くても、実行中にコードのその部分に多くの時間を費やさなければ問題ではないことにも注意してください...たとえば、浮動小数点除算が遅く、4倍にした場合時間の0.3%をFP除算に費やしても問題ありません...


0

インシリコのようにその実装に依存すると述べたが、一般的に例外はどの実装でも遅いと考えられており、パフォーマンスを重視するコードでは使用すべきではない。

編集:私はそれらをまったく使用しないと言っているわけではありませんが、パフォーマンスを重視するコードではそれらを避けるのが最善です。


9
これは、せいぜい例外的なパフォーマンスを調べる非常に単純な方法です。たとえば、GCCは「ゼロコスト」の実装を使用しており、例外がスローされない場合でもパフォーマンスヒットは発生しません。また、例外は例外的な(つまり、まれな)状況を対象としています。そのため、何らかのメトリックによって速度が遅くても、それらを使用しない理由としては不十分です。
インシリコ

@insilicoなぜ私が言ったのかを見てみると、例外をフルストップで使用しないとは言いませんでした。私はパフォーマンス集約的なコードを指定しました。これは正確な評価です。主にgpgpusで作業し、例外を使用した場合は撃たれます。
Chris McCabe
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.