C++ 后台程序实时性能监控
面對的問題:
做后臺程序經(jīng)常會被問一句話,你的程序能撐多少人。一般官方一點的回答是這個得根據(jù)實際情況而定。實際上后臺程序的性能是可以被量化的。我們開發(fā)的每一個服務器程序,對性能都非常有底,以為我們有數(shù)據(jù)。So,能撐多少人不少隨便猜的,讓數(shù)據(jù)報表來說話。
另外一種情況經(jīng)常發(fā)生在開發(fā)人員之中,甲乙丙一起討論接口實現(xiàn),經(jīng)常會說這么實現(xiàn)效率太低,那么實現(xiàn)效率才高等。實際上,效率高低都是相對而言的。一個函數(shù)1ms執(zhí)行完畢夠快嗎?看起來挺快,若某接口需要此函數(shù)100次循環(huán),那么情況就不是很樂觀了。但是若此接口又是十天半個月才會被觸發(fā)一次,似乎事情又變的不是很嚴重。說到這里想起《unix編程藝術》上關于性能優(yōu)化的總結:
- 最有效的優(yōu)化往往是優(yōu)化之外的,如清晰干凈的設計
- 最有效的優(yōu)化就是不優(yōu)化,摩爾定律會為你優(yōu)化
- 如果確定要優(yōu)化,必須找到真正的瓶頸
還有一種跟性能有關的情況是,后臺程序經(jīng)常有很多組件組成。比如在運行期發(fā)生接口調(diào)用性能下降的情況,必須知道是那些組件性能下降引起的。如果可以實時的知道所有接口的性能數(shù)據(jù),以上的問題都可迎刃而解。
總結如下原因,必須開啟實時性能監(jiān)控:
- 我們需要知道系統(tǒng)的吞吐量,以此參數(shù)做部署等。
- 實時了解各個系統(tǒng)組件的性能,某組件發(fā)生故障,可以及時發(fā)現(xiàn)
- 獲得程序接口調(diào)用熱點,調(diào)用多且慢的接口才需要優(yōu)化
解決方案:
后臺程序開發(fā)一個專門統(tǒng)計性能的組件,其需要有如下功能:
- 可以匯總性能數(shù)據(jù),如定時將1小時內(nèi)說有接口調(diào)用開銷、次數(shù)等數(shù)據(jù)匯總到文件
- 可以非常方便的與邏輯層接口集成,比如在現(xiàn)有接口增加一行代碼即可
- 直觀的報表,性能數(shù)據(jù)寫入文件必須按照通用的格式,方便工具分析數(shù)據(jù),生成報表
性能監(jiān)控組件
我實現(xiàn)了一個性能組件performance_daemon_t。接口如下:
//! 性能監(jiān)控 class performance_daemon_t { public:struct perf_tool_t{perf_tool_t(const char* mod_):mod(mod_){gettimeofday(&tm, NULL);}~perf_tool_t(){struct timeval now;gettimeofday(&now, NULL);long cost = (now.tv_sec - tm.tv_sec)*1000000 + (now.tv_usec - tm.tv_usec);singleton_t<performance_daemon_t>::instance().post(mod, cost);}const char* mod;struct timeval tm;}; public:performance_daemon_t();~performance_daemon_t();//! 啟動線程,創(chuàng)建文件int start(const string& path_, int seconds_);//! 關閉線程int stop();//! 增加性能監(jiān)控數(shù)據(jù)void post(const string& mod_, long us);perf_tool_t 是工具類,構造和析構自動調(diào)用兩次gettimeofday獲取函數(shù)調(diào)用開銷,例外有輔助宏定義如下:
#define AUTO_PERF() performance_daemon_t::perf_tool_t __tmp__(__FUNCTION__)#define PERF(m) performance_daemon_t::perf_tool_t __tmp__(m)使用示例:
void foo() {AUTO_PERF();//! TODO ----- }int main(int argc, char* argv[]) {singleton_t<performance_daemon_t>::instance().start("perf.txt", 5); foo(); }performance_daemon_t 每隔5秒將性能統(tǒng)計數(shù)據(jù)輸出到perf.txt, perf.txt的內(nèi)容是CVS文件格式。
報表工具:
perf.txt 文件內(nèi)容還不夠直觀,示例內(nèi)容如下:
time,mod,max_cost[us],min_cost[us],per_cost[us],request_per_second,exe_times 20120606-17:01:41,dumy,515,174,254,3937,390 20120606-17:01:41,foo,5924,4,506,1976,1030 20120606-17:01:41,test,304,8,243,4115,185 time,mod,max_cost[us],min_cost[us],per_cost[us],request_per_second,exe_times 20120606-17:11:41,dumy,1086,222,280,5571,312 20120606-17:11:41,foo,5707,194,503,1988,770 20120606-17:11:41,test,807,8,265,3773,142 time,mod,max_cost[us],min_cost[us],per_cost[us],request_per_second,exe_times 20120606-17:21:41,dumy,1086,222,680,2571,512 20120606-17:21:41,foo,5707,194,403,1388,470 20120606-17:21:41,test,807,8,265,4773,442為生成足夠友好、直觀的報表,我實現(xiàn)了一個WEB報表頁面,http://ffown.sinaapp.com/perf/, 將perf.txt 內(nèi)容直接粘貼到web 頁面,點擊轉(zhuǎn)換輸出如下報表:
各個接口性能監(jiān)控-折線圖:
此圖顯示了三個接口隨時間順序的走勢,可以非常清楚foo、test、dumy三個接口那個時間性能高,哪個時間性能低,一目了然。
接口熱點分布圖:
顯示三個接口隨時間調(diào)用次數(shù)走勢,可以很清楚顯示哪個時間段是高峰期。大餅圖顯示了哪個接口是熱點接口,很明顯,foo 接口調(diào)用次數(shù)最多,優(yōu)化當優(yōu)先優(yōu)化foo。
組件實現(xiàn)淺析:
post 接口:
程序把接口調(diào)用開銷投遞到性能組件任務隊列中,保證了對接口性能影響最小。
timer定時回調(diào):
timer_service_t 是我用epoll 實現(xiàn)的定時器,主要實現(xiàn)如下:
void timer_service_t::run() {struct epoll_event ev_set[64];//! interupt();struct timeval tv;do{::epoll_wait(m_efd, ev_set, 64, m_min_timeout);if (false == m_runing)//! cancel {break;}gettimeofday(&tv, NULL);long cur_ms = tv.tv_sec*1000 + tv.tv_usec / 1000;process_timer_callback(cur_ms);}while (true) ; }process_timer_callback 中檢測鏈表內(nèi)所有的定時任務,若超時,觸發(fā)回調(diào)函數(shù)。
備注:
有人可能當心AUTO_PERF(); 會影響接口性能,其實其平均開銷大約為1us?
代碼實現(xiàn):
https://ffown.googlecode.com/svn/trunk/example/ff_performance
WEB 報表生成工具:
http://ffown.sinaapp.com/perf/
?文檔:
http://ffown.sinaapp.com/perf/perf.pdf
總結
以上是生活随笔為你收集整理的C++ 后台程序实时性能监控的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++多进程并发框架FFLIB
- 下一篇: select case语句 is和to用