程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++ 性能剖析 (四):Inheritance 對性能的影響

C++ 性能剖析 (四):Inheritance 對性能的影響

編輯:C++入門知識

C++ 性能剖析 (四):Inheritance 對性能的影響


Inheritance 是OOP 的一個重要特征。雖然業界有許多同行不喜歡inheritance,但是正確地使用inheritance是一個應用層面和架構層面的重要設計決定。 大量使用inheritance,尤其在類似std container 中使用,會對程序性能產生何等影響呢?   從我個人的經驗來看,constructor對創建具有深層inheritance鏈的class,有很大的影響。 如果應用容許,最好使用沒有constructor的基類。下面舉個例子:   struct __declspec(novtable) ITest1   { virtual void AddRef() = 0;       virtual void Release() = 0;       virtual void DoIt(int x) = 0; };   class CTest: public ITest1   {   int ref;   public: inline CTest() { ref = 0; }   inline void AddRef() { ++ref; }   inline void Release() {--ref; }   inline void DoIt(int x) {ref *= x; }   inline void AddRef2() { ++ref; }   inline void Release2() {--ref; }   inline void DoIt2(int x) {ref *= x; }   static void TestPerf(int loop); };   這是個dummy程序,然而在COM中確是再常見不過。如果我們要大量創建並使用CTest,有經驗的程序員應該看出,ITest1 完全不需要constructor。 根據C++ 說明書,ITest1因為有虛擬函數,屬於“非簡單構造類”,編譯必須產生一個constructor,其唯一的目的是設置ITest1的vtbl (虛擬函數表)。   然而interface的唯一作用是被繼承,所以其vtbl一定是被其繼承類設置。編譯在這種情況下沒必要生成constructor。 微軟在設計ATL時認識到這一點,推出自己的方案來躲避C++官方SPEC的缺陷:VC++提供了novtable的class modifier,告訴編譯:我不需要你的constructor. 然而我在VS 2010中的測試結果卻令人失望:   ITest1的constructor 仍然被生成了,只是它沒有將vtbl賦值而已,這對增進基類構造的性能實為杯水車薪之舉。 下面我們看看這個“毫無用處的constructor”對性能的影響。 我們權且拿出另一個不需要虛擬函數的ITestPOD (POD的意思是“數據而已”)來做比較:   struct ITest1POD   { inline void AddRef() { }   inline void Release() { }   inline void DoIt(int x) { } };   ITestPOD當然不能完全作interface用(interface必須用虛擬函數),僅僅為了測試。然後,我們設計一個繼承類,和上面的CTest功能完全一樣:   class CTestPOD: public ITest1POD   {   int ref;   public: inline CTestPOD() { ref = 0; }   inline void AddRef() { ++ref; }   inline void Release() {--ref; }   inline void DoIt(int x) {ref *= x; }   };   我們的目的是用這個CTestPOD來和CTest作一番蘋果與蘋果的比較:   void CTest::TestPerf(int loop)   {   clock_t begin = clock();   for(int i = 0; i < loop; ++i) //loop1   {   CTestPOD testPOD; // line1   testPOD.AddRef();   testPOD.DoIt(0);   testPOD.Release();   }   clock_t end = clock();   printf("POD time: %f \n",double(end - begin) / CLOCKS_PER_SEC);   begin = clock();   for(int i = 0; i < loop; ++i) //loop2   {   CTest test; // line2   test.AddRef2();   test.DoIt2(0);   test.Release2();   }   end = clock();   printf("Interface time: %f \n",double(end - begin) / CLOCKS_PER_SEC);   }   上面的loop1和loop2的唯一區別在line1和line2,為了避免用虛擬函數,我特意給CTest准備了AddRef2,DoIt2,Release2,三個同樣的但卻是非虛擬的函數,為的是遵循性能測試的一大原理:compare apple to apple。   我將loop設為10萬,測試結果顯示,loop2比loop1的速度低了20% 左右。從生成的代碼來看,唯一的區別是CTest的constructor調用了編譯自動生成的ITest1 的constructor。這個constructor沒有任何作用,卻白占了許多CPU周期。一個好的編譯,應該是可以把這個constructor裁剪掉的,這個靠我們自己去搜索了。   總結   在應用inheritance時,除去基類裡無用的constructor,對大量構造的object的性能來說,會有明顯的影響。不幸的是,微軟的__declspec(novtable) class modifier對解決這個問題沒有提供任何幫助。在設計海量存儲的object的應用中,我們應該盡量用POD來做其基類,避免上面CTest類那樣明顯的性能漏洞。

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