程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 淺談C++ 異常處理的語義和性能

淺談C++ 異常處理的語義和性能

編輯:C++入門知識

淺談C++ 異常處理的語義和性能


異常處理是個十分深奧的主題,這裡只是淺論其對C++性能的影響。   在VC++中,有多個異常處理模式,三個最重要:   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 如果此函數沒有catch,繼續往上面函數,重復以上步驟   VC++的stack unwinding實現大致如此:   異常處理邏輯可以轉換成一個靜態的jump列表(列出上面的四個熱點的jump to 地址),和一個stack_unwind()函數(堆棧回滾函數),根據當前的”熱點”,通過此列表,動態地跳到異常處裡的回滾代碼處。   綜合起來,異常處理在C++中,根據函數的auto變量的分布,必須在每個可能出現異常的函數添加上訴jump列表,導致程序size和工作集明顯增加。但是測試表明,如果不出現異常,程序的執行速度的影響是可忽略的(僅僅需要保持熱點位置),TestMyAppObject的測試結果選擇異常處理(但不出異常)反而比選擇不支持異常處理稍快。    出現異常後,TestMyAppObject的測試結果表明,程序速度的影響可以在10%~15%以上。但是我的測試還沒有加rethrow 獲者其它異常處理邏輯,僅僅俘獲而已。   另一個有趣的問題是,函數中auto變量的分布,對“熱點列表”size的影響, 熱點太多,會導致熱點列表變得很大,所以如果可能,盡量把auto變量放在頂端:   X a, b;   Y c,d;   而不是   X a;   // do something (1)   X b;   // do something else (2)   Y c;   // do yet something else (3)   Y d;   因為第一種分布只有一個熱點(假設constructor 不會throw)。而第二種分布至少有三個熱點。   測試結果 測試上述TestMyAppObject函數,循環1000次的結果:    傳值0,使line4不出現異常(C++ throw),時間是0.802秒。         傳值1,使line4出現除零異常,時間是0.832秒。   傳值2,使line4出現異常(C++ throw),測試1000次測試,時間是1.043秒。 這個結果我有下列觀察:   C++ throw的代價明顯高於windows SEH。C++ throw 異常在上述測試的時間比不出現異常增加近20%。但是如果我們throw簡單的primitive 值,速度可能增快(讀者可以自己測試)。 除零異常在這裡和無異常速度接近,但是考慮到本測試的簡單性,和實際應用中try-catch可能縱跨多個函數,會線性增加stack-unwinding的代價。所以我認為,實際結果中,如果異常出現後出現性能10%~15%下降是正常的。 另外要考慮的是OS和編譯版本。VC++的異常處理比前面版本的性能大大提高了。  總結 異常處理是C++中具有重要附加值的語言構造,為安全可靠的應用程序提供了基石。   但是它也同時具有時空兩方面的代價(trade off),我們在應用時要清楚這個方面。異常應該在“異常時”用 (好像是廢話,其實是設計思想和模式的重要一環),不要把它當作方便的“控制構造 control construct”來用。如果應用容許,也要盡可能減少“熱點”,減小熱點列表。    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved