异常处理是个十分深奥的主题,这里只是浅论其对C++性能的影响。
?
?
No exception handling (无异常处理)
C++ only (C++语言异常处理)
C++ 加SEH (C++语言加windows 结构异常处理机制)
异常处理每增加一个级别,都要付出时空上的代价。我们从下面简单的C++例子着手,分析异常处理的原理及其性能:
?
?
?
// simple class
?
class MyAppObject
?
{
?
? ? public:
?
? ? ? ?MyAppObject(int id) : _myID(id) {}
?
? ? ? ?~MyAppObject();
?
? ? ? ?int _myID;
?
? ? ? ?void DoSomething(int throwWhat) ;
?
};?
?
// can throw 2 different exception
?
void MyAppObject::DoSomething(int throwWhat)
?
{
?
? ? printf("MyAppObject::DoSomething called for '%d'\n", _myID);
?
? ? switch (throwWhat)
?
? ? {
?
? ? ? ?case 0:
?
? ? ? ? ? ? ?break;
?
? ? ? ?case 1:
?
? ? ? ? ? ? ?this->_myID /= 0; ? ? ? ? ? ? // exception 1
?
? ? ? ? ? ? ?break;
?
? ? ? ?case 2:
?
? ? ? ? ? ? ?throw SimpleString("error!"); // exception 2
?
? ? ? ? ? ? ?break;
?
? ? ? ?}
?
}
?
?// Test exception for the above class
?
void TestMyAppObject()
?
{
?
? ? ? ?printf("before try”);?
?
? ? ? ?try ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// line1
?
? ? ? ?{
?
? ? ? ? ? ? ? printf("in try”);
?
?
?
? ? ? ? ? ? ?MyAppObject so = 1; ? ? ? ? ? ? ? ? ? ? ? ? ?// line2
?
? ? ? ? ? ? ?SimpleString ss("test ex point one"); ? ?// line3
?
? ? ? ? ? ? ?so.DoSomething(1); ? ? ? ? ? ? ? ? ? ? ? ? ? // line4
?
?
?
? ? ? ? ? ? ?printf("so::ID called for '%d'\n", so._myID);
?
? ? ? ? ? ? ?MyAppObject so2 = 2; ? ? ? ? ? ? ? ? ? ? ? // line5
?
?
?
? ? ? ? ? ? ?printf("so2::ID called for '%d'\n", so2._myID);
?
? ? ? ? ? ? ?so2.DoSomething(0); ? ? ? ? ? ? ? ? ? ? ? ?// line6
?
? ? ? ?}
?
? ? ? ?catch(const SimpleString &e) ? ? ? ? ? ? ? ? ? // line7
?
? ? ? ?{
?
? ? ? ? ? ? ?//printf("something happened: %s \n", e);
?
? ? ? ?}
?
? ? ? ?catch(...) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//line8
?
? ? ? ?{
?
? ? ? ? ? ? ?//printf("something happened: %s \n", "SEH");
?
? ? ? ?}
?
}?
?
?
?
第一步,我们先选择“no exception”,并将上面line1,line7,line8注释掉。代码的size是:
?
Exe
?
Obj
?
32,256 bytes
?
20,931 bytes
?
?
?
然而因为line4引入一个“除0”异常,我们的程序非正常地停止了工作。这并非什么大的灾难。但是如果这是关键的服务器程序,这样的结果肯定不能为客户接受。
?
?
?
第二步,我们选择了,C++ only flag(/EHsc)。代码size变为:
?
Exe
?
Obj
?
37,888 bytes
?
24,959 bytes
?
代码size较前面选择增加了近20%。
?
?
?
然而,这个选择决定了如果是C++的throw产生的异常我们可以俘获。操作
系统产生的异常,比如
windows SEH 异常机制产生的异常,也不能俘获。测试时,将line1,line7,Line8注释取消。
?
运行程序,“除0”异常仍然导致程序停止。然而,将line4输入改为2时,C++ throw 的异常被line7俘获。
?
?
?
第三步,我们选择“C++ 加 SHE (/EHa)”,代码size变为:
?
?
?
Exe
?
Obj
?
37.0 KB (37,888 bytes)
?
28,486 bytes
?
?
?
代码 obj size 略有变化,但是不显著。选择了这个后,MyAppObject::DoSomething的两种异常都能被俘获了。
?
异常处理语义
加了异常处理,程序的“工作集(working set)”, 的增长度高达20%,这是相当显著的。关键的软件部件必须考虑到这一点。那么,运行速度会不会受到影响呢?我们先看看异常处理的语义吧。
?
上面的TestMyAppObject中,由于C++必须保证一旦异常出现,能“正确地”地销毁自动变量,比如TestMyAppObject中的so,ss,和 so2 变量。在有异常处理的情况下,必须区分“现行程序”的“区域”和“热点”。
?
比如,TestMyAppObject的区域有before try 和 in try。
?
TestMyAppObject热点有line2 ~ line6 (每个line都是一个热点)。
?
TestMyAppObject异常处理的逻辑是:
?
做“stack unwinding (堆栈回滚)”:
如果line2出异常,无须作什么(除非有MyAppObject 里有部分未完成构造的成员partially constructed member 问题)。
如果line3出异常,so必须销毁。
如果line4出异常,so和ss都必须销毁。
如果line6出异常,so,ss,和so2都须销毁。
如果找到catch,执行catch