测量C ++中的异常处理开销
衡量C ++中异常处理开销/性能的最佳方法是什么?
请提供独立的代码示例。
我的目标是Microsoft Visual C ++ 2008和gcc。
我需要从以下情况中获得结果:
- 没有try / catch块时开销
- 有try / catch块但未引发异常时的开销
- 引发异常时的开销
解决方案
回答
作为建议:抛出异常时,不要为开销过多地打扰。异常处理实现通常不会使快速抛出和捕获缓慢。可以,因为这些案例非常特殊。
卡尔
回答
这是我想出的测量代码。我们看到任何问题吗?
到目前为止,可在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 }
回答
没有真正好的方法可以在代码中进行测量。我们将需要使用探查器。
这不会直接显示出我们在异常处理上花费了多少时间,但是通过一点点研究,我们会发现哪些运行时方法可以处理异常(例如,对于VC ++。NET,它是__cxx_exc [...])。
加倍他们的时间,你就有开销。在我们的项目中,我们使用了Intel的vTunes,该vTunes可与Visual C ++和gcc一起使用。
编辑:好吧,如果我们只需要一个可能有效的通用编号。以为我们有一个实际的应用程序可以分析我们无法关闭异常的情况。
回答
有关C ++性能的技术报告草稿中的5.4节完全涉及异常的开销。
回答
关于异常处理性能的另一个注意事项:简单测试不考虑缓存。尝试代码和捕获代码都很小,以至于所有内容都适合指令和数据缓存。但是编译器可能会尝试将捕获代码从try代码移开,从而减少正常情况下要保留在缓存中的代码量,从而提高性能。
如果将异常处理与传统的C风格的返回值检查进行比较,则还应考虑这种缓存效果(在讨论中通常会忽略该问题)。
卡尔
回答
答案是否取决于扔出后必须进行的清理工作?如果抛出一个异常导致整个对象的负载超出堆栈范围,那么这将增加开销。
换句话说,我不确定第三个问题的答案是否独立于代码的细节。
回答
凯文·弗赖(Kevin Frei)在他的演讲" Windows上C ++异常处理的代价"中谈到了异常处理的性能代价。 (在"摘要和结论"下,有一个列表项表示" [异常处理性能成本并非总是可衡量的"。)
回答
这里显示了有关g ++如何处理异常的完整详细信息。它描述它是用于Itanium体系结构的,但是所使用的通用技术是相同的。它不会告诉我们确切的时间开销,但是我们可以了解大致的代码开销。