windows中的时间函数

 

  介绍       来自百度

 

 

 

我们在衡量一个函数运行时间,或者判断一个算法的时间效率,或者在程序中我们需要一个定时器,定时执行一个特定的操作,比如在多媒

体中,比如在游戏中等,都会用到时间函数。还比如我们通过记录函数或者算法开始和截至的时间,然后利用两者之差得出函数或者算法的运行时间

。编译器和操作系统为我们提供了很多时间函数,这些时间函数的精度也是各不相同的,所以,如果我们想得到准确的结果,必须使用合适的时间函

数。现在介绍 windows 下的几种常用时间函数。

 

 

 

1 Sleep 函数

使用: sleep(1000) ,在 Windows Linux 1000 代表的含义并不相同, Windows 下的表示 1000 毫秒,也就是 1 秒钟;

 

                            Linux 下表示 1000 秒, Linux 下使用毫秒级别的函数可以使用 usleep

 

原理: sleep 函数是使调用 sleep 函数的线程休眠,线程主动放弃时间片。当经过指定的时间间隔后,再启动线程,继续执行代码。

 

         Sleep 函数并不能起到定时的作用,主要作用是延时。在一些多线程中可能会看到 sleep(0); 其主要目的是让出时间片。

 

精度: sleep 函数的精度非常低,当系统越忙它精度也就越低,有时候我们休眠 1 秒,可能 3 秒后才能继续执行。它的精度取决于线程自身优先级、

 

         其他线程的优先级,以及线程的数量等因素。

 

2 MFC 下的 timer 事件       

 

使用:

 

        1. 调用函数 SetTimer() 设置定时间隔,如 SetTimer(0,100,NULL) 即为设置 100 毫秒的时间间隔;

 

        2. 在应用程序中增加定时响应函数 OnTimer() ,并在该函数中添加响应的处理语句,用来完成时间到时的操作。    

 

原理:同 sleep 函数一样。不同的是 timer 是一个定时器,可以指定回调函数,默认为 OnTimer() 函数。    

 

精度: timer 事件的精度范围在毫秒级别,系统越忙其精度也就越差。

 

3 C 语言下的 Time

 

使用: time_t t;time(&t);Time 函数是获取当前时间。    

原理: time 函数主要用于获取当前时间,比如我们做一个电子时钟程序,就可以使用此函数,获取系统当前的时间。    

精度:秒级别

 

4 COM 对象中的 COleDateTime COleDateTimeSpan    

 

使用:

 

         COleDateTime start_time = COleDateTime::GetCurrentTime();

         COleDateTimeSpan end_time = COleDateTime::GetCurrentTime()-start_time;

         While(end_time.GetTotalSeconds() < 2)

 

         {

 

            // 处理延时或定时期间能处理其他的消息

               DoSomething()

               end_time = COleDateTime::GetCurrentTime-start_time;

 

         }

 

原理:以上代表延时 2 秒,而这两秒内我们可以循环调用 DoSomething() ,从而实现在延时的时候我们也能够处理其他的函数,或者消息。

 

         COleDateTime,COleDateTimeSpan MFC CTime CTimeSpan COM 中的应用,所以,上面的方法对于 CTime CTimeSpa 同样有效  

 

精度:秒级别

 

5 C 语言下的时钟周期 clock()      

 

使用:    clock_t start = clock();

            Sleep(100);

            clock_t end = clock();

            double d = (double)(start - end) / CLOCKS_PER_SEC;      

 

原理: clock() 是获取计算机启动后的时间间隔。

 

精度: ms 级别,对于短时间内的定时或者延时可以达到 ms 级别,对于时间比较长的定时或者延迟精度还是不够。

 

         windows CLOCKS_PER_SEC 1000

 

6 Windows 下的 GetTickCount()

 

使用:

        DWORD start = GetTickCount();

        Sleep(100);

        DWORD end = GetTickCount();

 

原理:

        GetTickCount() 是获取系统启动后的时间间隔。通过进入函数开始定时,到退出函数结束定时,从而可以判断出函数的执行时间,这种时间也并

        非是函数或者算法的真实执行时间,因为在函数和算法线程不可能一直占用 CPU ,对于所有判断执行时间的函数都是一样,不过基本上已经很准

        确,可以通过查询进行定时。 GetTickCount() Clock() 函数是向主板 BIOS real time clock 时间,会有中断产生,以及延迟问题。

 

精度: WindowsNT 3.5 以及以后版本精度是 10ms ,它的时间精度比 clock 函数的要高, GetTickCount() 常用于多媒体中。

 

7 Windows timeGetTime

 

使用:需要包含 Mmsystem.h Windows.h ,加入静态库 Winmm.lib.

         timeBeginPeriod(1);

         DWORD start = timeGetTime();

         Sleep(100);

         DWORD end = timeGetTime();

 

         timeEndPeriod(1);

原理: timeGetTime 也时常用于多媒体定时器中,可以通过查询进行定时。通过查询进行定时,本身也会影响定时器的定时精度。

精度:毫秒,与 GetTickCount() 相当。但是和 GetTickCount 相比, timeGetTime 可以通过 timeBeginPeriod timeEndPeriod 设置定时器的最小解析

         精度 , timeBeginPeriod,timeEndPeriod 必须成对出现。

 

8 windows 下的 timeSetEvent

 

使用:还记的 VC 下的 Timer 吗? Timer 是一个定时器,而以上我们提到几种时间函数或者类型,实现定时功能只能通过轮训来实现,也就是必须另外创

         建一个线程单独处理,这样会影响定时精度,好在 windows 提供了内置的定时器 timeSetEvent

         函数原型为 MMRESULT timeSetEvent UINT uDelay, // 以毫秒指定事件的周期

                                                                UINT uResolution, // 以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为 1ms

                                                                LPTIMECALLBACK lpTimeProc, // 指向一个回调函数

                                                                 WORD dwUser, // 存放用户提供的回调数据

                                                                 UINT fuEvent // 标志参数, TIME_ONESHOT :执行一次; TIME_PERIODIC :周期性执行       

        具体应用时,可以通过调用 timeSetEvent() 函数,将需要周期性执行的任务定义在 lpFunction 回调函数中 ( 如:定时采样、控制等 ) ,从而完成所

        需处理的事件。需要注意的是:任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后,应及时调用 timeKillEvent() 将之释放。

原理:可以理解为代回调函数的 timeGetTime

 

精度:

 

      毫秒, timeSetEvent 可以通过 timeBeginPeriod timeEndPeriod 设置定时器的最小解析精度 , timeBeginPeriod,timeEndPeriod 必须成对出现

 

9: 高精度时控函数 QueryPerformanceFrequency QueryPerformanceCounter

 

使用: LARGE_INTEGER m_nFreq;

          LARGE_INTEGER m_nBeginTime;

          LARGE_INTEGER nEndTime;

          QueryPerformanceFrequency(&m_nFreq); // 获取时钟周期

          QueryPerformanceCounter(&m_nBeginTime); // 获取时钟计数

          Sleep(100);

          QueryPerformanceCounter(&nEndTime);

          cout << (nEndTime.QuadPart-m_nBeginTime.QuadPart)*1000/m_nFreq.QuadPart << endl;

 

原理: CPU 上也有一个计数器,以机器的 clock 为单位,可以通过 rdtsc 读取,而不用中断,因此其精度与系统时间相当。

精度:计算机获取硬件支持,精度比较高,可以通过它判断其他时间函数的精度范围。

         QueryPerformanceCount 计数器,随系统的不同可以提供微秒级的计数。对于实时图形处理、多媒体数据流处理、或者实时系统构造的程序

         员,善用 QueryPerformanceCount/QueryPerformanceFrequency 是一项基本功。

 

10: 小结

 

以上提到常用的 9 种时间函数,由于他们的用处不同,所以他们的精度也不尽相同,所以

如果简单的延时可以用 sleep 函数,稍微准确的延时可以使用 clock 函数, GetTickCount 函数,更高级的实用 timeGetTime 函数;

简单的定时事件可以用 Timer ,准确地可以用 imeSetEvent

或取一般系统时间可以通 time ,或者 CTime ,或者 COleDateTime ,获取准确的时间可以用 clock ,或者 GetTickCount 函数,或者 timeGetTime 函数

而获取准确地系统时间要使用硬件支持的 QueryPerformanceFrequency 函数, QueryPerformanceCounter 函数。

 

 

另一种直接利用 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   <tstdio.h>  

  #include   "KTimer.h"  

  main()  

  {  

    unsigned   t;  

    KTimer   timer;  

    timer.Start();  

    Sleep(1000);  

    t   =   timer.Stop();  

    printf("Lasting   Time:   %d/n",t);  

  }  

  

  //Timer2.cpp   使用了 timeGetTime 函数   

  // 需包含 <mmsys.h> ,但由于 Windows 头文件错综复杂的关系   

  // 简单包含 <windows.h> 比较偷懒:)   

  // 编译行: CL   timer2.cpp   /link   winmm.lib    

  #include   <twindows.h>  

  #include   <tstdio.h>    

  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   <windows.h>  

  #include   <stdio.h> 

  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  

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值