在C++中,經常需要通過計時來統計性能信息,通過統計的耗時信息,來分析性能瓶頸,通常情況下,可能毫秒級別的時間統計就足夠用了,但是在毫厘必爭的性能熱點的地方,毫秒級別的統計還是不夠的,這種情況下,就需要至少微秒級別的統計信息,甚至要精確到CPU的指令周期級別。下面來重點說一下毫秒級的計時統計信息。
在Windows平台上,用來統計微秒級別耗時信息,需要用到兩個Windows API:
BOOL WINAPI QueryPerformanceFrequency( _Out_ LARGE_INTEGER *lpFrequency ); BOOL WINAPI QueryPerformanceCounter( _Out_ LARGE_INTEGER *lpPerformanceCount );
QueryPerformanceFrequency用於獲取性能計數的頻率,每秒多少次,
QueryPerformanceCounter用於獲取當前性能計數的值,
有了這兩個API,我們就可以用來統計耗時了,思路如下:
秒級耗時 = (結束性能計數值 - 開始性能計數值) / 性能計數頻率 微秒耗時 = (結束性能計數值 - 開始性能計數值)* 1000000 / 性能計數頻率
LARGE_INTEGER freq_; QueryPerformanceFrequency(&freq_); LARGE_INTEGER begin_time; LARGE_INTEGER end_time; QueryPerformanceCounter(&begin_time); Sleep(100); QueryPerformanceCounter(&end_time); double ns_time = (end_time.QuadPart - begin_time.QuadPart) * 1000000.0 / freq_.QuadPart;
雖然上面已經實現了微秒精度計時,但是由於每次調用API時,都要定義變量等,使用起來肯定會有很多重復或者類似的代碼,那麼為了避免這種情況,對此實現進行了封裝,如下:
class stop_watch { public: stop_watch() : elapsed_(0) { QueryPerformanceFrequency(&freq_); } ~stop_watch(){} public: void start() { QueryPerformanceCounter(&begin_time_); } void stop() { LARGE_INTEGER end_time; QueryPerformanceCounter(&end_time); elapsed_ += (end_time.QuadPart - begin_time_.QuadPart) * 1000000 / freq_.QuadPart; } void restart() { elapsed_ = 0; start(); } //微秒 double elapsed() { return static_cast<double>(elapsed_); } //毫秒 double elapsed_ms() { return elapsed_ / 1000.0; } //秒 double elapsed_second() { return elapsed_ / 1000000.0; } private: LARGE_INTEGER freq_; LARGE_INTEGER begin_time_; long long elapsed_; };
那麼,如何使用此封裝的類呢,來看一下調用的例子:
stop_watch watch; watch.start(); Sleep(100); watch.stop(); cout << watch.elapsed() << " ns" << endl;
看看調用是不是更方便了呢,是不是有點似曾相識的感覺,對,沒錯,你猜對了。。。
QueryPerformanceFrequency
QueryPerformanceCounter