日誌組件優化報告
背景
目前項目組日誌組件存在以下問題:
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 實在不行考慮異步落盤,先將日誌數據寫到共享內存,由專用的日誌進程寫到日誌文件。
日誌組件優化報告