C++算法計時器的實現(xiàn)示例
有時為了檢測和比較算法效率和復(fù)雜度,需要一個計時器,而這個計時器往往需要精確到毫秒ms、微秒μs甚至納秒ns,不太常用的庫或api就不放上來了。
1.毫秒級精度
1.1 CLOCKS_PER_SEC
在頭文件time.h或ctime中,clock()函數(shù)返回從“開啟這個程序進(jìn)程”到“程序中調(diào)用clock()函數(shù)”時之間的CPU時鐘計時單元(clock tick)數(shù),在MSDN中稱之為掛鐘時間(wal-clock),常量CLOCKS_PER_SEC,它用來表示一秒鐘會有多少個時鐘計時單元,精確到毫秒,其使用方法如下:
精華代碼:
#include <iostream> #include<vector> #include <algorithm> #include <ctime> using namespace std; int main() { clock_t begin, end; begin = clock(); for (int i = 1; i <= 100; ++i) { } end = clock(); cout << "100次循環(huán)所用時間:" << double(end - begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl; return 0; }
示例為檢測二叉堆不同輸入一個一個插入所用時間(不能直接跑):
#include <iostream> #include<vector> #include <algorithm> #include <ctime> using namespace std; int main() { int num, mode; cout << "輸入大小和模式,其中模式1為正序,2為倒序,3位隨機(jī)" << endl; cout << "示例:1000 2" << endl; cin >> num >> mode;//輸入大小和模式,其中模式1為正序,2為倒序,3位隨機(jī) BinaryHeap<int> heap1,heap2; clock_t begin, end; switch (mode) { case 1://正序 begin = clock(); for (int i = 1; i <= num; ++i) { heap1.insert(i); } end = clock(); cout << "一個一個正序插入所用時間:" << double(end - begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl; break; case 2://倒序 begin = clock(); for (int i = num; i >= 1; --i) { heap1.insert(i); } end = clock(); cout << "一個一個倒序插入所用時間:" << double(end - begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl; break; case 3://正倒序交叉模擬隨機(jī) begin = clock(); for (int i = 1; i<num/2; ++i) { heap1.insert(i); heap1.insert(num - i); } end = clock(); cout << "一個一個隨機(jī)插入所用時間:" << double(end - begin) / CLOCKS_PER_SEC * 1000 << "ms" << endl; break; default: break; } return 0; }
1.2 GetTickCount()函數(shù) (Windows API)
GetTickCount返回(retrieve)從操作系統(tǒng)啟動所經(jīng)過(elapsed)的毫秒數(shù),它的返回值是DWORD。
#include <stdio.h> #include <windows.h> #include<iostream> #pragma comment(lib, "winmm.lib") //告訴編譯器要導(dǎo)入winmm庫,有時候可刪 int main() { DWORD t1, t2; t1 = GetTickCount(); for(int i=1;i<=10000;++i) { }//do something t2 = GetTickCount(); //printf("Use Time:%f\n", (t2 - t1) * 1.0); cout<<"Use Time:"<<(double)(t2-t1)<<"ms"<<endl; return 0; }
1.3 timeGetTime()函數(shù)(Windows API)
以毫秒計的系統(tǒng)時間,該時間為從系統(tǒng)開啟算起所經(jīng)過的時間。在使用timeGetTime之前應(yīng)先包含頭文件#include <Mmsystem.h>或#include <Windows.h>并在project->settings->link->Object/library modules中添加winmm.lib。也可以在文件頭部添加 #pragma comment( lib,"winmm.lib" )。
備注:命令行:#pragma comment( lib,"xxx.lib" )時預(yù)編譯處理指令,讓vc將winmm.lib添加到工程中去進(jìn)行編譯。
//#include<stdio.h> #include<windows.h> #include<iostream> #pragma comment( lib,"winmm.lib" ) int main() { DWORD t1, t2; t1 = timeGetTime(); foo();//do something t2 = timeGetTime(); //printf("Use Time:%f\n", (t2 - t1)*1.0 / 1000); cout<<"Use Time:"<<(double)(t2-t1)<<"ms"<<endl; return 0; }
該函數(shù)的時間精度是五毫秒或更大一些,這取決于機(jī)器的性能??捎胻imeBeginPeriod和timeEndPeriod函數(shù)提高timeGetTime函數(shù)的精度。如果使用了,連續(xù)調(diào)用timeGetTime函數(shù),一系列返回值的差異由timeBeginPeriod和timeEndPeriod決定。也可以用timeGetTime實現(xiàn)延時功能Delay
void Delay(DWORD delayTime) { DWORD delayTimeBegin; DWORD delayTimeEnd; delayTimeBegin=timeGetTime(); do { delayTimeEnd=timeGetTime(); }while((delayTimeEnd-delayTimeBegin)<delayTime) }
1.4 timeval結(jié)構(gòu)體(Linux)
timeval結(jié)構(gòu)體
#include <sys/time.h> #include <iostream> #include <time.h> double get_wall_time() { struct timeval time ; if (gettimeofday(&time,NULL)){ return 0; } return (double)time.tv_sec + (double)time.tv_usec * .000001; } int main() { unsigned int t = 0; double start_time = get_wall_time() while(t++<10e+6); double end_time = get_wall_time() std::cout<<"循環(huán)耗時為:"<<end_time-start_time<<"ms"; return 0; }
2.微秒級精度
QueryPerformanceCounter()函數(shù)和QueryPerformanceFrequency()函數(shù)(Windows API)
QueryPerformanceFrequency()函數(shù)返回高精確度性能計數(shù)器的值,它可以以微妙為單位計時,但是QueryPerformanceCounter()確切的精確計時的最小單位是與系統(tǒng)有關(guān)的,所以,必須要查詢系統(tǒng)以得到QueryPerformanceCounter()返回的嘀噠聲的頻率。QueryPerformanceFrequency()提供了這個頻率值,返回每秒嘀噠聲的個數(shù)。
//#include<stdio.h> #include<iostream> #include<windows.h> #pragma comment( lib,"winmm.lib" ) int main() { LARGE_INTEGER t1, t2, tc; QueryPerformanceFrequency(&tc); QueryPerformanceCounter(&t1); foo();//do something QueryPerformanceCounter(&t2); //printf("Use Time:%f\n", (t2.QuadPart - t1.QuadPart)*1.0 / tc.QuadPart); cout << "Use Time:" << (double)((t2.QuadPart - t1.QuadPart) * 1000000.0 / tc.QuadPart) << "μs" << endl; return 0; }
封裝好的易于調(diào)用的代碼:
//MyTimer.h// #ifndef __MyTimer_H__ #define __MyTimer_H__ #include <windows.h> class MyTimer { private: int _freq; LARGE_INTEGER _begin; LARGE_INTEGER _end; public: long costTime; // 花費(fèi)的時間(精確到微秒) public: MyTimer() { LARGE_INTEGER tmp; QueryPerformanceFrequency(&tmp);//QueryPerformanceFrequency()作用:返回硬件支持的高精度計數(shù)器的頻率。 _freq = tmp.QuadPart; costTime = 0; } void Start() // 開始計時 { QueryPerformanceCounter(&_begin);//獲得初始值 } void End() // 結(jié)束計時 { QueryPerformanceCounter(&_end);//獲得終止值 costTime = (long)((_end.QuadPart - _begin.QuadPart) * 1000000 / _freq); } void Reset() // 計時清0 { costTime = 0; } }; #endif //main.cpp #include "MyTimer.h" #include <iostream> int main() { MyTimer timer; unsigned int t = 0; timer.Start(); while (t++ < 10e+5); timer.End(); std::cout << "耗時為:" << timer.costTime << "us"; return 0 ; }
3.納秒級精度
要先獲取CPU頻率。
在Intel Pentium以上級別的CPU中,有一個稱為“時間戳(Time Stamp)”的部件,它以64位無符號整型數(shù)的格式,記錄了自CPU上電以來所經(jīng)過的時鐘周期數(shù)。由于目前的CPU主頻都非常高,因此這個部件可以達(dá)到納秒級的計時精度。這個精確性是上述幾種方法所無法比擬的.在Pentium以上的CPU中,提供了一條機(jī)器指令RDTSC(Read Time Stamp Counter)來讀取這個時間戳的數(shù)字,并將其保存在EDX:EAX寄存器對中。由于EDX:EAX寄存器對恰好是Win32平臺下C++語言保存函數(shù)返回值的寄存器,所以我們可以把這條指令看成是一個普通的函數(shù)調(diào)用,因為RDTSC不被C++的內(nèi)嵌匯編器直接支持,所以我們要用_emit偽指令直接嵌入該指令的機(jī)器碼形式0X0F、0X31。
inline unsigned __int64 GetCycleCount() { __asm { _emit 0x0F; _emit 0x31; } } void test() { unsigned long t1,t2; t1 = (unsigned long)GetCycleCount(); foo();//dosomething t2 = (unsigned long)GetCycleCount(); printf("Use Time:%f\n",(t2 - t1)*1.0/FREQUENCY); //FREQUENCY指CPU的頻率 }
下面為獲取CPU精度的代碼
#include<Windows.h> LONGLONG GetFrequency(DWORD sleepTime) //獲取CPU主頻 { DWORD low1 = 0, high1 = 0, low2 = 0, high2 = 0; LARGE_INTEGER fq, st, ed; /*在定時前應(yīng)該先調(diào)用QueryPerformanceFrequency()函數(shù)獲得機(jī)器內(nèi)部計時器的時鐘頻率。接著在 需要嚴(yán)格計時的事件發(fā)生前和發(fā)生之后分別調(diào)用QueryPerformanceCounter(),利用兩次獲得的技術(shù) 之差和時鐘的頻率,就可以計算出時間經(jīng)歷的精確時間。*/ ::QueryPerformanceFrequency(&fq); //精確計時(返回硬件支持的高精度計數(shù)器的頻率) ::QueryPerformanceCounter(&st); //獲得起始時間 __asm { //獲得當(dāng)前CPU的時間數(shù) rdtsc mov low1, eax mov high1, edx } ::Sleep(sleepTime); //將線程掛起片刻 ::QueryPerformanceCounter(&ed); //獲得結(jié)束時間 __asm { rdtsc //讀取CPU的時間戳計數(shù)器 mov low2, eax mov high2, edx } //將CPU得時間周期數(shù)轉(zhuǎn)化成64位整數(shù) LONGLONG begin = (LONGLONG)high1 << 32 | low1; LONGLONG end = (LONGLONG)high2 << 32 | low2; //將兩次獲得的CPU時間周期數(shù)除以間隔時間,即得到CPU的頻率 //由于windows的Sleep函數(shù)有大約15毫秒的誤差,故以windows的精確計時為準(zhǔn) return (end - begin) * fq.QuadPart / (ed.QuadPart - st.QuadPart); }
4.利用chrono的各精度集成版(本質(zhì)微秒)
4.1 chrono庫介紹
函數(shù)原型:
template <class Clock, class Duration = typename Clock::duration> class time_point;
std::chrono::time_point 表示一個具體時間
第一個模板參數(shù)Clock用來指定所要使用的時鐘,在標(biāo)準(zhǔn)庫中有三種時鐘,分別為:
- system_clock:當(dāng)前系統(tǒng)范圍(即對各進(jìn)程都一致)的一個實時的日歷時鐘(wallclock)
- steady_clock:當(dāng)前系統(tǒng)實現(xiàn)的一個維定時鐘,該時鐘的每個時間嘀嗒單位是均勻的(即長度相等)。
- high_resolution_clock:當(dāng)前系統(tǒng)實現(xiàn)的一個高分辨率時鐘。
第二個模板函數(shù)參數(shù)用來表示時間的計量單位(特化的std::chrono::duration<> )
時間點(diǎn)都有一個時間戳,即時間原點(diǎn)。chrono庫中采用的是Unix的時間戳1970年1月1日 00:00。所以time_point也就是距離時間戳(epoch)的時間長度(duration)。
4.2 代碼示例
#include <iostream> #include <chrono> using namespace std; using namespace std::chrono; class TimerClock { public: TimerClock() { update(); } ~TimerClock() { } void update() { _start = high_resolution_clock::now(); } //獲取秒 double getTimerSecond() { return getTimerMicroSec() * 0.000001; } //獲取毫秒 double getTimerMilliSec() { return getTimerMicroSec()*0.001; } //獲取微妙 long long getTimerMicroSec() { //當(dāng)前時鐘減去開始時鐘的count return duration_cast<microseconds>(high_resolution_clock::now() - _start).count(); } private: time_point<high_resolution_clock>_start; }; //測試的主函數(shù) int main() { TimerClock TC; int sum = 0; TC.update(); for (int i = 0; i > 100000; i++) { sum++; } cout << "cost time:" << TC.getTimerMilliSec() <<"ms"<< endl; cout << "cost time:" << TC.getTimerMicroSec() << "us" << endl; return 0; }
5.秒級精度
單純以備不時之需,沒人用吧。
time() 函數(shù)
在頭文件time.h中,time()獲取當(dāng)前的系統(tǒng)時間,只能精確到秒,返回的結(jié)果是一個time_t類型,其使用方法如下:
#include <time.h> #include <stdio.h> int main() { time_t first, second; first=time(NULL); delay(2000); second=time(NULL); printf("The difference is: %f seconds",difftime(second,first)); //調(diào)用difftime求出時間差 return 0; }
到此這篇關(guān)于C++算法計時器的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)C++算法計時器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VC程序在Win32環(huán)境下動態(tài)鏈接庫(DLL)編程原理
這篇文章主要介紹了VC程序在Win32環(huán)境下動態(tài)鏈接庫(DLL)編程原理,包括了dll文件的原理與具體實現(xiàn)過程,對于深入掌握VC程序設(shè)計具有很好的參考借鑒價值,需要的朋友可以參考下2014-10-10C++編寫DLL動態(tài)鏈接庫的步驟與實現(xiàn)方法
這篇文章主要介紹了C++編寫DLL動態(tài)鏈接庫的步驟與實現(xiàn)方法,結(jié)合實例形式分析了C++導(dǎo)出類文件及生成與調(diào)用DLL動態(tài)連接庫的相關(guān)操作技巧,需要的朋友可以參考下2016-08-08C++面試題之?dāng)?shù)a、b的值互換(不使用中間變量)
這篇文章主要介紹了不使用中間變量,C++實現(xiàn)數(shù)a、b的值互相轉(zhuǎn)換操作,感興趣的小伙伴們可以參考一下2016-07-07使用VS2010創(chuàng)建MFC ActiveX工程項目
VS2010開發(fā)ActiveX有兩種方法,分別是MFC和ATL。MFC開過起來比較簡單,但是最終生成的文件比較大,ATL是專門用來開發(fā)ActiveX的,但是相對比較難,必須知道很多原理機(jī)制和API。咱先從MFC開發(fā)ActiveX開始吧。2015-06-06C++ opencv ffmpeg圖片序列化實現(xiàn)代碼解析
這篇文章主要介紹了C++ opencv ffmpeg圖片序列化實現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08