首页 > Windows开发 > 详细

高精度计时器QueryPerformanceCounter正确的打开方式(windows环境下)

时间:2015-03-27 10:58:14      阅读:453      评论:0      收藏:0      [点我收藏+]

引言

游戏程序中有很多需要用到时间的地方,往往会通过windows API来获取时间。先前写过一篇文章是关于时间同步的:网络游戏中的(低精度)时间同步,当需求更高精度的时间同步时,就需要QueryPerformanceCounter这样的API,QueryPerformanceCounter的使用有一些隐含的陷阱需要注意。

关于QueryPerformanceCounter
官方解释:https://msdn.microsoft.com/zh-cn/ms644904用于高精度计时器时间读取,重点是执行成功返回非0值,精度是<1us的。

多核CPU采用QueryPerformanceCounter的问题
程序中通常使用时间差定时地调用某种接口的情况,而时间差的使用有一个隐含因素,即时间是顺序累加的,当然我们通常的时间当然是累加的,不会出现停滞甚至倒转,而QueryPerformanceCounter的运行情况是依赖于CPU的,当CPU是多核时,在某一线程内调用QueryPerformanceCounter,线程会切换于不同的核心之间,这时候QueryPerformanceCounter返回值是不确定的,或者说这时候的计时器并不能保证是顺序累加的,相应地,当使用时间差时会出现负数或者0的情况,这显然不符合开发者的预期。

如何在多核CPU的环境下使用QueryPerformanceCounter
目前多核的CPU已经飞入寻常百姓人家,因而作为开发人员,不得不面对在多核CPU的机器上使用QueryPerformanceCounter的情况。当我们需要在某一进程中获取时间,需要将该线程绑定在某一固定的核心上,这样获取的高精度计时器才是可靠的。通过SetThreadAffinityMask可以实现这一目的。举个栗子:
HANDLE thread = GetCurrentThread();

// Set affinity to the first core
DWORD_PTR oldMask = SetThreadAffinityMask(thread, GTimerMask );

// Query the timer
QueryPerformanceCounter(&mStartTime);
mStartTick = GetTickCount();

// Reset affinity
SetThreadAffinityMask(thread, oldMask);
使用前,需要重置GTimerMask:
void appResetTimerMask()

{

// Get the current process core mask

ULONG_PTR proMask;

ULONG_PTR sysMask;

GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);


// If procMask is 0, consider there is only one core available

// (using 0 as procMask will cause an infinite loop below)

if (procMask == 0)

     procMask = 1;


// Find the lowest core that this process uses

      if (GTimerMask == 0)

      {

          GTimeMask = 1;

          while ((GTimerMask & procMask) == 0)

          {

              GTimerMask <<= 1;

          }

      }

}
完整的使用方法:

inline DOUBLE appSeconds()

{
	LARGE_INTEGER Cycles;

	HANDLE thread = NULL;

	ULONG_PTR oldMask = 0;


	if (GEnableTimerAffinityMask)      //GEnableTimerAffinityMask为TRUE时需要将线程绑定到固定核心
	{

		thread = GetCurrentThread();


		if (GTimerMask == 0)       //GTimerMask默认值为0

		{

			appResetTimerMask();

		}


		oldMask = SetThreadAffinityMask(thread, GTimerMask);

	}

	QueryPerformanceCounter(&Cycles);

	if (GEnableTimerAffinityMask)

	{

		SetThreadAffinityMask(thread, oldMask);

	}


	return Cycles.QuadPart * GSecondsPerCycle + 16777216.0;

}

参考资料:
http://bbs.csdn.net/topics/310177138
https://msdn.microsoft.com/en-us/library/ee417693%28v=vs.85%29.aspx
http://blog.csdn.net/hunter8777/article/details/6204719



高精度计时器QueryPerformanceCounter正确的打开方式(windows环境下)

原文:http://blog.csdn.net/coffeecato/article/details/44656001

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!