windows下实现微秒级的延时
2010年11月17日
1.微秒级的延时肯定不能基于消息(SetTimer函数),因为一出现消息堵塞等就会影响精
度,而且setTimer单位才是毫秒.实际响应时间可能要到55毫秒左右.
2.微秒级的延时也不能不能基于中断,VxD最快的时钟服务程序Set_Global_Time_Out函数
才能保证1毫秒的精度.其他挂接int 8H中断处理函数等,只能保证55ms的精度.(有时还不
能)
3.因此可以想到汇编下的那种基于循环执行语句的那种延时.但汇编那种代码不通用,跟
cpu的频率有关.
所以可以用windows下的几个函数来写出通用代
码.GetTickCout,timeGetTime,QueryPerformanceCounter.
1)GetTickCout响应只能保证55ms的精度
2)timeGetTime只能保证1ms的精度
3)而QueryPerformanceCounter函数不依赖计算中断的次数,而是靠读取别的硬件时钟来
实现的,可以有0.8微秒的精度.这个系统不支持windows 95以下的系统,不过这些系统应
该没人用了吧.呵呵.
下面是示例代码:
//LARGE_INTEGER类型类似一个64位的整型,是一个union,里面是LongLong类型和两个
long组成的结构体的union.
//QueryPerformanceFrequency函数得到你的计算机里高精度计时器每秒计时多少次,
//参数LARGE_INTEGER,返回false表示你的当前计算机硬件不支持高精度计时器.
//QueryPerformanceCounter函数得到当前计时器记了多少次.类似与GetTickCout.
#include
#include
using namespace std;
void main(){
int delayTime = 20; //微秒级的延时.
LARGE_INTEGER m_liPerfFreq={0};
if (!QueryPerformanceFrequency(&m_liPerfFreq))
{
cout = delayTime)
break;
}
cout.precision(40);
cout windows是多任务系统,只要保证windows执行这段代码时不被其他进程打断,就可以
保证延时微秒级成功.出现打断的几率很小.一般可以不考虑.如果代码执行时间低于一
个时间片,那就100%不会被打断了.
在SDK中,可以用DWORD timeGetTime(VOID)函数获取系统时间,其返回值是毫秒单位的。可以用其实现延时功能的函数。
void Delay(DWORD delayTime)
{
DWORD delayTimeBegin;
DWORD delayTimeEnd;
delayTimeBegin=timeGetTime();
do
{
delayTimeEnd=timeGetTime();
}while(delayTimeEnd-delayTimeBegin或#i nclude 并在project->settings->link->Object/library modules中添加winmm.lib
也可以在文件头部添加 #pragma comment( lib,"winmm.lib" )
命令行:#pragma comment( lib,"xxx.lib" )时预编译处理指令,让vc将winmm.lib添加到工程中去进行编译。 在Windows平台下,常用的计时器有两种,一种是timeGetTime多媒体计时器,它可以提供毫秒级的计时。但这个精度对很多应用场合而言 还是太粗糙了。另一种是QueryPerformanceCount计数器,随系统的不同可以提供微秒级的计数。对于实时图形处理、多媒体数据流处理、或 者实时系统构造的程序员,善用QueryPerformanceCount/QueryPerformanceFrequency是一项基本功。
本文要介绍的,是另一种直接利用Pentium CPU内部时间戳进行计时的高精度计时手段。以下讨论主要得益于《Windows图形编程》一书,第15页-17页,有兴趣的读者可以直接参考该书。关于 RDTSC指令的详细讨论,可以参考Intel产品手册。本文仅仅作抛砖之用。
在Intel Pentium以上级别的CPU中,有一个称为"时间戳(Time Stamp)"的部件,它以64位无符号整型数的格式,记录了自CPU上电以来所经过的时钟周期数。由于目前的CPU主频都非常高,因此这个部件可以达到 纳秒级的计时精度。这个精确性是上述两种方法所无法比拟的。
在Pentium以上的CPU中,提供了一条机器指令RDTSC(Read Time Stamp Counter)来读取这个时间戳的数字,并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C++语言保存函数 返回值的寄存器,所以我们可以把这条指令看成是一个普通的函数调用。像这样:
inline unsigned __int64 GetCycleCount()
{
__asm RDTSC
}
但是不行,因为RDTSC不被C++的内嵌汇编器直接支持,所以我们要用_emit伪指令直接嵌入该指令的机器码形式0X0F、0X31,如下:
inline unsigned __int64 GetCycleCount()
{
__asm _emit 0x0F
__asm _emit 0x31
}
以后在需要计数器的场合,可以像使用普通的Win32 API一样,调用两次GetCycleCount函数,比较两个返回值的差,像这样:
unsigned long t;
t = (unsigned long)GetCycleCount();
//Do Something time-intensive ...
t -= (unsigned long)GetCycleCount();
《Windows图形编程》第15页编写了一个类,把这个计数器封装起来。有兴趣的读者可以去参考那个类的代码。作者为了更精确的定时,做了一 点小小的改进,把执行RDTSC指令的时间,通过连续两次调用GetCycleCount函数计算出来并保存了起来,以后每次计时结束后,都从实际得到的 计数中减掉这一小段时间,以得到更准确的计时数字。但我个人觉得这一点点改进意义不大。在我的机器上实测,这条指令大概花掉了几十到100多个周期,在 Celeron 800MHz的机器上,这不过是十分之一微秒的时间。对大多数应用来说,这点时间完全可以忽略不计;而对那些确实要精确到纳秒数量级的应用来说,这个补偿 也过于粗糙了。
这个方法的优点是:
1.高精度。可以直接达到纳秒级的计时精度(在1GHz的CPU上每个时钟周期就是一纳秒),这是其他计时方法所难以企及的。
2.成本低。timeGetTime 函数需要链接多媒体库winmm.lib,QueryPerformance* 函数根据MSDN的说明,需要硬件的支持(虽然我还没有见过不支持的机器)和KERNEL库的支持,所以二者都只能在Windows平台下使用(关于 DOS平台下的高精度计时问题,可以参考《图形程序开发人员指南》,里面有关于控制定时器8253的详细说明)。但RDTSC指令是一条CPU指令,凡是 i386平台下Pentium以上的机器均支持,甚至没有平台的限制(我相信i386版本UNIX和Linux下这个方法同样适用,但没有条件试验),而 且函数调用的开销是最小的。
3.具有和CPU主频直接对应的速率关系。一个计数相当于1/(CPU主频Hz数)秒,这样只要知道了CPU的主频,可以直接计算出时间。这和 QueryPerformanceCount不同,后者需要通过QueryPerformanceFrequency获取当前计数器每秒的计数次数才能换 算成时间。
这个方法的缺点是:
1.现有的C/C++编译器多数不直接支持使用RDTSC指令,需要用直接嵌入机器码的方式编程,比较麻烦。
2.数据抖动比较厉害。其实对任何计量手段而言,精度和稳定性永远是一对矛盾。如果用低精度的timeGetTime来计时,基本上每次计时的结果都是相同的;而RDTSC指令每次结果都不一样,经常有几百甚至上千的差距。这是这种方法高精度本身固有的矛盾。
关于这个方法计时的最大长度,我们可以简单的用下列公式计算:
自CPU上电以来的秒数 = RDTSC读出的周期数 / CPU主频速率(Hz)
64位无符号整数所能表达的最大数字是1.8×10^19,在我的Celeron 800上可以计时大约700年(书中说可以在200MHz的Pentium上计时117年,这个数字不知道是怎么得出来的,与我的计算有出入)。无论如何,我们大可不必关心溢出的问题。
下面是几个小例子,简要比较了三种计时方法的用法与精度
//Timer1.cpp 使用了RDTSC指令的Timer类//KTimer类的定义可以参见《Windows图形编程》P15
//编译行:CL Timer1.cpp /link USER32.lib
#include
#include "KTimer.h"
main()
{
unsigned t;
KTimer timer;
timer.Start();
Sleep(1000);
t = timer.Stop();
printf("Lasting Time: %d\n",t);
}
//Timer2.cpp 使用了timeGetTime函数
//需包含,但由于Windows头文件错综复杂的关系
//简单包含比较偷懒:)
//编译行:CL timer2.cpp /link winmm.lib
#include
#include
main()
{
DWORD t1, t2;
t1 = timeGetTime();
Sleep(1000);
t2 = timeGetTime();
printf("Begin Time: %u\n", t1);
printf("End Time: %u\n", t2);
printf("Lasting Time: %u\n",(t2-t1));
}
//Timer3.cpp 使用了QueryPerformanceCounter函数
//编译行:CL timer3.cpp /link KERNEl32.lib
#include
#include
main()
{
LARGE_INTEGER t1, t2, tc;
QueryPerformanceFrequency(&tc);
printf("Frequency: %u\n", tc.QuadPart);
QueryPerformanceCounter(&t1);
Sleep(1000);
QueryPerformanceCounter(&t2);
printf("Begin Time: %u\n", t1.QuadPart);
printf("End Time: %u\n", t2.QuadPart);
printf("Lasting Time: %u\n",( t2.QuadPart- t1.QuadPart));
}
////////////////////////////////////////////////
//以上三个示例程序都是测试1秒钟休眠所耗费的时间
file://测/试环境:Celeron 800MHz / 256M SDRAM
// Windows 2000 Professional SP2
// Microsoft Visual C++ 6.0 SP5
////////////////////////////////////////////////
以下是Timer1的运行结果,使用的是高精度的RDTSC指令
Lasting Time: 804586872
以下是Timer2的运行结果,使用的是最粗糙的timeGetTime API
Begin Time: 20254254
End Time: 20255255
Lasting Time: 1001
以下是Timer3的运行结果,使用的是QueryPerformanceCount API
Frequency: 3579545
Begin Time: 3804729124
End Time: 3808298836
Lasting Time: 3569712
http://blog.csdn.net/lhsxsh/archive/2009/05/05/415 1393.aspx
发表评论
-
Windows窗口和消息(2)
2012-01-20 00:26 466Windows窗口和消息(2) 2011 ... -
Windows Azure真实案例--Lokad 公司通过软件+服务高效提供先进的预测服务
2012-01-20 00:26 575Windows Azure真实案例--Loka ... -
探究 Windows Vista 中新的语音识别与合成 API(转)
2012-01-20 00:26 935探究 Windows Vista 中新的 ... -
方法总比问题多,解决打不开windows mobile工程的问题。
2012-01-20 00:26 937方法总比问题多,解决打 ... -
晒晒孩子们的作文(一)
2012-01-19 01:23 628晒晒孩子们的作文(一) ... -
爱,不是曾经
2012-01-19 01:23 521爱,不是曾经 2011年12月1 ... -
多线程opengl 创建位图失败之解决方案
2012-01-17 00:39 2165多线程opengl 创建位图失败之解决方案 2010年07月 ... -
代码例子区全区搜索索引(27th Feb 2011 Updated)
2012-01-17 00:39 607代码例子区全区搜索索引(27th Feb 2011 Updat ... -
基于Windows CE的激光切割机开放式数控系统的研究
2012-01-17 00:39 533基于Windows CE的激光切割 ... -
转: Boost下载安装编译配置使用指南(含Windows和Linux)
2012-01-17 00:39 604转: Boost下载安装编译配 ... -
windows编程经典书籍
2012-01-17 00:39 681windows编程经典书籍 2011年07月04日 [转 ... -
la la la la
2012-01-15 15:00 591la la la la 2009年11月09日 ... -
铁路企业职工再加薪 一线员工每月至少涨620元
2012-01-15 15:00 691铁路企业职工再加薪 一 ... -
7689
2012-01-15 15:00 5727689 2009年09月28日 家长会发言稿 家长 ... -
泰国副总理:已获本国军人杀害中国船员充分证据
2012-01-15 15:00 408泰国副总理:已获本国军人杀害中国船员充分证据 2012年01 ...
相关推荐
如何在Windows平台上实现微秒级延时, time相关函数实现的只是毫秒级的延时,有时无法满足精度要求,下面是一个微秒级延时的测试实例,测试OK。测试程序每间隔100us,计数器加1,计数器每增加10000,打印出当前的...
基于51单片机的精确延时(微秒级)对于某些对时间精度要求较高的程序,用c 写延时显得有些力不从心,故需用到汇编程序。 本人通过测试,总结了51 的精确延时函数(在c 语言中嵌入汇编)分享给大家。至于如何在c 中嵌入...
实现毫秒精度的延时, QueryPerformanceFrequency
Visual C++实现微秒级精度定时器。
利用内核函数实现时间的精准输出用Kernel32 内核函数
用Sleep函数延时,如何时间太长,程序就像无响应一样,采用消息泵可以解决这个问题 延时 时间 包括 秒 毫秒 微秒
实现了微秒级计时器,提供一个类,风格和C#自带的定时器差不多,误差应该在1ms以内,这玩意想要真正十分精确还是得用硬件计时器才行.
nrf52832微秒级定时器,定时器244微秒执行一次定时事件
CPU 测速(MHz)和高精度延时(微秒级
其一:对于延时很短的,要求在us级的,采用“_nop_”函数,这个函数相当汇编NOP指令,延时几微秒,就插入个这样的函数。 其二:对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。 在选择C51中循环...
采用系统api实现 微秒级精确计时
QueryPerformanceCounter和QueryPerformanceFrequency,可以实现微秒级延时,毫秒级特别精准,里面有一个延时函数和其进行对比,亲自测试过
Visual+C++实现微秒级精度定时器[借鉴].pdf
Windows微秒级延时/Sleep延时测试代码 在Windows环境下使用Sleep的时候,经常会遇到明明Sleep了1ms,结果被唤醒时所消耗的时间大于1ms, 对于那些精确度稍微高一点的Sleep控制时序,就不起作用了,众所周知Windows...
在 VC中,实现了以微秒延时函数的程序,可以用于对延时要求非常精确的程序,精度为 "1e6/CPU主频" 微秒。已调试通过。
在使用C语言编程时延时程序是非常常见的,但是实现一个精确的延时是不太容易的,在给一个朋友的公司产品做维护时,发现一段代码,可以实现微妙级的延时。看起来代码非常简单。但是我以前没有想到过。
基于Windows 的高精度实时定时程序,精度可达到微秒级,可以用来发送串口数据等
STM32延时函数,包括毫秒级延时函数和微秒级延时函数两个函数,微妙延时函数延时误差为百分之一,即延时100微妙比实际值少1微妙。
实现微秒级的定时,一个实现的小例子,不长,但很有用。
Linux下对时间进行运算,如果是到秒级的,一般是用time之类的函数实现。文中介绍了Linux下精确到微秒级的时间操作函数。主要是用到了gettimeofday函数,并且介绍了这个函数的结构。