高分辨率计时器
C标准库提供clock()函数,它可以用于计算经过的时间。它是定义在time.h(ctime)中的一个操作系统独立的C函数(兼容于大多数操作系统),不过,它并不提供精确结果,甚至不提供毫秒级精度。
为了检测clock()的精度,在你的系统上尝试下列代码。输出结果显示clock()函数可以检测的最小时钟。
#include <iostream> #include <ctime> using namespace std; int main() { clock_t t1, t2; t1 = t2 = clock(); // 循环直到t2获取不同值 while(t1 == t2) t2 = clock(); // 打印clock()分辨率 cout << (double)(t2 - t1) / CLOCKS_PER_SEC * 1000 << " ms.\n"; return 0; }
因此,我们需要至少1毫米的高分辨率计时器以测量过去时间。值得庆幸的是存在这样的高分辨率计时器函数,不过它们是系统特有的。也就是说,在不同系统,你需要书写不同的代码。Windows提供QueryPerformanceCounter()函数,Unix、Linux与Mac OS X系统拥有定义在sys/time.h中的gettimeofday()。这两个函数都能够测量至少1微秒的区别。
Windows
Windows API提供额外的高分辨率计时器函数,QueryPerformanceCounter()与QueryPerformanceFrequency()。QueryPerformanceCounter()用于获取当前过去的时钟计数,QueryPerformanceFrequency()用于获取每秒钟的时钟记数,它用于将时钟计数转换为实际时间。
下面为使用QueryPerformanceCounter()测量过去时间的用法。
#include <iostream> #include <windows.h> // 使用Windows APIs using namespace std; int main() { LARGE_INTEGER frequency; // 1秒的时钟标记 LARGE_INTEGER t1, t2; // 时钟计数 double elapsedTime; // 获取每秒的时钟计数 QueryPerformanceFrequency(&frequency); // 开始计时 QueryPerformanceCounter(&t1); // 处理... ... // 结束计时 QueryPerformanceCounter(&t2); // 计算与打印毫米级的过去时间 elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart; cout << elapsedTime << " ms.\n"; return 0; }
Unix、Linux与Mac
gettimeofday()可以用于Unix或Linux类系统。该函数定义于”sys/time.h“,因此,在使用gettimeofday()之前必须包含该头文件。它也可以产生1微妙级的分辨率。这里为代码片段。
#include <iostream> #include <sys/time.h> // 使用gettimeofday() using namespace std; int main() { timeval t1, t2; double elapsedTime; // 开始计时 gettimeofday(&t1, NULL); // 处理... ... // 结束计时 gettimeofday(&t2, NULL); // 计算以及打印毫秒级过去时间 elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0; // sec to ms elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms cout << elapsedTime << " ms.\n"; return 0; }
C++Timer类
该时钟类结合QueryPerformanceCounter()与gettimeofday()。因此,它可以用于Unix/Linux/Mac及Windows系统。它也提供简单的获取过去时间的借口。
头文件:
#ifdef WIN32 // Windows系统 #include <windows.h> #else // Unix类系统 #include <sys/time.h> #endif class Timer { public: Timer(); ~Timer(); void start(); // 开始计时 void stop(); // 结束计时 double getElapsedTime(); // 获取秒级过去时间 double getElapsedTimeInSec(); // 获取秒级过去时间(同getElapsedTime) double getElapsedTimeInMilliSec(); // 获取毫秒级过去时间 double getElapsedTimeInMicroSec(); // 获取微妙级过去时间 protected: private: double startTimeInMicroSec; double endTimeInMicroSec; int stopped; #ifdef WIN32 LARGE_INTEGER frequency; LARGE_INTEGER startCount; LARGE_INTEGER endCount; #else timeval startCount; timeval endCount; #endif };
实现文件:
#include "Timer.h" #include <stdlib.h> Timer::Timer() { #ifdef WIN32 QueryPerformanceFrequency(&frequency); startCount.QuadPart = 0; endCount.QuadPart = 0; #else startCount.tv_sec = startCount.tv_usec = 0; endCount.tv_sec = endCount.tv_usec = 0; #endif stopped = 0; startTimeInMicroSec = 0; endTimeInMicroSec = 0; } Timer::~Timer() { } void Timer::start() { stopped = 0; // 重置结束标记 #ifdef WIN32 QueryPerformanceCounter(&startCount); #else gettimeofday(&startCount, NULL); #endif } void Timer::stop() { stopped = 1; // 设置计时器结束标记 #ifdef WIN32 QueryPerformanceCounter(&endCount); #else gettimeofday(&endCount, NULL); #endif } double Timer::getElapsedTimeInMicroSec() { #ifdef WIN32 if(!stopped) QueryPerformanceCounter(&endCount); startTimeInMicroSec = startCount.QuadPart * (1000000.0 / frequency.QuadPart); endTimeInMicroSec = endCount.QuadPart * (1000000.0 / frequency.QuadPart); #else if(!stopped) gettimeofday(&endCount, NULL); startTimeInMicroSec = (startCount.tv_sec * 1000000.0) + startCount.tv_usec; endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec; #endif return endTimeInMicroSec - startTimeInMicroSec; } double Timer::getElapsedTimeInMilliSec() { return this->getElapsedTimeInMicroSec() * 0.001; } double Timer::getElapsedTimeInSec() { return this->getElapsedTimeInMicroSec() * 0.000001; } double Timer::getElapsedTime() { return this->getElapsedTimeInSec(); }
下面代码展示基础用法。
#include <iostream> #include "Timer.h" using namespace std; int main() { Timer timer; // 开始计时 timer.start(); // 处理... ... // 结束计时 timer.stop(); // 打印毫秒级过去时间 cout << timer.getElapsedTimeInMilliSec() << " ms.\n"; return 0; }
源代码在:timer.zip。