C++中的异常

C++中的异常是很多人不推荐使用的机制,但其中有很多玄学,不妨来讲一讲。

C++ 异常的开销

C++ 将异常视为特殊路径。编译器优化时倾向于对 try-catch 包裹中的代码按照不抛异常进行优化。反之,如果执行到异常代码,则会伴随着很高的惩罚。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <iostream>
#include <stdexcept>
#include <exception>
#include <thread>
#include <chrono>
#include <cstdio>
#include <vector>

using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;

std::vector<int> global_vec;

int exception_call_all_throw_inner(int a) {
throw std::runtime_error("prevent optimization");
return 1;
}

int exception_all_throw_call(int & a) {
try
{
exception_call_all_throw_inner(a);
} catch (...) {
a = (a + 1) % 10000;
if (a % 100 == 0) {
global_vec.push_back(a);
}
if (a == 9999) {
global_vec.clear();
}
if (a % 100 == 55) {
a += global_vec.size();
}
}
return a;
}

int exception_call_no_throw_inner(int a) {
if (a == 10010) {
throw std::runtime_error("prevent optimization");
}
return 1;
}

int exception_no_throw_call(int & a) {
a = (a + 1) % 10000;
if (a % 100 == 0) {
global_vec.push_back(a);
}
if (a == 9999) {
global_vec.clear();
}
try
{
exception_call_no_throw_inner(a);
} catch (...) {
printf("error");
}
if (a % 100 == 55) {
a += global_vec.size();
}
return a;
}

int no_exception_call(int & a) {
a = (a + 1) % 10000;
if (a % 100 == 0) {
global_vec.push_back(a);
}
if (a == 9999) {
global_vec.clear();
}
if (a % 100 == 55) {
a += global_vec.size();
}
return a;
}

template<typename F>
int loop_main(F f) {
int a = 1;
global_vec.clear();
for(int i = 0; i < 19999933; i++) {
f(a);
}
printf("%d\n", a);
return 0;
}

int main() {
int ms_inte_no;
int ms_inte_all;
int ms_intnoe;
{
auto t1 = high_resolution_clock::now();
loop_main(exception_no_throw_call);
auto t2 = high_resolution_clock::now();
ms_inte_no = duration_cast<milliseconds>(t2 - t1).count();
printf("exception no throw cost %d vec %lu\n", ms_inte_no, global_vec.size());
}
{
auto t1 = high_resolution_clock::now();
loop_main(exception_all_throw_call);
auto t2 = high_resolution_clock::now();
ms_inte_all = duration_cast<milliseconds>(t2 - t1).count();
printf("exception all throw cost %d vec %lu\n", ms_inte_all, global_vec.size());
}
{
auto t1 = high_resolution_clock::now();
loop_main(no_exception_call);
auto t2 = high_resolution_clock::now();
ms_intnoe = duration_cast<milliseconds>(t2 - t1).count();
printf("no exception cost %d vec %lu\n", ms_intnoe, global_vec.size());
}
printf("loss exception no throw %f\n", (ms_inte_no - ms_intnoe) * 1.0 / ms_intnoe);
printf("loss exception all throw %f\n", (ms_inte_all - ms_intnoe) * 1.0 / ms_intnoe);
}

以上面的代码为例,执行结果如下,可以发现,如果全抛异常的执行时间达到一百多倍之多。

1
2
3
4
5
6
7
8
1619
exception no throw cost 118 vec 45
1619
exception all throw cost 21453 vec 45
1619
no exception cost 114 vec 45
loss exception no throw 0.035088
loss exception all throw 187.184211

Reference