日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

日志库EasyLogging++学习系列(9)—— 性能跟踪功能

發(fā)布時間:2025/3/12 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 日志库EasyLogging++学习系列(9)—— 性能跟踪功能 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

性能跟蹤是 Easylogging++ 其中一個非常顯著的功能,而且使用起來也十分簡單。如果在Windows平臺下使用性能跟蹤的話,其原理是基于 Windows API函數(shù)?GetSystemTimeAsFileTime 實現(xiàn)的。關于API函數(shù)?GetSystemTimeAsFileTime 的精度討論,網(wǎng)上眾說紛紜,根據(jù)我自己的經(jīng)驗,個人認為在毫秒級的話,這個函數(shù)還是可以用的,其精準度和 Sleep 函數(shù)差不多。雖然在?Easylogging++ 的介紹中,該功能可以跟蹤到微妙級別,不過我在實際使用中發(fā)現(xiàn),微妙級別基本都不正確。所以,在Windows平臺下使用性能跟蹤的話,建議只在精度為毫秒級(請根據(jù)實際情況選擇精度)的情況下使用。


如果你想在你的程序中使用性能跟蹤功能,只需要在你想要開始跟蹤的地方加上下面其中一個宏定義即可:


  • TIMED_FUNC(obj-name),主要用來檢測整個函數(shù)的性能,一般放在函數(shù)首行。
  • TIMED_SCOPE(obj-name, block-name),主要用來檢測一定范圍內(nèi)的代碼性能。
  • TIMED_BLOCK(obj-name, block-name),主要用來檢測某一段代碼塊的性能。
其實上面三個宏定義是差不多的,都是使用了?el::base::PerformanceTracker 這個類,而日志的輸出也都是在這個類的析構函數(shù)中控制的,使用時靈活運用就可以了。另外,如果把宏?TIMED_SCOPE(obj-name, block-name) ?中的參數(shù) block-name 用函數(shù)名稱來賦值之后就變成了宏?TIMED_FUNC(obj-name)?,而宏?TIMED_BLOCK(obj-name, block-name) 的定義實際上就是在一個只有一次循環(huán)的 For 循環(huán)中使用了宏?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++編譯器里是編譯不過的,如果直接就調(diào)用宏?TIMED_BLOCK(obj-name, block-name) ,編譯器會提示“error C2332: “struct”: 缺少標記名”之類的錯誤,這種在循環(huán)里定義一個結構體變量的用法在?Visual Studio 里需要用C語言編譯器才能編譯通過。所以,為了能在C++代碼里面能夠直接就調(diào)用宏?TIMED_BLOCK(obj-name, block-name) ,需要對這段代碼做一個小改動,就是把結構體的定義放在循環(huán)外面即可: [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)下面的代碼演示了上述三個實現(xiàn)性能跟蹤的宏定義最簡單的一個用法: [cpp] view plaincopy print?
  • #include?"easylogging++.h"??
  • ??
  • INITIALIZE_EASYLOGGINGPP??
  • ??
  • void?PerformanceTest()???
  • {??
  • ????///?TIMED_FUNC會統(tǒng)計其后續(xù)所有代碼的執(zhí)行時間??
  • ????TIMED_FUNC(timerFunObj);??
  • ????Sleep(100);??
  • ??
  • ????///?TIMED_SCOPE會統(tǒng)計其后續(xù)所有代碼的執(zhí)行時間??
  • ????TIMED_SCOPE(timerScopeObj,?"TIMED_SCOPE_Test");??
  • ????Sleep(100);??
  • ??
  • ????///?TIMED_BLOCK只會統(tǒng)計花括號{}里所有代碼的執(zhí)行時間??
  • ????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會統(tǒng)計其后續(xù)所有代碼的執(zhí)行時間TIMED_FUNC(timerFunObj);Sleep(100);/// TIMED_SCOPE會統(tǒng)計其后續(xù)所有代碼的執(zhí)行時間TIMED_SCOPE(timerScopeObj, "TIMED_SCOPE_Test");Sleep(100);/// TIMED_BLOCK只會統(tǒng)計花括號{}里所有代碼的執(zhí)行時間TIMED_BLOCK(timerBlockObj, "TIMED_BLOCK_Test"){Sleep(100);}Sleep(100); }int main(int argc, char** argv) {PerformanceTest();system("pause");return 0; }從演示代碼中可以看到,三個宏定義中的參數(shù)是可以隨便命名的,其中的參數(shù)?block-name 會在日志中輸出,可以標識某個宏定義的輸出;而參數(shù)?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能夠輸出日志,不然類el::base::PerformanceTracker無法進行析構??
  • ????{??
  • ????????///?TIMED_SCOPE會統(tǒng)計其后續(xù)所有代碼的執(zhí)行時間??
  • ????????TIMED_SCOPE(timerScopeObj,?"TIMED_SCOPE_Test");??
  • ????????Sleep(100);??
  • ??
  • ????????///?PERFORMANCE_CHECKPOINT會統(tǒng)計和TIMED_SCOPE之間所有代碼的執(zhí)行時間??
  • ????????PERFORMANCE_CHECKPOINT(timerScopeObj);??
  • ????????Sleep(100);??
  • ??
  • ????????///?PERFORMANCE_CHECKPOINT_WITH_ID會統(tǒng)計和TIMED_SCOPE之間所有代碼的執(zhí)行時間??
  • ????????///?參數(shù)"mychkpnt"可以標識該宏定義的輸出??
  • ????????///?另外計算出和前面最近一次使用PERFORMANCE_CHECKPOINT之間所有代碼的執(zhí)行時間??
  • ????????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會統(tǒng)計其后續(xù)所有代碼的執(zhí)行時間TIMED_SCOPE(timerScopeObj, "TIMED_SCOPE_Test");Sleep(100);/// PERFORMANCE_CHECKPOINT會統(tǒng)計和TIMED_SCOPE之間所有代碼的執(zhí)行時間PERFORMANCE_CHECKPOINT(timerScopeObj);Sleep(100);/// PERFORMANCE_CHECKPOINT_WITH_ID會統(tǒng)計和TIMED_SCOPE之間所有代碼的執(zhí)行時間/// 參數(shù)"mychkpnt"可以標識該宏定義的輸出/// 另外計算出和前面最近一次使用PERFORMANCE_CHECKPOINT之間所有代碼的執(zhí)行時間PERFORMANCE_CHECKPOINT_WITH_ID(timerScopeObj, "mychkpnt");Sleep(100);}system("pause");return 0; }
    最后,我們還可以利用回調(diào)函數(shù)對性能跟蹤的數(shù)據(jù)進行更有效的處理,下面的例子將會演示如何注冊和注銷回調(diào)函數(shù): [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;?????///?跳過第一次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;??
  • }??
  • #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; } 在使用回調(diào)函數(shù)時,可以使用正常的日志記錄,但是千萬不要再使用性能跟蹤功能,否則將會陷于無限循環(huán)之中!

    總結

    以上是生活随笔為你收集整理的日志库EasyLogging++学习系列(9)—— 性能跟踪功能的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。