私はC ++での系統的エラー処理を見ていました。AndreiAlexandrescuは、C ++の例外は非常に遅いと主張しています。
これはC ++ 98でも同じですか?
私はC ++での系統的エラー処理を見ていました。AndreiAlexandrescuは、C ++の例外は非常に遅いと主張しています。
これはC ++ 98でも同じですか?
回答:
例外(Itanium ABI、VC ++ 64ビット)に現在使用されている主なモデルは、ゼロコストモデルの例外です。
ガードは、ガードを設定してすべての場所で例外の存在を明示的に確認することで時間を失う代わりに、例外をスローする可能性のあるポイント(プログラムカウンター)をハンドラーのリストにマップするサイドテーブルを生成するという考え方です。例外がスローされると、このリストが参照されて適切なハンドラー(存在する場合)が選択され、スタックが展開されます。
典型的なif (error)
戦略と比較して:
if
例外が発生した場合の約10倍/ 20倍のコストただし、コストを測定するのは簡単ではありません。
dynamic_cast
に各ハンドラーのテスト)そのため、ほとんどがキャッシュミスであり、純粋なCPUコードと比較して重要ではありません。
注:詳細については、TR18015レポートの5.4例外処理(pdf)の章を参照してください。
そのため、はい、例外は例外パスで低速ですが、その他の点if
では一般的に明示的なチェック(戦略)よりも高速です。
注:Andrei Alexandrescuは、この「より速い」に疑問を呈しているようです。私は個人的に物事が両方の方向に振れるのを見ました、いくつかのプログラムは例外でより速く、他はブランチでより速いので、確かに特定の条件で最適化の損失があるようです。
それは重要ですか?
そうではないと主張します。プログラムは、パフォーマンスではなく(少なくとも、最初の基準としてではなく)読みやすさを念頭に置いて作成する必要があります。例外は、呼び出し側がその場で障害を処理できない、または望まない場合に使用され、スタックに渡されます。ボーナス:C ++ 11では、標準ライブラリを使用して、スレッド間で例外をマーシャリングできます。
これは微妙ですが、map::find
スローすべきではないと主張していますが、nullであるため逆参照の試行が失敗した場合にスローするをmap::find
返すことchecked_ptr
で問題はありません。後者の場合、Alexandrescuが導入したクラスの場合のように、呼び出し元が選択します明示的なチェックと例外への依存の間。通常、発信者に責任を与えずに権限を与えることは、優れたデザインのしるしです。
abort
、バイナリサイズのフットプリントを測定し、load-time / i-cacheが同様に動作することを確認できます。もちろん、次のいずれにもヒットしない方がよいabort
...
質問が投稿されたとき、私は医者に行く途中で、タクシーを待っていたので、その時は短いコメントしかありませんでした。しかし、今はコメントし、賛成票と反対票を投じたので、自分の回答を追加した方がいいでしょう。場合でもマシューの答えは、すでにかなり良いです。
申し立てについて
「C ++での体系的なエラー処理を見ていました。AndreiAlexandrescu氏は、C ++の例外は非常に遅いと主張しています。」
それが文字通りアンドレイが主張するものであるならば、それから彼は一度も全く間違っていないとしても、非常に誤解を招くでしょう。発生/スローされた例外の場合、プログラミング言語に関係なく、言語の他の基本的な操作と比較して常に遅いです。主張されている主張が示すように、C ++だけでなく、C ++では他の言語よりも多くなります。
一般に、ほとんど言語に関係なく、複雑なデータ構造を処理するルーチンの呼び出しに変換されるため、他の言語機能よりも桁違いに遅い2つの基本的な言語機能は次のとおりです。
例外のスロー、および
動的メモリ割り当て。
C ++では幸いなことに、タイムクリティカルなコードで両方を回避できることがよくあります。
残念ながら、たとえC ++のデフォルトの効率がかなり近づいても、無料のランチほどのものはありません。:-)例外のスローと動的メモリ割り当てを回避することで得られる効率のために、C ++を単なる「より優れたC」として使用して、より低い抽象化レベルでコーディングすることにより、一般に達成されます。また、抽象度が低いほど、「複雑さ」が増します。
複雑さが増すほど、メンテナンスに費やす時間が増え、コードの再利用によるメリットがほとんどまたはまったくなくなります。これは、見積もりや測定が難しい場合でも、実際の金銭的コストです。つまり、C ++では、必要に応じて、プログラマの効率と実行の効率をトレードオフできます。実際にはコストではなくゲインのみを簡単に推定して測定できるため、そうするかどうかは、主にエンジニアリングと直感の決定です。
はい、国際C ++標準化委員会はC ++パフォーマンスに関するテクニカルレポートTR18015を発行しています。
主に、ハンドラーの検索によりthrow
、int
割り当てなどに比べて非常に長い時間がかかることを意味します。
TR18015がセクション5.4「例外」で説明しているように、2つの主要な例外処理実装戦略があります。
try
例外がスローされたときにハンドラーの動的チェーンの検索が実行されるように、各ブロックが動的に例外キャッチを設定するアプローチ
スローされた例外のハンドラーを判別するために使用される静的ルックアップテーブルをコンパイラーが生成する方法。
最初の非常に柔軟で一般的なアプローチは、32ビットWindowsではほぼ強制されますが、64ビットランドと* nixランドでは、2番目にはるかに効率的なアプローチが一般的に使用されます。
また、そのレポートで説明しているように、各アプローチには、例外処理が効率に影響を与える3つの主要な領域があります。
try
-ブロック、
通常の機能(最適化の機会)、および
throw
-式。
主に、動的ハンドラーアプローチ(32ビットWindows)では、例外処理はtry
ブロックに影響を与えます。これは、ほとんどの場合、言語に関係なく(これはWindowsの構造化例外処理スキームによって強制されるため)、静的テーブルアプローチでは、コストはほぼゼロですtry
-ブロック。これについて議論することは、SOの答えとして実用的であるよりもはるかに多くのスペースと研究を必要とします。したがって、詳細についてはレポートを参照してください。
残念ながら、2006年のレポートは、2012年後半の時点ですでに少し古くなっており、私が知る限り、新しい比較可能なものはありません。
別の重要な視点は、例外の使用によるパフォーマンスへの影響は、サポート言語機能の分離された効率とは非常に異なるということです。
「例外処理を検討する場合、それはエラーを処理する別の方法と対照的でなければなりません。」
例えば:
プログラミングスタイルの違いによるメンテナンスコスト(正確さ)
冗長な呼び出しサイトif
障害チェックと集中化try
キャッシュの問題(例:短いコードはキャッシュに収まる)
レポートには考慮すべき側面の異なるリストがありますが、実行効率についての確かな事実を取得する唯一の実用的な方法は、おそらく、例外を使用して、例外を使用せずに、同じプログラムを実装することです。それぞれの方法に慣れ、次にMEASURE。
正確さはほとんどの場合、効率を上回ります。
例外なく、次のことが簡単に起こります。
一部のコードPは、リソースを取得したり、情報を計算したりするためのものです。
呼び出しコードCは、成功/失敗をチェックするはずですが、チェックしていません。
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ライブラリによって提供されています。またOptional
、std::vector
POD以外の結果の場合に値キャリアとしてas を使用して、クラスを自分で簡単に実装できます。
最初のアプローチでは、呼び出しコードCは例外処理技術を使用する以外に選択肢はありません。ただし、2番目のアプローチでは、呼び出し元のコードC自体が、if
ベースのチェックを行うか、一般的な例外処理を行うかを決定できます。したがって、2番目のアプローチは、プログラマーと実行時間の効率のトレードオフをサポートします。
「これは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言語のコールバックの両側で例外処理をより簡単に使用できる場合を除きます。
longjmp
、ハンドラーに移動するだけなので、スタックをほどく必要はありません。
try..finally
コンストラクトは、スタック巻き戻しすることなく実施することができます。F#、C#、およびJavaはすべてtry..finally
、スタックの巻き戻しを使用せずに実装されます。あなたlongjmp
はハンドラーに行きます(すでに説明したように)。
コードをアセンブリに変換またはベンチマークしない限り、パフォーマンスについて主張することはできません。
ここにあなたが見るものがあります:(quick-bench)
エラーコードは、発生率に影響されません。例外は、スローされない限り、少しオーバーヘッドがあります。それらを投げると、悲惨さが始まります。この例では、0%、1%、10%、50%、90%のケースでスローされます。例外が90%の時間でスローされる場合、コードは、例外が10%の時間でスローされる場合よりも8倍遅くなります。ご覧のとおり、例外は本当に遅いです。頻繁に投げられる場合は使用しないでください。アプリケーションにリアルタイム要件がない場合は、ごくまれにしか発生しないのであれば、遠慮なく投げてください。
あなたはそれらについて多くの矛盾した意見を見ます。しかし、最後に、例外は遅いですか?私は判断しません。ベンチマークを見てください。
コンパイラに依存します。
たとえば、GCCは例外を処理するときのパフォーマンスが非常に低いことで知られていましたが、過去数年間でかなり改善されました。
ただし、例外の処理は、名前が示すように、ソフトウェア設計の規則ではなく例外でなければならないことに注意してください。パフォーマンスに影響するほど多くの例外を1秒あたりにスローするアプリケーションがあり、これが通常の操作と見なされる場合は、別の方法で行うことを検討する必要があります。
例外は、不格好なエラー処理コードをすべて取り除くことでコードを読みやすくする優れた方法ですが、例外が通常のプログラムフローの一部になるとすぐに、追跡が難しくなります。a throw
はほとんどgoto catch
変装していることを忘れないでください。
throw new Exception
はJava-ismです。原則として、ポインタをスローしないでください。
はい、しかしそれは問題ではありません。どうして?
これを読む: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除算に費やしても問題ありません...
インシリコのようにその実装に依存すると述べたが、一般的に例外はどの実装でも遅いと考えられており、パフォーマンスを重視するコードでは使用すべきではない。
編集:私はそれらをまったく使用しないと言っているわけではありませんが、パフォーマンスを重視するコードではそれらを避けるのが最善です。