系統性能調優經驗

在開發對外提供服務的模塊的時候,系統的性能常常會是令咱們頭疼的問題,具體系統性能的定義與瓶頸的定位方法,能夠參考陳皓的這篇文章:html

性能調優攻略http://coolshell.cn/articles/7490.html 大牛的這篇文章仍是很全面的。linux

下面我會以咱們的一些工程經驗和曾經遇到過的問題,來實例化一些系統性能調優的經驗。算法

 

Ø  代碼級別shell

1.       減小數據copy緩存

1)使用指針,代替靜態數據copy服務器

如下面的數據結構爲例:網絡

struct goods_info_for_rank_t數據結構

{多線程

    good_info_ptrp_goods_info;//靜態數據,只可讀不可寫!!!架構

    ….//省略若干

   //策略相關的字段

   float doc_weight;

   float goods_weight;

   };

之前這個goods_info_for_rank_t 進行內容賦值的時候要從p_goods_info 拷貝所有的數據到goods_info_for_rank結構,其實不少靜態數據只是可讀的而且數據量很大,當時優化的一個思路是去掉這個結構體,只使用good_info_ptr這個結構,可是忽略了這個結構體是靜態只讀,而且多線程併發使用的,從而使得多線程狀況下出現core dump(修改後須要寫,致使多線程同時寫一份good_info_ptr數據),後來才用這個方案優化了這段程序性能;

 

2)使用引用代替棧上數據;

這個是常識性的知識,即當咱們傳入一個大結構體或者類的時候,儘可能使用引用或者經過指針訪問,不然棧上變量進行大內存申請將是很大的性能損耗。

 

3)返回指針,減小數據的COPY,對比下面的兩個函數:

void read(uint32 id, void *brief)

{

   if(id >= _num)

       return;

   memcpy(brief, _array_mem + id * _size, _size);

}

 

void read_no_copy(uint32 id)

{

    if(id>= _num)

       return NULL;

   return _array_mem + id * _size;

}

若是數據只讀,後面的函數減小一次內存copy效率更高;

2.       內存申請

1)減小沒必要要的malloc

堆是多線程共享的,頻繁的malloc會致使系統申請內存速度的降低,因此肯定的大塊內存,咱們的建議都是在線程空間預先申請好,而不是每次都申請內存。

對多線程頻繁malloc 內存消耗的解釋:

堆是進程內共享的, mallocfree的原理,大概是維護一個空閒內存塊的hash表:

1.     malloc時直接從這裏去,若是沒合適塊,還要作一些分裂;

2.     free時,視時機作一些內存塊合併,並掛接到hash表中;

在多線程狀況下,若是mallocfree太頻繁,線程間確定須要同步,並且分裂和合並等操做也會更多,也是開銷。

使用tcmalloc庫,能夠優化malloc內存效率,一些測試的結果是在多線程下能夠優化內存申請效率30%,tcmalloc源碼

http://gperftools.googlecode.com/svn/trunk/

也可使用本身實現的內存池技術來優化內存申請。

2)當心的memset

仍是繼續說前面的問題,當咱們在線程裏面預先申請大塊內存後,當一個請求處理完成再進入另一次請求時,咱們天然的想到要用memset清理內存,可是這個內存空間若是很大,memset這個內存就會很是的慢,曾經的一個項目裏面因爲memset 一個10M的內存,使得系統性能降低一半。

避免memset的一個方案是經過一個長度變量控制內存的讀取長度。

3)謹慎使用局部變量

局部變量真」好」,用的時候隨便申請一個,等做用域失效後自動釋放。但方便的同時也極可能引入問題,這個問題和1.2比較相似,linux系統的線程棧空間有限能夠經過ulimit –s查詢,查看咱們系統的限制爲10K(我寫了一個程序,發現超過2M的時候會core dump),因此當聲明一個較大的局部變量的時候,就會出現系統運行速度減慢,甚至會出現core dump。

3.       減小系統調用

對於程序的時間開銷咱們應該有這樣的認識:

 

   因此當進行頻繁的系統調用的時候對系統性能會有影響,在跟進檢索請求超時的問題中,發現每召回一個obj都要進行次過濾並調用time()函數看是否過時,致使請求時間超長。後面修改成一次請求只獲取time一次,則修復超時問題。在咱們調試性能問題的時候也常常在系統加入不少獲取時間的系統調用,自己這些系統調用也會影響到性能,從而使性能測試結果不許。

 

Ø  網絡級別

1)使用NODELAY參數

對於咱們的網絡服務器,咱們都是但願發出的包馬上可以發送給對方,因此通常設置NODEAL參數,以防止nagle算法對數據報進行緩存。

有關nagle算法:

http://en.wikipedia.org/wiki/Nagle's_algorithm

2)長鏈接與短鏈接

對於內部服務器間的通訊,若是能夠咱們都建議使用長鏈接,以提升響應的速度;

3)異常數據包的處理

通常當下遊服務器接受到一個異常的數據包的時候,咱們可能會簡單的丟棄它,這樣可能引起一些問題。好比:一個同窗在升級下游服務的時候將異常的報丟棄掉,而且沒有關閉鏈接,致使上游服務器一直等下游迴應,而hang住整個系統,由於你沒法知道上游服務器的處理邏輯,若是它設計的很挫,可能對等待這個響應報好久才超時;另外當異常報比較多的狀況下,咱們若是reset鏈接,也會影響到整個系統的響應時間,由於上游服務器要從新創建鏈接。(上游最好不要把錯誤的包傳到下游,由於那可能引起下游的報警或者異常)

Ø  架構級別

1)併發訪問

當咱們有些請求要發送給多個下游服務器,而這些請求又是獨立的時候,能夠採用併發訪問。好比一個檢索系統裏面咱們同時可能要獲取幾個檢索分類的結果,這個時候咱們就可能用到併發訪問(網絡模型、select、epoll奔騰而過)。

2)Cache使用

Cache是提高系統性能的經常使用方法,若是檢索系統中有些query就是比較慢,一個直觀的方法就是使用Cache,咱們可使用結果緩存,甚至能夠採用預充的手段,將長響應query預充到緩存區;好吧,咱們又必須面臨緩存區髒了的問題。

3)空間換時間&線下計算

若是有些數據能夠在線下計算,那儘可能在線上完成,但通常會增長些系統的複雜度和空間的佔用。咱們的一個項目之前要實時計算每一個站點的結果數(召回站點結果真後計數),後面把每一個站點的結果數建到索引裏面,性能提高30倍。

Ø  編譯優化

 

    -O2是推薦的優化等級,也是咱們程序使用的優化等級。

相關文章
相關標籤/搜索