在C中测量异常处理开销/性能的最佳方法是什么?请给予独立的代码样本。我的目标是Microsoft Visual C 2008和GCC。我需要从以下案例中获得结果:1.没有try/catch块时的开销1.当有try/catch块但没有抛出异常时的开销1.引发异常时的开销
sg3maiej1#
draft Technical Report on C++ Performance的第5.4节专门讨论异常的开销。
px9o7tmv2#
作为一个建议:当抛出异常时,不要太在意开销。异常处理的实现通常会使不抛出变快,捕获变慢。这是确定的,因为这些情况下,嗯,例外。卡尔
yyhrrdl83#
这是我想出来的测量代码。你觉得有什么问题吗?到目前为止可以在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设置。一些初步结果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 }
rbpvctlc4#
Kevin Frei在他的演讲“The Cost of C++ Exception Handling on Windows“中谈到了异常处理性能成本。(在“总结和结论”下有一个列表项,上面写着“[异常处理性能成本]并不总是可衡量的”。
izkcnapc5#
在代码中没有好的方法来保证这一点。你需要使用一个侧写器。这不会直接显示异常处理花费了多少时间,但通过一点点研究,您将发现哪些运行时方法处理异常(例如,对于VC++.NET,它是__cxx_exc[...])。把他们的时间加起来,你就有了开销。在我们的项目中,我们使用了Intel的vcss,它可以与Visual C++和gcc一起工作。编辑:好吧,如果你只是需要一个通用的数字,可能会工作。我以为你有一个实际的应用程序来分析你不能关闭异常的地方。
aij0ehis6#
关于异常处理性能的另一个注意事项:简单的测试不考虑缓存。try代码和catch代码都很小,可以容纳指令和数据缓存。但是编译器可能会尝试将catch-code移到远离try-code的地方,这减少了通常需要保存在缓存中的代码量,从而提高了性能。如果将异常处理与传统的C风格的返回值检查进行比较,那么也应该考虑这种缓存效应(在讨论中通常忽略这个问题)。卡尔
7xzttuei7#
答案难道不取决于投掷的结果是什么?如果抛出一个异常,导致整个对象负载超出堆栈的作用域,那么这将增加开销。换句话说,我不确定第三个问题的答案是否独立于代码的细节。
dojqjjoe8#
关于g++如何处理异常的完整细节显示在here中。它将其描述为用于Itanium体系结构,但是使用的一般技术是相同的。它不会告诉你确切的时间开销,但是你可以收集粗略的代码开销。
8条答案
按热度按时间sg3maiej1#
draft Technical Report on C++ Performance的第5.4节专门讨论异常的开销。
px9o7tmv2#
作为一个建议:当抛出异常时,不要太在意开销。异常处理的实现通常会使不抛出变快,捕获变慢。这是确定的,因为这些情况下,嗯,例外。
卡尔
yyhrrdl83#
这是我想出来的测量代码。你觉得有什么问题吗?
到目前为止可以在Linux和Windows上运行,编译时使用:
或者例如Visual C++ Express。
要获取基本情况(“从语言中完全删除异常支持”),请使用:用途:
或类似的MSVC设置。
一些初步结果here。由于机器负载的变化,它们可能都是虚假的,但它们确实给予了一些关于相对异常处理开销的概念。(执行摘要:当没有抛出异常时,没有或很少,当它们实际上被抛出时,巨大。
rbpvctlc4#
Kevin Frei在他的演讲“The Cost of C++ Exception Handling on Windows“中谈到了异常处理性能成本。(在“总结和结论”下有一个列表项,上面写着“[异常处理性能成本]并不总是可衡量的”。
izkcnapc5#
在代码中没有好的方法来保证这一点。你需要使用一个侧写器。
这不会直接显示异常处理花费了多少时间,但通过一点点研究,您将发现哪些运行时方法处理异常(例如,对于VC++.NET,它是__cxx_exc[...])。
把他们的时间加起来,你就有了开销。在我们的项目中,我们使用了Intel的vcss,它可以与Visual C++和gcc一起工作。
编辑:好吧,如果你只是需要一个通用的数字,可能会工作。我以为你有一个实际的应用程序来分析你不能关闭异常的地方。
aij0ehis6#
关于异常处理性能的另一个注意事项:简单的测试不考虑缓存。try代码和catch代码都很小,可以容纳指令和数据缓存。但是编译器可能会尝试将catch-code移到远离try-code的地方,这减少了通常需要保存在缓存中的代码量,从而提高了性能。
如果将异常处理与传统的C风格的返回值检查进行比较,那么也应该考虑这种缓存效应(在讨论中通常忽略这个问题)。
卡尔
7xzttuei7#
答案难道不取决于投掷的结果是什么?如果抛出一个异常,导致整个对象负载超出堆栈的作用域,那么这将增加开销。
换句话说,我不确定第三个问题的答案是否独立于代码的细节。
dojqjjoe8#
关于g++如何处理异常的完整细节显示在here中。它将其描述为用于Itanium体系结构,但是使用的一般技术是相同的。它不会告诉你确切的时间开销,但是你可以收集粗略的代码开销。