C ++で例外処理のオーバーヘッド/パフォーマンスを測定するための最良の方法は何ですか?
スタンドアロンのコードサンプルを提供してください。
Microsoft Visual C++2008とgccをターゲットにしています。
次の場合から結果を得る必要があります。
- try/catchブロックがない場合のオーバーヘッド
- try / catchブロックがあるが、例外がスローされない場合のオーバーヘッド
- 例外がスローされたときのオーバーヘッド
C ++で例外処理のオーバーヘッド/パフォーマンスを測定するための最良の方法は何ですか?
スタンドアロンのコードサンプルを提供してください。
Microsoft Visual C++2008とgccをターゲットにしています。
次の場合から結果を得る必要があります。
C ++パフォーマンスに関するテクニカルレポート草案のセクション5.4は、例外のオーバーヘッドに完全に専念しています。
提案として:例外がスローされたときにオーバーヘッドをあまり気にしないでください。例外処理の実装では、通常、スローを速くせず、キャッチを遅くします。それらのケースは、まあ、例外的であるため、それは問題ありません。
カール
これが私が思いついた測定コードです。何か問題がありますか?
これまでのところLinuxとWindowsで動作し、次のコマンドでコンパイルします。
g++ exception_handling.cpp -o exception_handling [ -O2 ]
または、たとえばVisual C++Express。
基本ケース(「言語から例外サポートが完全に削除された」)を取得するには、次を使用します。
g++ exception_handling.cpp -o exception_handling [ -O2 ] -fno-exceptions -DNO_EXCEPTIONS
またはMSVCの同様の設定。
ここにいくつかの予備的な結果があります。マシンの負荷が変化するため、これらはおそらくすべておかしなものですが、相対的な例外処理のオーバーヘッドについてはある程度のアイデアがあります。(エグゼクティブサマリー:例外がスローされない場合はなしまたはほとんど、実際にスローされる場合は巨大です。)
#include <stdio.h>
// Timer code
#if defined(__linux__)
#include <sys/time.h>
#include <time.h>
double time()
{
timeval tv;
gettimeofday(&tv, 0);
return 1.0 * tv.tv_sec + 0.000001 * tv.tv_usec;
}
#elif defined(_WIN32)
#include <windows.h>
double get_performance_frequency()
{
unsigned _int64 frequency;
QueryPerformanceFrequency((LARGE_INTEGER*) &frequency); // just assume it works
return double(frequency);
}
double performance_frequency = get_performance_frequency();
double time()
{
unsigned _int64 counter;
QueryPerformanceCounter((LARGE_INTEGER*) &counter);
return double(counter) / performance_frequency;
}
#else
# error time() not implemented for your platform
#endif
// How many times to repeat the whole test
const int repeats = 10;
// How many times to iterate one case
const int times = 1000000;
// Trick optimizer to not remove code
int result = 0;
// Case 1. No exception thrown nor handled.
void do_something()
{
++result;
}
void case1()
{
do_something();
}
// Case 2. No exception thrown, but handler installed
#ifndef NO_EXCEPTIONS
void do_something_else()
{
--result;
}
void case2()
{
try
{
do_something();
}
catch (int exception)
{
do_something_else();
}
}
// Case 3. Exception thrown and caught
void do_something_and_throw()
{
throw ++result;
}
void case3()
{
try
{
do_something_and_throw();
}
catch (int exception)
{
result = exception;
}
}
#endif // !NO_EXCEPTIONS
void (*tests[])() =
{
case1,
#ifndef NO_EXCEPTIONS
case2,
case3
#endif // !NO_EXCEPTIONS
};
int main()
{
#ifdef NO_EXCEPTIONS
printf("case0\n");
#else
printf("case1\tcase2\tcase3\n");
#endif
for (int repeat = 0; repeat < repeats; ++repeat)
{
for (int test = 0; test < sizeof(tests)/sizeof(tests[0]); ++test)
{
double start = time();
for (int i = 0; i < times; ++i)
tests[test]();
double end = time();
printf("%f\t", (end - start) * 1000000.0 / times);
}
printf("\n");
}
return result; // optimizer is happy - we produce a result
}
Kevin Freiは、講演「 Windows での C++ 例外処理のコスト」で例外処理のパフォーマンス コストについて語っています。(「概要と結論」の下に、「[例外処理のパフォーマンス コストは] 必ずしも測定可能ではない」というリスト項目が 1 つあります。)
コードでそれを測定するための本当に良い方法はありません。プロファイラーを使用する必要があります。
これは、例外処理に費やされた時間を直接示すものではありませんが、少し調べてみると、どのランタイムメソッドが例外を処理するかがわかります(たとえば、VC++。NETの場合は__cxx_exc[...])。
それらの時間を合計すると、オーバーヘッドが発生します。私たちのプロジェクトでは、VisualC++とgccの両方で動作するIntelのvTunesを使用しました。
編集:まあ、あなたがうまくいくかもしれない一般的な番号が必要なだけなら。例外をオフにするだけでは不十分なプロファイルを作成するための実際のアプリケーションがあると思いました。
例外処理のパフォーマンスに関するもう 1 つの注意事項: 単純なテストではキャッシュが考慮されていません。try-code と catch-code はどちらも非常に小さいため、すべてが命令キャッシュとデータ キャッシュに収まります。ただし、コンパイラは catch-code を try-code から遠くに移動しようとする場合があります。これにより、通常はキャッシュに保持するコードの量が減り、パフォーマンスが向上します。
例外処理を従来の C スタイルの戻り値チェックと比較する場合、このキャッシュ効果も考慮に入れる必要があります (この質問は通常、議論では無視されます)。
カール
g++が例外を処理する方法の詳細をここに示します。Itaniumアーキテクチャ用であると説明されていますが、使用される一般的な手法は同じです。時間の観点から正確なオーバーヘッドはわかりませんが、大まかなコードオーバーヘッドがどのようになるかを収集できます。
答えは、スローの結果として発生する必要があるクリーンアップに依存しませんか? オブジェクトのロード全体がスタックの範囲外になる例外がスローされると、オーバーヘッドが追加されます。
言い換えれば、コードの詳細とは無関係な 3 番目の質問に対する回答があるかどうかはわかりません。