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


引言

游戏程序中有很多需要用到时间的地方,往往会通过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



  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值