在C ++中衡量异常处理开销/性能的最佳方法是什么?
请提供独立的代码示例。
我的目标是Microsoft Visual C ++ 2008和gcc。
我需要从以下案例中获得结果:
答案 0 :(得分:34)
draft Technical Report on C++ Performance的第5.4节完全专注于异常的开销。
答案 1 :(得分:9)
作为建议:抛出异常时不要过多担心开销。异常处理实现通常不会快速抛出并缓慢捕获。没关系,因为那些案例非常特殊。
卡尔
答案 2 :(得分:8)
这是我提出的测量代码。你觉得它有什么问题吗?
到目前为止适用于Linux和Windows,编译时使用:
g++ exception_handling.cpp -o exception_handling [ -O2 ]
要获得基本案例(“完全从语言中删除异常支持”),请使用:
g++ exception_handling.cpp -o exception_handling [ -O2 ] -fno-exceptions -DNO_EXCEPTIONS
或MSVC中的类似设置。
一些初步结果here。由于机器负载的不同,它们可能都很糟糕,但它们确实对相关的异常处理开销有所了解。 (执行摘要:当没有异常被抛出时,没有或很少,当它们实际被抛出时是巨大的。)
#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
}
答案 3 :(得分:3)
Kevin Frei在演讲“The Cost of C++ Exception Handling on Windows”中讨论了异常处理性能成本问题。 (在“摘要和结论”下,有一个列表项,说“[异常处理性能成本]并不总是可衡量的。”
答案 4 :(得分:2)
在代码中没有真正好的方法来确保这一点。您需要使用分析器。
这不会直接向您显示异常处理花费了多少时间,但通过一些研究,您将了解哪些运行时方法处理异常(例如对于VC ++ .NET,它是__cxx_exc [... ])。
添加他们的时间,你就有了开销。在我们的项目中,我们使用了英特尔的vTunes,它与Visual C ++和gcc一起使用。
编辑:好吧,如果你只需要一个可能有效的通用号码。以为你有一个实际的应用程序来分析你不能只关闭异常。
答案 5 :(得分:2)
关于异常处理性能的另一个注意事项:简单测试不考虑缓存。 try-code和catch-code都很小,所以一切都适合指令和数据缓存。但是编译器可能会尝试将catch代码远离try-code,从而减少了正常保存在缓存中的代码量,从而提高了性能。
如果将异常处理与传统的C风格的返回值检查进行比较,也应该考虑这种缓存效果(在讨论中通常会忽略该问题)。
卡尔
答案 6 :(得分:0)
答案不会取决于投掷后必须进行的清理工作吗?如果抛出一个导致整个对象加载超出堆栈范围的excpetion,则会增加开销。
换句话说,我不确定第三个问题的答案是否独立于代码的具体细节。
答案 7 :(得分:0)
显示了g ++如何处理异常的完整详细信息here。它将其描述为适用于Itanium体系结构,但使用的一般技术是相同的。它不会告诉你时间方面的确切开销,但是你可以收集粗略的代码开销。