Windows几种sleep精度的测试,结果基于微秒

本文对比测试了Windows环境下不同方法的Sleep精度,包括原生Sleep、C++11的sleep_for、socket的select、多媒体时钟QueryPerformanceCounter及MsgWaitForMultipleObjectsEx,发现select方法精度稍高,而QueryPerformanceCounter虽精度高但CPU消耗大。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Windows几种sleep精度的测试

在Windows环境下使用Sleep的时候,经常会遇到明明Sleep了1ms,结果被唤醒时所消耗的时间大于1ms,

对于那些精确度稍微高一点的Sleep控制时序,就不起作用了,众所周知Windows的Sleep是基于毫秒级别的,如果需要精确到微秒级,需要另辟蹊径

本文总结了几种常用的控制时序的方式,可以作为一个参考,测试出来的数值并不一定准确,我在我电脑上和另外一台Win7 i5 2.3GHz的电脑上跑出来的结果差别比较大,可以根据自己电脑或者使用环境多跑些数据

源码下载链接:0积分下载链接,没有积分的评论区留言,我改链接

测试类别分别包括:

    1、Windows中的原生Sleep

    2、C++11的this_thread::sleep_for以及timeBeginPeriod调整定时器精度两个方式

    3、socket连接的select的方式

    4、多媒体时钟QueryPerformanceCounter的微秒级方式

    5、MsgWaitForMultipleObjectsEx

系统环境:Win10,CPU i7-8750 2.2GHz

一、Sleep耗时

先上代码和测试结果

代码:

void Precision_Sleep()
{
	std::string buffer;
	buffer.assign(BUFFER_SIZE, 0);
	buffer.clear();
	int i = TEST_TIMES;
	uint64_t total_used = 0;
	while (i) {
		i--;
		steady_clock::time_point time_begin = steady_clock::now();
		Sleep(Interval_Millisceonds);
		steady_clock::time_point time_end = steady_clock::now();
		char tmp[128] = {0};
		uint64_t used = duration_cast<microseconds>(time_end - time_begin).count();
		snprintf(tmp, 128, "%s Sleep %d ms, time used : %lld us\n",
				 __FUNCTION__, Interval_Millisceonds, used);
		total_used += used;
		buffer += tmp;
	}
	printf("%s\n", buffer.c_str());
	printf("%s Sleep %d ms, avatar %lld us\n\n",
		   __FUNCTION__, Interval_Millisceonds,total_used / TEST_TIMES);

}

结果:

这是跑了50次的平均结果,Sleep(1),实际的平均sleep时间大约在1500微秒

二、C++11,sleep_for

代码:

void Precision_sleep_for()
{
	std::string buffer;
	buffer.assign(BUFFER_SIZE, 0);
	buffer.clear();
	int i = TEST_TIMES;
	uint64_t total_used = 0;
	while (i) {
		i--;
		steady_clock::time_point time_begin = steady_clock::now();
		std::this_thread::sleep_for(microseconds(Interval_Microseconds));
		steady_clock::time_point time_end = steady_clock::now();
		char tmp[128] = {0};
		uint64_t used = duration_cast<microseconds>(time_end - time_begin).count();
		snprintf(tmp, 128, "%s Sleep %d us, time used : %lld us\n",
				 __FUNCTION__, Interval_Microseconds, used);
		total_used += used;
		buffer += tmp;
	}
	printf("%s", buffer.c_str());
	printf("%s Sleep %d us , avatar %lld us\n\n",
		   __FUNCTION__, Interval_Microseconds, total_used / TEST_TIMES);
}

结果:

sleep_for 1000us的平均时间也是在1500us左右

三、C++11 sleep_for 加上timeBeginPeriod

代码

void Precision_sleep_for_timeBeginPeriod()
{
	// Test for sleep_for and timeBeginPeriod;
	std::string buffer;
	buffer.assign(BUFFER_SIZE, 0);
	buffer.clear();
	int i = TEST_TIMES;
	uint64_t total_used = 0;

	while (i) {
		i--;
		steady_clock::time_point time_begin = steady_clock::now();
		timeBeginPeriod(1);//set Minimum timer resolution.
		std::this_thread::sleep_for(microseconds(Interval_Microseconds));
		timeEndPeriod(1);
		steady_clock::time_point time_end = steady_clock::now();
		char tmp[128] = {0};
		uint64_t used = duration_cast<microseconds>(time_end - time_begin).count();
		snprintf(tmp, 128, "%s Sleep %d us , time used : %lld us\n",
				 __FUNCTION__, Interval_Microseconds, used);
		total_used += used;
		buffer += tmp;
	}
	printf("%s", buffer.c_str());
	printf("%s Sleep %d us , avatar %lld us\n\n",
		   __FUNCTION__,Interval_Microseconds, total_used / TEST_TIMES);
}

结果

 

无语。。。结果跟不加差不多。。。

四、select的方法

socket 中的select有个超时设置,据说这个超时设置是微秒级的,所以利用这点来测试.

代码

void SleepSelectUS(SOCKET s, int64_t usec)
{
	struct timeval tv;
	fd_set dummy;
	FD_ZERO(&dummy);
	FD_SET(s, &dummy);
	tv.tv_sec = usec / 1000000L;
	tv.tv_usec = usec % 1000000L;
	select(0, 0, 0, &dummy, &tv);
	DWORD err = GetLastError();
	if (err != 0)
		printf("Error : %d", err);
}

void Precision_select()
{
	std::string buffer;
	buffer.assign(BUFFER_SIZE, 0);
	buffer.clear();
	int i = TEST_TIMES;
	uint64_t total_used = 0;
	WORD wVersionRequested = MAKEWORD(1, 0);
	WSADATA wsaData;
	WSAStartup(wVersionRequested, &wsaData);

	SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	while (i) {
		i--;
		steady_clock::time_point time_begin = steady_clock::now();
		SleepSelectUS(s, Interval_Microseconds);
		steady_clock::time_point time_end = steady_clock::now();
		char tmp[128] = {0};
		uint64_t used = duration_cast<microseconds>(time_end - time_begin).count();
		snprintf(tmp, 128, "%s Sleep %d us, time used : %lld us\n",__FUNCTION__, Interval_Microseconds, used);
		total_used += used;
		buffer += tmp;
	}
	closesocket(s);
	printf("%s", buffer.c_str());
	printf("%s Sleep %d us, avatar %lld us\n\n", __FUNCTION__, Interval_Microseconds, total_used / TEST_TIMES);

}

结果:

select的精度度稍微有所提高,平均时间为1017us,比上面两个精确一些,基本上可以达到我们的要求。

再看下500us和100us的

500微秒和100微秒的时间差不多,大致可以判断select的精度在500us左右。

五、多媒体时钟轮训的方式

代码

void SleepPerformUS(DWORD usec)
{
	LARGE_INTEGER perfCnt, start, now;

	QueryPerformanceFrequency(&perfCnt);
	QueryPerformanceCounter(&start);

	do {
		QueryPerformanceCounter((LARGE_INTEGER*)&now);
	} while ((now.QuadPart - start.QuadPart) / float(perfCnt.QuadPart) * 1000 * 1000 < usec);
}

void Precision_QueryPerformanceCounter()
{
	// Test for select;
	std::string buffer;
	buffer.assign(BUFFER_SIZE, 0);
	buffer.clear();
	int i = TEST_TIMES;
	uint64_t total_used = 0;

	WORD wVersionRequested = MAKEWORD(1, 0);
	WSADATA wsaData;
	WSAStartup(wVersionRequested, &wsaData);

	SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	while (i) {
		i--;
		steady_clock::time_point time_begin = steady_clock::now();
		SleepPerformUS(Interval_Microseconds);
		steady_clock::time_point time_end = steady_clock::now();
		char tmp[128] = {0};
		uint64_t used = duration_cast<microseconds>(time_end - time_begin).count();
		snprintf(tmp, 128, "%s Sleep 1000 us, time used : %lld us\n", __FUNCTION__, used);
		total_used += used;
		buffer += tmp;
	}
	closesocket(s);
	printf("%s", buffer.c_str());
	printf("%s Sleep 1000 us, avatar %lld us\n\n", __FUNCTION__, total_used / TEST_TIMES);

}

结果

喜出望外~sleep1000us所消耗的时间正好是我们需要的1000us!!!!是不是很心动??

再试试500us和100us的

但是!!!!!别高兴太早!!

从代码里面,我们可以看到这段代码:

	do {
		QueryPerformanceCounter((LARGE_INTEGER*)&now);
	} while ((now.QuadPart - start.QuadPart) / float(perfCnt.QuadPart) * 1000 * 1000 < usec);

这就意味着,这种方式实现的Sleep是不停的在轮训,CPU一直在被占用,我在我的电脑上跑下来,这个轮训会消耗我8%的CPU

我是6核12线程,就意味着是单线程满负荷

这个方式虽然好用,经不起CPU的消耗,也是没有办法满足我们需要的!

那些在推荐别人使用这种方式的人,不知道自己有没有使用这个代码,如果用在业务上了,我很佩服你们的产品!

六、MsgWaitForMultipleObjectsEx

代码:

void Precision_MsgWaitForMultipleObjectsEx()
{
	std::string buffer;
	buffer.assign(BUFFER_SIZE, 0);
	buffer.clear();
	int i = TEST_TIMES;
	uint64_t total_used = 0;

	while (i) {
		i--;
		steady_clock::time_point time_begin = steady_clock::now();
		MsgWaitForMultipleObjectsEx(0, NULL, Interval_Millisceonds, QS_ALLPOSTMESSAGE,
									MWMO_INPUTAVAILABLE);
		steady_clock::time_point time_end = steady_clock::now();
		char tmp[128] = {0};
		uint64_t used = duration_cast<microseconds>(time_end - time_begin).count();
		snprintf(tmp, 128, "%s Sleep %d ms, time used : %lld us\n", __FUNCTION__, Interval_Millisceonds, used);
		total_used += used;
		buffer += tmp;
	}
	printf("%s", buffer.c_str());
	printf("%s Sleep %d us, avatar %lld ms\n\n", __FUNCTION__, Interval_Millisceonds, total_used / TEST_TIMES);

}

结果:

MsgWaitForMultipleObjectsEx测试下来的时间浮动在1200us左右,最低的出现一次1060us,最高的跑到了1400+us,

 

综上所述,使用select的方式可以让我们的精度有所提高,相对于windows的Sleep和C++11的sleep_for,还是稍微有点提高,

后者时间这么接近,sleep_for的实现,不知道下面是不是就直接调用的Sleep。

另外有个高精度的方式,我没有做测试,从作者给出的时间来看也是很准的,有兴趣的也可以尝试一下

链接地址:高精度/微秒级线程的实现

 

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值