有篇写的不错游戏主循环框架的文章,https://blog.csdn.net/u014038143/article/details/78192076,提到了如何在避免死等的情况下实现高精度定时,另外一个程序员基于写了个测试程序https://blog.csdn.net/Gammy_Ye/article/details/90272904。这个程序每个循环都有输出,没法测试总体的偏差,注释掉其中的cout输出,统计一下6000次循环,每次等待0.01秒的总耗时。编译运行
- 总时间为60秒,期望在60秒左右完成,如果系统压力不大,实际上在92秒左右(观察Windows CPU,此期间CPU频率会降低到2Ghz以下)
- 打开视频播放器,基本在62秒左右完成
所以看起来还是有其它因素影响这个精度。搜索Windows API,看到多次提到mmtimer提供高精度计时,所以修改了一下代码,核心是通过timeBeginPeriod设置系统计时精度,如果设置为1或者2毫秒,则测试程序总是可以在62秒左右完成,输出如下
Total excu time for Loop 6000 times by 10ms=62684ms
Timer interval is 0.01
Use timer times is 6000
Total deviation is 26394715|2.63947
Average deviation is 0.000439912
其实把setwaitabletimer换成sleep(),完成时间也差不多,所以看起来要得到高精度计时,必须通过timeBeginPeriod来设置系统timer精度,按微软文档,在Win10 2004前,这个设置甚至是全局的,即任何一个程序设置过,所以程序都会使用最小精度值,2004后则只影响同样调用过此函数的进程。
代码如下:
#include <windows.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"winmm.lib")
int main()
{
#define TARGET_RESOLUTION 1 // 1-millisecond target resolution
TIMECAPS tc;
UINT wTimerRes;
if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR)
{
// Error; application can\'t continue.
}
wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
wTimerRes = 2;
timeBeginPeriod(wTimerRes);
HANDLE hTimer = CreateWaitableTimer(nullptr, true, nullptr);
if (hTimer == nullptr)
{
return 1;
}
LARGE_INTEGER DueTime;
DueTime.QuadPart = -10000000LL / 100; //定时的间隔为0.01秒
int LoopTimes = 6000; //做6000次定时
LARGE_INTEGER Counter_1, Counter_2, Counter_3, Counter_Dif, Frequency;
Counter_Dif.QuadPart = 0;
if (QueryPerformanceFrequency(&Frequency) == false)
{
return 1;
}
QueryPerformanceCounter(&Counter_1);
for (int I = 0; I < LoopTimes; ++I)
{
SetWaitableTimer(hTimer, &DueTime, 0, nullptr, nullptr, 0);
if (QueryPerformanceCounter(&Counter_2) == false)
{
return 1;
}
WaitForSingleObject(hTimer, INFINITE);
//Sleep(10);
if (QueryPerformanceCounter(&Counter_3) == false)
{
return 1;
}
LARGE_INTEGER Delta;
Delta.QuadPart = abs(Counter_3.QuadPart - Counter_2.QuadPart + DueTime.QuadPart);//本次定时的误差值(单位是硬件计数)
Counter_Dif.QuadPart += Delta.QuadPart;
//cout << Counter_3.QuadPart - Counter_2.QuadPart << "|" << Delta.QuadPart << "|"
// << (long double)Delta.QuadPart / Frequency.QuadPart << endl; //本次定时的误差时间(单位为秒)
}
timeEndPeriod(wTimerRes);
cout << "Total excu time for Loop 6000 times by 10ms=" << (Counter_3.QuadPart - Counter_1.QuadPart)*1000 / Frequency.QuadPart << "ms" << endl;
cout << "Timer interval is " << (long double)-DueTime.QuadPart / 10000000 << endl;
cout << "Use timer times is " << LoopTimes << endl;
cout << "Total deviation is " << Counter_Dif.QuadPart << "|" << (long double)Counter_Dif.QuadPart / Frequency.QuadPart << endl;
cout << "Average deviation is " << (long double)Counter_Dif.QuadPart / Frequency.QuadPart / LoopTimes << endl;//平均误差时间
system("pause");
}
