日誌元件優化報告
背景
目前專案組日誌元件存在以下問題:
1 日誌檔案每寫一次日誌就開啟關閉一次,存在效能浪費
2 日誌裡面獲取時間需要呼叫localtime、stat,在頻繁呼叫時該函式消耗cpu比較多
3 日誌元件獲取環境變數時未判斷是否成功,如果環境變數沒設定會引起程式core
4 日誌元件在寫日誌時,先初始化區域性變數再判斷日誌級別,可以優化
5 日誌元件裡面有不少處呼叫了sprintf、ReplaceEnvVar和strlen,其實可以避免
6 日誌元件呼叫了大量不可重入函式,在訊號處理函式中寫日誌可能引起core
改造方案
1 增加檔案控制代碼快取與輸出緩衝:
增加一個環境變數HIGH_PERFORMANCE_LOG,預設值為0,不啟用。
當HIGH_PERFORMANCE_LOG為1時,每次開啟日誌後將日誌檔名記錄到g_logInfo. sFullLogName,寫完日誌後暫時不執行fclose。
下次寫日誌時判斷待寫日誌檔名是否與g_logInfo. sFullLogName一致,如果一致則不用重新開啟檔案。否則先將日誌檔案控制代碼關閉再進行開啟日誌檔案的流程。
增加一個環境變數HIGH_PERFORMANCE_LOG_CACHE,預設值為0,不啟用。在HIGH_PERFORMANCE_LOG_CACHE和HIGH_PERFORMANCE_LOG都為1時,寫完日誌後不立即執行重新整理到磁碟(不呼叫fflush)
2 減少非必要系統函式呼叫:
增加函式localtime專案組代替原來的localtime,每次呼叫localtime專案組時先判斷是否與上次呼叫間隔超過1分鐘,如果沒超過則使用上次呼叫localtime返回的結果,如果超過則重新呼叫localtime並且將結果快取起來待下次使用。這樣從原來一分鐘呼叫多次localtime變成一分鐘呼叫一次。
修改函式CommonGetCurrentDate,首次呼叫時記錄當天起始秒數和第二天起始秒數,並且將返回的日誌快取起來。之後每次呼叫判斷時間是否當天範圍內,如果是則直接使用快取返回,否則重複上面流程。樣從原來天呼叫多次localtime變成一天呼叫一次。
增加環境變數LOG_SYSCALL_INTERVAL_TIMES,預設值為100,每寫LOG_SYSCALL_INTERVAL_TIMES次日誌時才執行一次stat(如果快取開啟情況,需要先呼叫fflush將日誌緩衝刷出),不需要每寫一次日誌就檢查一次。注意,由於每100行才檢查一次日誌大小,所以會影響原來50m切換一次日誌檔案的邏輯,不一定精確的在50m時切換,會略大一些。
3 獲取環境變數失敗增加預設值:
改造原來獲取環境變數的程式碼,增加環境變數獲取失敗時使用預設值的邏輯,避免異常情況下導致程式core
4 將判斷日誌級別的邏輯往前提:
進入寫日誌邏輯後,先判斷當前日誌級別是否需要輸出日誌,如果不需要則直接返回。減少非必要的變數初始化操作。
5 使用耗時較小的函式代替耗時長的函式:
日誌元件每次寫日誌時需要使用ReplaceEnvVar函式替換日誌檔案路徑裡面的變數, 現改為在初始化時就呼叫ReplaceEnvVar將檔案路徑變數替換完。後面寫日誌時使用ifNeedReplaceEnvVar判斷是否需要替換變數,如果不需要則不呼叫ReplaceEnvVar
把簡單的sprintf呼叫改為同功能其它函式代替,如將fprintf(fp, "\n")改為fputc ('\n', fp)
判斷字串是否為空,不使用strlen(str) == 0的寫法,改為str[0] == '\0'
非功能測試對比
測試寫20萬行日誌改造前後日誌元件消耗時間
結果如下:
單位ms
不設定環境變數 |
HIGH_PERFORMANCE_LOG=1 |
HIGH_PERFORMANCE_LOG=1 HIGH_PERFORMANCE_LOG_CACHE=1 |
HIGH_PERFORMANCE_LOG=1 HIGH_PERFORMANCE_LOG_CACHE=1 LOG_SYSCALL_INTERVAL_TIMES=1000 |
|
新WrtieLog |
4836.7 |
1001.88 |
453.12 |
454.28 |
新HtLog |
5603.4 |
1355.96 |
1253.96 |
1277.58 |
新DebugLog |
7017.66 |
2600.98 |
2323.88 |
2333.98 |
舊WrtieLog |
7388.92 |
7442.24 |
7196.72 |
7380.76 |
舊HtLog |
7654.6 |
7712.28 |
7498.46 |
7655.82 |
舊DebugLog |
9404.58 |
9431.44 |
8805.68 |
9049.94 |
效率比 |
0.65 |
0.13 |
0.06 |
0.06 |
(50次平均值 測試多次結果偏差不大)
基準測試:
測試日誌記錄:
結論:
改造後日志元件效能有所提高,效率提高約44%~70%
需要注意HIGH_PERFORMANCE_LOG環境變數啟用的情況,如果程式寫日誌過程中日誌檔案被刪除或者移走,程式不能即時發現,仍然會往原來的日誌檔案寫日誌。
需要等到寫滿LOG_SYSCALL_INTERVAL_TIMES行日誌後才會觸發日誌檔案重新開啟關閉功能。
callgrind分析情況
後續優化計劃
如果以後日誌元件再次成為效能瓶頸,可以考慮以下優化:
1 使用linux檔案對映進行日誌檔案讀寫,代替現有的標準庫函式。這樣可以減少資料拷貝。
2 寫日誌時使用流壓縮,邊寫日誌邊壓縮,減少磁碟消耗。
3 實在不行考慮非同步落盤,先將日誌資料寫到共享記憶體,由專用的日誌程序寫到日誌檔案。