xiv*_*r77 16 c c++ exception setjmp
我编写了一个测试来测量带有线程的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;
}
Run Code Online (Sandbox Code Playgroud)
这是我最初为了好玩而编写的C版本.
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <glib.h>
#define N 100000
static GPrivate jumpBuffer;
static void doSomething(volatile int *pn)
{
jmp_buf *pjb = g_private_get(&jumpBuffer);
--*pn;
longjmp(*pjb, 1);
}
static void *throwManyManyTimes(void *p)
{
jmp_buf jb;
volatile int n = N;
(void)p;
g_private_set(&jumpBuffer, &jb);
while (n)
{
switch (setjmp(jb))
{
case 0:
doSomething(&n);
case 1:
continue;
default:
printf("error\n");
exit(EXIT_FAILURE);
}
}
return NULL;
}
int main(void)
{
int nCPUs = g_get_num_processors();
GThread *threads[nCPUs];
int i;
for (i = 0; i < nCPUs; ++i)
{
threads[i] = g_thread_new(NULL, throwManyManyTimes, NULL);
}
for (i = 0; i < nCPUs; ++i)
{
g_thread_join(threads[i]);
}
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
与C版本相比,C++版本运行速度非常慢.
$ 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
Run Code Online (Sandbox Code Playgroud)
所以我运行了callgrind探查器.
因为cpp-test,__cxz_throw被称为正好400,000次,自费为8,000,032.
因为c-test,__longjmp_chk被称为正好400,000次,自费为5,600,000.
整个成本cpp-test是4,048,441,756.
整个成本c-test是60,417,722.
我想的不仅仅是保存跳转点的状态,以后再用C++异常完成.我无法测试更大,N因为callgrind探查器将永远运行C++测试.
在这个例子中,C++异常所涉及的额外成本是多少,使得它比setjmp/ longjmppair 慢很多倍?
Dre*_*ann 19
这是设计的.
预计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
Run Code Online (Sandbox Code Playgroud)
在C:
/*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
Run Code Online (Sandbox Code Playgroud)
C++异常所涉及的额外成本是多少,使得它比setjmp/longjmp对慢很多倍,至少在这个例子中?
g ++实现了零成本模型异常,当没有抛出异常时,它们没有有效的开销*.生成机器代码就好像没有try/ catch块一样.
这种零开销的代价是,当抛出异常时必须在程序计数器上执行表查找,以确定跳转到适当的代码以执行堆栈展开.这将整个try/ catch块实现放在执行代码的代码中throw.
您的额外费用是查表.
*可能会出现一些小的时序伏都教,因为PC查找表的存在可能会影响内存布局,这可能会影响CPU缓存未命中.
| 归档时间: |
|
| 查看次数: |
2203 次 |
| 最近记录: |