生活随笔
收集整理的這篇文章主要介紹了
日志库EasyLogging++学习系列(9)—— 性能跟踪功能
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
性能跟蹤是 Easylogging++ 其中一個非常顯著的功能,而且使用起來也十分簡單。如果在Windows平臺下使用性能跟蹤的話,其原理是基于 Windows API函數?GetSystemTimeAsFileTime 實現的。關于API函數?GetSystemTimeAsFileTime 的精度討論,網上眾說紛紜,根據我自己的經驗,個人認為在毫秒級的話,這個函數還是可以用的,其精準度和 Sleep 函數差不多。雖然在?Easylogging++ 的介紹中,該功能可以跟蹤到微妙級別,不過我在實際使用中發現,微妙級別基本都不正確。所以,在Windows平臺下使用性能跟蹤的話,建議只在精度為毫秒級(請根據實際情況選擇精度)的情況下使用。
如果你想在你的程序中使用性能跟蹤功能,只需要在你想要開始跟蹤的地方加上下面其中一個宏定義即可:
- TIMED_FUNC(obj-name),主要用來檢測整個函數的性能,一般放在函數首行。
- TIMED_SCOPE(obj-name, block-name),主要用來檢測一定范圍內的代碼性能。
- TIMED_BLOCK(obj-name, block-name),主要用來檢測某一段代碼塊的性能。
其實上面三個宏定義是差不多的,都是使用了?el::base::PerformanceTracker 這個類,而日志的輸出也都是在這個類的析構函數中控制的,使用時靈活運用就可以了。另外,如果把宏?TIMED_SCOPE(obj-name, block-name) ?中的參數 block-name 用函數名稱來賦值之后就變成了宏?TIMED_FUNC(obj-name)?,而宏?TIMED_BLOCK(obj-name, block-name) 的定義實際上就是在一個只有一次循環的 For 循環中使用了宏?TIMED_SCOPE(obj-name, block-name) 的定義。這里特別說一下?TIMED_BLOCK(obj-name, block-name) 這個宏定義,下面是其源碼:
[cpp] view plaincopy print?
#define?TIMED_BLOCK(obj,?blockName)?for?(struct?{?int?i;?el::base::PerformanceTracker?timer;?}?obj?=?{?0,?\??????el::base::PerformanceTracker(blockName,?ELPP_MIN_UNIT)?};?obj.i?<?1;?++obj.i)?? #define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::PerformanceTracker timer; } obj = { 0, \el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i)這段代碼中在 Visual Studio 的C++編譯器里是編譯不過的,如果直接就調用宏?
TIMED_BLOCK(obj-name, block-name) ,編譯器會提示“error C2332: “struct”: 缺少標記名”之類的錯誤,這種在循環里定義一個結構體變量的用法在?Visual Studio 里需要用C語言編譯器才能編譯通過。所以,為了能在C++代碼里面能夠直接就調用宏?TIMED_BLOCK(obj-name, block-name) ,需要對這段代碼做一個小改動,就是把結構體的定義放在循環外面即可:
[cpp] view plaincopy print?
typedef?struct?st_PerformanceTracker{??????int?i;???????el::base::PerformanceTracker?timer;??}?TIME_BLOCK_PERFORMANCE_TRACKER;??#define?TIMED_BLOCK(obj,?blockName)?for?(TIME_BLOCK_PERFORMANCE_TRACKER?obj?=?{?0,?\??????el::base::PerformanceTracker(blockName,?ELPP_MIN_UNIT)?};?obj.i?<?1;?++obj.i)?? typedef struct st_PerformanceTracker{int i; el::base::PerformanceTracker timer;
} TIME_BLOCK_PERFORMANCE_TRACKER;
#define TIMED_BLOCK(obj, blockName) for (TIME_BLOCK_PERFORMANCE_TRACKER obj = { 0, \el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i)下面的代碼演示了
上述三個實現性能跟蹤的宏定義最簡單的一個用法:
[cpp] view plaincopy print?
#include?"easylogging++.h"????INITIALIZE_EASYLOGGINGPP????void?PerformanceTest()???{????????????TIMED_FUNC(timerFunObj);??????Sleep(100);??????????????TIMED_SCOPE(timerScopeObj,?"TIMED_SCOPE_Test");??????Sleep(100);??????????????TIMED_BLOCK(timerBlockObj,?"TIMED_BLOCK_Test")??????{??????????Sleep(100);??????}????????Sleep(100);??}????int?main(int?argc,?char**?argv)??{??????PerformanceTest();????????system("pause");??????return?0;??}?? #include "easylogging++.h"INITIALIZE_EASYLOGGINGPPvoid PerformanceTest()
{/// TIMED_FUNC會統計其后續所有代碼的執行時間TIMED_FUNC(timerFunObj);Sleep(100);/// TIMED_SCOPE會統計其后續所有代碼的執行時間TIMED_SCOPE(timerScopeObj, "TIMED_SCOPE_Test");Sleep(100);/// TIMED_BLOCK只會統計花括號{}里所有代碼的執行時間TIMED_BLOCK(timerBlockObj, "TIMED_BLOCK_Test"){Sleep(100);}Sleep(100);
}int main(int argc, char** argv)
{PerformanceTest();system("pause");return 0;
}從演示代碼中可以看到,三個宏定義中的參數是可以隨便命名的,其中的參數?
block-name 會在日志中輸出,可以標識某個宏定義的輸出;而參數?obj-name 并不會在日志中輸出,它主要是用在下面兩個宏定義中:
- PERFORMANCE_CHECKPOINT(timed-block-obj-name)
- PERFORMANCE_CHECKPOINT_WITH_ID(timed-block-obj-name, id)
這兩個宏定義的用法和功能是一樣的,只是后者多了一個標識。下面的代碼演示了這兩個宏定義的用法:
[cpp] view plaincopy print?
#include?"easylogging++.h"????INITIALIZE_EASYLOGGINGPP????int?main(int?argc,?char**?argv)??{????????????{????????????????????TIMED_SCOPE(timerScopeObj,?"TIMED_SCOPE_Test");??????????Sleep(100);??????????????????????PERFORMANCE_CHECKPOINT(timerScopeObj);??????????Sleep(100);??????????????????????????????????????????PERFORMANCE_CHECKPOINT_WITH_ID(timerScopeObj,?"mychkpnt");??????????Sleep(100);??????}????????system("pause");??????return?0;??}?? #include "easylogging++.h"INITIALIZE_EASYLOGGINGPPint main(int argc, char** argv)
{/// 用花括號是為了使宏TIMED_SCOPE能夠輸出日志,不然類el::base::PerformanceTracker無法進行析構{/// TIMED_SCOPE會統計其后續所有代碼的執行時間TIMED_SCOPE(timerScopeObj, "TIMED_SCOPE_Test");Sleep(100);/// PERFORMANCE_CHECKPOINT會統計和TIMED_SCOPE之間所有代碼的執行時間PERFORMANCE_CHECKPOINT(timerScopeObj);Sleep(100);/// PERFORMANCE_CHECKPOINT_WITH_ID會統計和TIMED_SCOPE之間所有代碼的執行時間/// 參數"mychkpnt"可以標識該宏定義的輸出/// 另外計算出和前面最近一次使用PERFORMANCE_CHECKPOINT之間所有代碼的執行時間PERFORMANCE_CHECKPOINT_WITH_ID(timerScopeObj, "mychkpnt");Sleep(100);}system("pause");return 0;
}
最后,我們還可以利用回調函數對性能跟蹤的數據進行更有效的處理,下面的例子將會演示如何注冊和注銷回調函數:
[cpp] view plaincopy print?
#include?"easylogging++.h"????INITIALIZE_EASYLOGGINGPP????class?MyPerformanceTrackingOutput?:?public?el::PerformanceTrackingCallback???{??public:??????MyPerformanceTrackingOutput()???????{????????????????????el::PerformanceTrackingCallback*?defaultCallback?=??????????????el::Helpers::performanceTrackingCallback<el::base::DefaultPerformanceTrackingCallback>("DefaultPerformanceTrackingCallback");??????????defaultCallback->setEnabled(false);??????}??????virtual?~MyPerformanceTrackingOutput()???????{????????????????????el::PerformanceTrackingCallback*?defaultCallback?=??????????????el::Helpers::performanceTrackingCallback<el::base::DefaultPerformanceTrackingCallback>("DefaultPerformanceTrackingCallback");??????????defaultCallback->setEnabled(true);??????}????protected:??????????????void?handle(const?el::PerformanceTrackingData*?data)???????{??????????if?(data->firstCheckpoint())??????????{???????????????return;???????????????}?????????????el::base::type::stringstream_t?ss;??????????ss?<<?data->blockName()->c_str()?<<?"?took?"?<<?*data->formattedTimeTaken()?<<?"?to?run";????????????if?(data->dataType()?==?el::PerformanceTrackingData::DataType::Checkpoint)???????????{??????????????ss?<<?"?[CHECKPOINT?ONLY]?";??????????}??????????CLOG(INFO,?data->loggerId().c_str())?<<?ss.str();??????}??};????int?main(int?argc,?char**?argv)???{??????{??????????TIMED_SCOPE(mainBlock,?"main");???????????????Sleep(100);????????????el::Helpers::installPerformanceTrackingCallback<MyPerformanceTrackingOutput>("MyPerformanceTrackingOutput");????????????TIMED_SCOPE(timer,?"myblock");????????????????Sleep(100);????????????PERFORMANCE_CHECKPOINT(timer);????????????????Sleep(100);????????????PERFORMANCE_CHECKPOINT(timer);????????????????Sleep(100);????????????????el::Helpers::uninstallPerformanceTrackingCallback<MyPerformanceTrackingOutput>("MyPerformanceTrackingOutput");??????}????????????system("pause");??????return?0;??}?? #include "easylogging++.h"INITIALIZE_EASYLOGGINGPPclass MyPerformanceTrackingOutput : public el::PerformanceTrackingCallback
{
public:MyPerformanceTrackingOutput() {/// 禁用默認的性能日志輸出格式el::PerformanceTrackingCallback* defaultCallback =el::Helpers::performanceTrackingCallback<el::base::DefaultPerformanceTrackingCallback>("DefaultPerformanceTrackingCallback");defaultCallback->setEnabled(false);}virtual ~MyPerformanceTrackingOutput() {/// 恢復默認的性能日志輸出格式el::PerformanceTrackingCallback* defaultCallback =el::Helpers::performanceTrackingCallback<el::base::DefaultPerformanceTrackingCallback>("DefaultPerformanceTrackingCallback");defaultCallback->setEnabled(true);}protected:/// 自定義性能日志輸出格式void handle(const el::PerformanceTrackingData* data) {if (data->firstCheckpoint()){ return; /// 跳過第一次PERFORMANCE_CHECKPOINT} el::base::type::stringstream_t ss;ss << data->blockName()->c_str() << " took " << *data->formattedTimeTaken() << " to run";if (data->dataType() == el::PerformanceTrackingData::DataType::Checkpoint) {ss << " [CHECKPOINT ONLY] ";}CLOG(INFO, data->loggerId().c_str()) << ss.str();}
};int main(int argc, char** argv)
{{TIMED_SCOPE(mainBlock, "main"); /// 使用默認的性能日志格式輸出日志Sleep(100);el::Helpers::installPerformanceTrackingCallback<MyPerformanceTrackingOutput>("MyPerformanceTrackingOutput");TIMED_SCOPE(timer, "myblock"); /// 使用自定義的性能日志格式輸出日志Sleep(100);PERFORMANCE_CHECKPOINT(timer); /// 第一次使用PERFORMANCE_CHECKPOINT,會被忽略Sleep(100);PERFORMANCE_CHECKPOINT(timer); /// 使用自定義的性能日志格式輸出日志Sleep(100);el::Helpers::uninstallPerformanceTrackingCallback<MyPerformanceTrackingOutput>("MyPerformanceTrackingOutput");}system("pause");return 0;
}
在使用回調函數時,可以使用正常的日志記錄,但是千萬不要再使用性能跟蹤功能,否則將會陷于無限循環之中!
總結
以上是生活随笔為你收集整理的日志库EasyLogging++学习系列(9)—— 性能跟踪功能的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。