天天看點

C++ 應用程式性能優化

<a href="mailto:[email protected]">[email protected]</a>

1. Introduction

對于幾何造型核心OpenCASCADE,由于會涉及到大量的數值算法,如矩陣相關計算,微積分,Newton疊代法解方程,以及非線性優化的一些算法,如BFGS,FRPR,PSO等等用于多元函數的極值求解,是以這些數值算法的性能直接影響系統的性能。軟體的性能優化是計算機軟體開發過程中需要一直關注的重要因素,是以有必要學習下C++應用程式性能優化的方法。

在網上尋找相關資料時,發現這方面的資料也很少,最後發現一本由電子工業出版社出版的《C++應用程式性能優化方法》,從中可以學習下IBM的性能優化方法。

本文主要結合《C++性能優化方法》并結合代碼執行個體來說明記憶體優化處理對程式性能的影響。看完本書,其實發現C++性能優化方法主要還是依賴的計算機相關的基礎知識,比如說計算機作業系統,資料結構與算法等等。

2.Memory Optimize

C++程式中的存儲空間可以分為靜态/全局存儲區,棧區和堆區。靜态/全局存儲區和棧區的大小一般在程式編譯階段決定;而堆區則随着程式的運作而動态變化,每一次程式運作都會有不同的行為。這種動态的記憶體管理對于一個程式在運作過程中占用的記憶體大小及程式的性能有非常重要的影響。

因為靜态/全局存儲區在編譯時就已經确定了,而棧區則是由作業系統在管理,這些都不勞程式員費神。就是這個堆區是提供給程式員的自由舞台,可以任性發揮。堆區的存儲區域怎麼玩呢?了解了這三個問題應該就知道了:一是堆區資料怎麼産生(從哪兒來)?二是堆區的資料怎麼銷毀(到哪兒去)?堆區資料怎麼通路?由C++标準規定得知,C++實作通過全局的new和delete來提供動态記憶體的通路和管理。而堆區資料的通路就是通過指針了。當使用new/delete來操作堆上的存儲區域時,作業系統就要對堆的存儲區域進行管理,是以這個管理工作就會對應用程式的性能有影響。

為了解決記憶體洩露問題,即new之後不再使用時并沒有delete,OpenCASCADE中入了Handle智能指針的宏定義。Handle的使用也很簡單,隻需要将用Handle(Class)就可以了。

利用預設的記憶體管理new/delete在堆上配置設定和釋放記憶體會有一些額外的開銷。系統在接收到一定大小記憶體請求時,首先查找内部維護的記憶體空閑塊表,并且需要根據一定的算法(例如配置設定最先找到的不小于申請大小的記憶體塊給請求者,或者配置設定最适于申請大小的記憶體塊等)找到合适大小的記憶體塊。如果該空閑記憶體塊過大,還需要切割成已配置設定的部分和較小的空閑塊,然後系統更新記憶體塊表,完成一次記憶體配置設定。類似地,在釋放記憶體時,系統把釋放的記憶體塊重新加入到空閑記憶體塊表中。如果有可能的話,可以把相鄰的空閑塊合并成較大的空閑塊。

預設的記憶體管理函數還考慮到多線程的應用,需要在每次配置設定和釋放記憶體時加鎖,同樣增加開銷。可見,如果應用程式頻繁地在堆上配置設定格釋放記憶體,則會導緻性能的損失。并且會使系統中出現大量的記憶體碎片,降低記憶體的使用率。

由此可見,在簡單的new和delete背後,系統默默地為我們做了這麼多的事,而做這些事都是要花時間的啊!雖然預設的記憶體管理算法也考慮了性能,但是考慮的是更通用的情況,為了應付更複雜、更廣泛的情況,需要做更多額外工作。而對于具體的應用程式來說,适合自身特定的記憶體管理則可以獲得更好的性能,為此OpenCASCADE引入了自己的記憶體管理機制,與記憶體池概念類似。OCCT的記憶體通過配置,可以使用沒的優化技術,或者不使用任何優化直接使用系統的malloc和free。這些配置都是通過環境變量來實作,其中打開和關閉記憶體優化的開關是環境變量:MMGT_OPT,這個預設是0,即不使用任何優化;設定成1就是使用;設定成2就是使用TBB的記憶體優化技術(這個的前提是第三方庫正确配置,如果沒有還是使用的malloc和free;

// paralleling with Intel TBB

#ifdef HAVE_TBB

  #include &lt;tbb/scalable_allocator.h&gt;

  using namespace tbb;

#else

  #define scalable_malloc malloc

  #define scalable_calloc calloc

  #define scalable_realloc realloc

  #define scalable_free free

#endif

下面通過代碼來看看使用記憶體管理的性能有什麼變化。

3.Code Example

下面通過程式來看看使用這些技術對性能的影響:

/*

*    Copyright (c) 2016 Shing Liu All Rights Reserved.

*

*           File : main.cpp

*         Author : Shing Liu([email protected])

*           Date : 2016-07-31  11:54

*        Version : OpenCASCADE7.0.0

*    Description : Test OCCT Memory Manager and Handle(smart pointer).

*/

#include &lt;OSD_Timer.hxx&gt;

#include &lt;Poly.hxx&gt;

#include &lt;Poly_Triangulation.hxx&gt;

#pragma comment(lib, "TKernel.lib")

#pragma comment(lib, "TKMath.lib")

*@brief test memory without Handle(smart pointer) management.

 */

void testMemory(Standard_Integer theCount)

{

    OSD_Timer aTimer;

    aTimer.Start();

    for (Standard_Integer i = 0; i &lt; theCount; i++)

    {

        Poly_Triangulation* aTriangulation = new Poly_Triangulation(10, 5, Standard_False);

        delete aTriangulation;

    }

    aTimer.Stop();

    aTimer.Show();

}

*@brief test memory with Handle(smart pointer) management.

void testHandle(Standard_Integer theCount)

        Handle(Poly_Triangulation) aTriangulation = new Poly_Triangulation(10, 5, Standard_False);

* @brief set environment variable MMGT_OPT=0 to use malloc/free directly;

*        set environment varialbe MMGT_OPT=1 to use OCCT memory optimization technique;

*        set environment variable MMGT_OPT=2 to use paralleling with Interl TBB;

int main(int argc, char* argv[])

    int aCount = 100000;

    std::cout &lt;&lt; "\ntest pointer without handle" &lt;&lt; std::endl;

    testMemory(aCount);

    std::cout &lt;&lt; "\ntest pointer with handle" &lt;&lt; std::endl;

    testHandle(aCount);

    return 0;

如果直接運作上面的代碼編譯的程式,得到的結果如下圖所示:

C++ 應用程式性能優化

由圖可知,當使用Handle(智能指針)的時候,時間上影響不大,即使用Handle對性能影響基本上可以忽略,但是得到很多好處,主要的就是不用自己去delete了。使用Visual Studio的性能分析工具檢視,結果也類似:

C++ 應用程式性能優化

時間的開銷也是集中在記憶體的配置設定上面:

C++ 應用程式性能優化

注意到上面的Allocate()是類Standard_MMgrRaw的,即是直接使用的系統的malloc和free來管理記憶體。下面設定環境變量MMGT_OPT=1來使用OCCT的記憶體優化類看看對性能影響如何?

C++ 應用程式性能優化

程式運作的結果如下圖所示:

C++ 應用程式性能優化

與沒有使用記憶體優化的時候0.1相比,使用了記憶體優化處理的要快40%左右。

由下圖可以看出,性能熱點也是集中在記憶體的配置設定上面:

C++ 應用程式性能優化

注意現在記憶體配置設定使用的是Standard_MMgrOpt類中的Allocate函數:

C++ 應用程式性能優化

總的來說,将環境變量MMGT_OPT設定成1來使用OCCT的記憶體優化算法,性能提升還是很明顯的。

4.Conclusion

當程式規模越來越大,算法越來算複雜的時候,找到性能的瓶頸越麻煩一些。性能優化第一步是測量,找到合适的測量工具。《C++應用程式性能優化》一書中提供的是IBM Rational Quantify,在網上搜了下還有Intel VTune Amplifier等,功能都很強大。在Windows中開發程式使用的Visual Studio自帶了性能分析功能,使用起來也比較友善。

找到性能瓶頸,就要對其進行分析原因,進而修改程式,提高性能。這方面的方法論可以借鑒《C++應用程式性能優化》,從資料結構、程式啟動、記憶體管理等方面來分析。摘出此書中程式性能優化的流程圖:

C++ 應用程式性能優化

5. References

1.馮宏華,徐瑩,程遠,汪磊. C++應用程式性能優化. 電子工業出版社.

2.Scott Meryers. Effective C++(評注版). 電子工業出版社. 2011

3.OpenCASCADE Foundation Classes Document 7.0.0. 2016