C 程式常見的效能調優方式

2021-08-13 04:51:26 字數 3082 閱讀 4082

冗餘的變數拷貝

相對c而言,寫c++**經常一不小心就會引入一些臨時變數,比如函式實參、函式返回值。在臨時變數之外,也會有其他一些情況會帶來一些冗餘的變數拷貝。

之前針對冗餘的變數拷貝問題寫過一些帖子,詳情這裡。

多重過濾

很多服務都會過濾的部分結果的需求,比如遊戲交談中過濾需要過濾掉敏感詞。假設現在有兩個過濾詞典,乙個詞典a內容較少,另乙個詞典b內容較多,現在有1000個詞需要驗證合法性。

詞落在詞典a中的概率是1%,落在詞典b中的概率是10%,而判斷詞是否落在詞典a或b中的操作耗時差不多,記作n。

那麼要判斷詞是否合法,有兩種方式:

1. 先判斷詞是否在a中,如果在返回非法;如果不在再判斷是否在b中,如果在返回非法,否則返回合法。

2. 和方式一類似,不過是先判斷是否在b中。

現在我們來計算兩種方式的耗時:

1. 1000*n+1000*(1-1%)*n

2. 1000*n+1000*(1-10%)*n

很明顯,方式二的過濾操作排序優化方式一。

說得有些囉嗦,其實簡單點說就是一句話:多重過濾中把強過濾前移;過濾強度差不多時,過濾消耗較小的前移

如果有些過濾條件較強,但是過濾消耗也較大怎麼辦?該前移還是後移?個人到沒遇到過這種情況,如果確實需要考慮,也可以用之前計算方式

一、二整體耗時的方法也計算一遍。

字元陣列的初始化

一些情況是:寫**時,很多人為了省事或者說安全起見,每次申請一段記憶體之後都先全部初始化為0。

另一些情況是:用了一些api,不了解底層實現,把申請的記憶體全部初始化為0了,比如char buf[1024]=""的方式,有篇帖子寫得比較細,請看這裡。

上面提到兩種記憶體初始化為0的情況,其實有些時候並不是必須的。比如把char型陣列作為string使用的時候只需要初始化第乙個元素為0即可,或者把char型陣列作為乙個buffer使用的大部分時候根本不需要初始化。

頻繁的記憶體申請、釋放操作

曾經遇到過乙個效能問題是:乙個服務在啟動了4-5小時之後,效能突然下降。

檢視系統狀態發現,這時候cpu的sys態比較高,同時又發現系統的minflt值迅速增加,於是懷疑是記憶體的申請、釋放造成的效能下降。

最後定位到是服務的處理執行緒中,在處理請求時有大量申請和釋放記憶體的操作。定位到原因之後就好辦了,直接把臨時申請的記憶體改為執行緒變數,效能一下子回公升了。

能夠迅速的懷疑到是臨時的記憶體申請造成的效能下降,還虧之前看過這篇帖子。

至於為什麼是4-5小時之後,效能突然下降,則懷疑是記憶體碎片的問題。

提前計算

這裡需要提到的有兩類問題:

1. 區域性的冗餘計算:迴圈體內的計算提到迴圈體之前

2. 全域性的冗餘計算

問題1很簡單,大部分人應該都接觸到過。有人會問編譯器不是對此有對應的優化措施麼?對,公共子表示式優化是可以解決一些這個問題。不過實測發現如果迴圈體內是呼叫的某個函式,即使這個函式是沒有side effect的,編譯器也無法針對這種情況進行優化。(我是用gcc 3.4.5測試的,不排除更高版本的gcc或者其他編譯器可以針對這種情況進行優化)

對於問題2,我遇到的情況是:服務**中定義了乙個const變數,假設叫做max_x,處理請求是,會計算乙個pow(max_x)用作過濾閾值,而效能分析發現,這個pow操作佔了整體系統cpu占用的10%左右。對於這個問題,我的優化方式很簡單,直接計算定義乙個max_x_pow變數用作過濾即可。**修改2行,效能提公升10%。

空間換時間

這其實是老生常談、在大學裡就經常提到的問題了。

不過第一次深有體會的應用卻是在前段時間剛遇到。簡單來說是這樣乙個應用場景:系統內有乙份詞表和乙份非法詞表,原來的處理邏輯是根據請求中的資料查詢到對應的詞(很多),然後用非法詞表過濾掉其中非法的部分。對系統做效能分析發現,依次判斷查詢出來的詞是否在非法詞表中的操作比較耗效能,能佔整體系統消耗cpu的15-20%。後來的優化手段其實也不複雜,就是服務啟動載入詞表和非法詞表的時候,再生成一張合法詞表,請求再來的時候,直接在合法詞表中查到結果即可。不直接用合法詞表代替原來那份總的詞表的原因是,總的詞表還是其他用途。

內聯頻繁呼叫的短小函式

很多人知道這個問題,但是有時候會不太關注,個人揣測可能的原因有:

1. 編譯器會內聯小函式

2. 覺得函式呼叫的消耗也不是特別大

針對1,我的看法是,即使編譯器會內聯小函式,如果把函式定義寫在cpp檔案中並在另外乙個cpp中呼叫該函式,這時編譯器無法內聯該呼叫。

針對2,我的實際經驗是,內聯了乙個每個請求呼叫幾百次的get操作之後,響應時間減少5%左右。

位運算代替乘除法

據說如果是常量的運算的話,編譯器會自動優化選擇最優的計算方式。這裡的常量計算不僅僅是指"4*8"這樣的操作,也可能是"a*b"但編譯的時候編譯器已經可以知道a和b的值。

不過在編譯階段無法知道變數值的時候,將*、/、% 2的冪的運算改為位運算,對效能有時還是蠻有幫助的。

我遇到的一次優化經歷是,將每個請求都會呼叫幾十到數百次不等的函式中乙個*8改為<<3和乙個%8改為&7之後,伺服器的響應時間減少了5%左右。

下面是我實測的一些資料:

%2的次方可以用位運算代替,a%8=a&7(兩倍多效率提公升)

/2的次方可以用移位運算代替,a/8=a>>3(兩倍多效率提公升)

*2的次方可以用移位運算代替,a*8=a<<3(小數值測試效率不明顯,大數值1.5倍效率)

整數次方不要用pow,i*i比pow(i,2)快8倍,i*i*i比pow快40倍

strncpy, snprintf效率對比:目標串》源串 strncpy效率低,源串》目標串 snprintf效率低

編譯優化

gcc編譯的時候,很多服務都是採用o2的優化選項了。不過在使用公共庫的時候,可能沒注意到就使用了乙個沒開任何優化的產出了。我就遇到過至少3個服務因為開啟了tcmalloc庫的o2選項之後效能提公升有10%以上的。

不過開o2優化,有些時候可能會遇到一些非預期的結果,比如這篇帖子提到的memory aliasing的問題。

ABAP 報表程式OpenSQL效能調優

臨上線了,需求還在改,報表也得改,需求滿足了,程式效能又不滿足了,淨折騰啊 可客戶是付錢的上帝,所以程式要調優要改進,不然,要錢不給!sap 系統是經典的三層架構,分為db,應用伺服器,展示層三層 abap程式執行在應用伺服器層,訪問的資料儲存在db,而我們要做的就是把資料從db中取出來,然後送到展...

sql的效能調優

週末火速趕到了客戶現場,週六開始幹活,查詢程式效能差的原因。經過分析,有一些還是我們需要注意總結的。如 com 元件的預設事務隔離級別,在大併發下面,是乙個很大的瓶頸。asp.net對於sp的呼叫,事務是否正常使用,也是乙個很大的瓶頸。但是對於pssdiag分析出來的結果,我還不知道應該怎麼用?只能...

MySQL常見效能調優命令彙總

分析命令 show full processlist 該命令用於檢視當前連線到mysql伺服器的鏈結,以及經行的什麼操作及狀態。如果不加full,只能列出當前100條。show global session status 用於資料庫執行時統計資訊,預設是session。show global ses...