C+的成本+;异常和setjmp/longjmp 我编写了一个测试,用线程来测量C++异常的成本。 #include <cstdlib> #include <iostream> #include <vector> #include <thread> static const int N = 100000; static void doSomething(int& n) { --n; throw 1; } static void throwManyManyTimes() { int n = N; while (n) { try { doSomething(n); } catch (int n) { switch (n) { case 1: continue; default: std::cout << "error" << std::endl; std::exit(EXIT_FAILURE); } } } } int main(void) { int nCPUs = std::thread::hardware_concurrency(); std::vector<std::thread> threads(nCPUs); for (int i = 0; i < nCPUs; ++i) { threads[i] = std::thread(throwManyManyTimes); } for (int i = 0; i < nCPUs; ++i) { threads[i].join(); } return EXIT_SUCCESS; }

C+的成本+;异常和setjmp/longjmp 我编写了一个测试,用线程来测量C++异常的成本。 #include <cstdlib> #include <iostream> #include <vector> #include <thread> static const int N = 100000; static void doSomething(int& n) { --n; throw 1; } static void throwManyManyTimes() { int n = N; while (n) { try { doSomething(n); } catch (int n) { switch (n) { case 1: continue; default: std::cout << "error" << std::endl; std::exit(EXIT_FAILURE); } } } } int main(void) { int nCPUs = std::thread::hardware_concurrency(); std::vector<std::thread> threads(nCPUs); for (int i = 0; i < nCPUs; ++i) { threads[i] = std::thread(throwManyManyTimes); } for (int i = 0; i < nCPUs; ++i) { threads[i].join(); } return EXIT_SUCCESS; },c++,c,exception,setjmp,C++,C,Exception,Setjmp,所以我运行了callgrind分析器 对于cpp测试,\uuuucxz\uthrow被精确调用了400000次,自身成本为800032 对于c-test,longjmp\u chk的调用次数正好为400000次,自开销为5600000 cpp测试的全部成本为4048441756 c-test的全部成本为60417722 我猜,除了简单地保存跳点状态和稍后恢复,还有很多事情是用C++异常完成的。我不能用较大的 n>代码>进行测试,因为CalgReD剖析器将永远运行C++测试。 C++异常中的额

所以我运行了callgrind分析器

对于
cpp测试
\uuuucxz\uthrow
被精确调用了400000次,自身成本为800032

对于
c-test
longjmp\u chk
的调用次数正好为400000次,自开销为5600000

cpp测试的全部成本为4048441756

c-test
的全部成本为60417722


<>我猜,除了简单地保存跳点状态和稍后恢复,还有很多事情是用C++异常完成的。我不能用较大的<代码> n>代码>进行测试,因为CalgReD剖析器将永远运行C++测试。


C++异常中的额外开销是什么,使得它比 SETJMP LojMP >对的速度慢很多倍?至少在这个例子中是这样的?

<强>这是设计。< /强>

C++异常在本质上被认为是异常的,因此被优化。当不发生异常时,程序被编译为最有效的

您可以通过注释掉测试中的异常来验证这一点

在C++中:

    //throw 1;

$ g++ -O3 -g -std=c++11 test.cpp -o cpp-test -pthread

$ time ./cpp-test

real    0m0.003s
user    0m0.004s
sys     0m0.000s
在C中:

C++异常的额外开销是什么?它至少比这个例子中的SETJMP/LIJMP对慢很多倍?

g++实现零成本模型异常,当异常未抛出时,它没有有效的开销*。机器代码的生成就像没有
try
/
catch
块一样

这种零开销的代价是,当抛出异常时,必须在程序计数器上执行表查找,以确定跳转到执行堆栈展开的适当代码。这将整个
try
/
catch
块实现放在执行
抛出的代码中


额外的费用是查表


*可能会出现一些小的计时巫术,因为PC查找表的存在可能会影响内存布局,这可能会影响CPU缓存未命中。

这里有各种groovy信息:您正在测试不应该经常调用的异常的性能。如果你的程序抛出了那么多导致性能问题的异常,那么你就有了一个严重的问题。您应该测试的是使用try/catch块与不使用try/catch块,并查看准备好异常以处理问题的成本。这将向您展示在真正需要时准备好异常的成本。异常(正确完成)与setjmp/longjmp有很大不同。除了例外情况,您实际上可以处理问题并保持程序运行。您需要提供有关您的g++构建的更多信息。在Windows中,g++可以使用3种不同的异常处理机制构建:SetJmp/LongJmp、Win32 SEH或Dwarf2。后两个选项(我相信)是零开销的:如果没有抛出异常,那么就没有运行时惩罚。当您输入
try{
块时,SJLJ会浪费时间设置setjmp。我不确定,但我怀疑您会发现,如果您使用SJLJ,那么您会得到与C程序类似的结果;SEH和Dwarf2选项使最常见的用例更快(即,不会抛出任何东西)请注意,异常涉及所有类型的清理工作,而
setjmp
/
longjmp
完全忽略了这些工作。如果在调用
setjmp
和调用
longjmp
之间分配内存,则在调用
longjmp
时可能泄漏了它。很可能是泄漏了其他资源(文件描述符等)。即使不是在您的测试代码中,在一般情况下。是的,
setjmp
longjmp
工作,但它们通常是一种非常粗糙的异常处理机制。“您额外的成本是查表。”这是对问题的轻描淡写。在热表中查表(在这段代码中,表肯定是热的,因为相同的异常会从同一行一次又一次地抛出)成本很低。一个大成本
setjmp
/
longjmp
避免的是内存分配;每个抛出的异常都涉及动态内存分配(例如,在
g++
上,它调用
\ucxa\u allocate\u exception
,以后必须释放该异常),然后
抛出
本身涉及到在跳转到处理程序代码之前组装大量堆栈展开信息(在这种情况下,大部分信息都会被丢弃而不被使用)。
$ g++ -O3 -g -std=c++11 test.cpp -o cpp-test -pthread
$ gcc -O3 -g -std=c89 test.c -o c-test `pkg-config glib-2.0 --cflags --libs`
$ time ./cpp-test

real    0m1.089s
user    0m2.345s
sys     0m1.637s
$ time ./c-test

real    0m0.024s
user    0m0.067s
sys     0m0.000s
    //throw 1;

$ g++ -O3 -g -std=c++11 test.cpp -o cpp-test -pthread

$ time ./cpp-test

real    0m0.003s
user    0m0.004s
sys     0m0.000s
    /*longjmp(*pjb, 1);*/

$ gcc -O3 -g -std=c89 test.c -o c-test `pkg-config glib-2.0 --cflags --libs`

$ time ./c-test

real    0m0.008s
user    0m0.012s
sys     0m0.004s