度量一个程序的执行时间通常有哪几种方法,计量时间的方法

  度量一个程序的执行时间通常有哪几种方法,计量时间的方法

  GetTickCount()和GetCurrentTime()都只精确到55ms(1个tick就是55ms)。要精确到毫秒,应该使用timeGetTime函数或QueryPerformanceCounter函数。具体例子请参考QA 001022《VC中VC使用高精度定时器》,QA001813《如何在Windows中实现精确定时》,QA 004842《time gettime函数延时不准确》。

  GetTickCount本身不够精确,无法实现真正的毫秒级功能。

  虽然GetTickCount返回值的单位是1ms,但实际上它的精度只有10ms左右。(我上网搜了GetTickCount()函数。有两个精度,一个是55ms,一个是10 ms左右,哪个准确?我不知道)

  如果想提高精度,可以使用QueryPerformanceCounter和QueryPerformanceFrequency。并非每个系统都支持这两个功能。对于支持它们的系统,可以获得小于1ms的精度。Windows中有一个精度非常高的定时器,精度在微秒级,但是这个定时器的频率在不同的系统中是不一样的,这个频率可能跟硬件和操作系统都有关系。这个定时器的频率可以通过使用API函数QueryPerformanceFrequency获得。API函数QueryPerformanceCounter可用于获取计时器的当前值。根据需要延时的时间和定时器的频率,可以计算出定时器需要延时的周期数。在循环中,QueryPerformanceCounter不断读出定时器值,直到经过了指定的循环数,循环才结束,从而达到高精度延时的目的。

  按如下方式编写函数:

  浮动时间()

  {

  static _ _ int 64 start=0;

  静态__int64频率=0;

  if (start==0)

  {

  QueryPerformanceCounter((LARGE _ INTEGER *)start);

  QueryPerformanceFrequency((LARGE _ INTEGER *)频率);

  返回0.0f

  }

  _ _ int 64 counter=0;

  QueryPerformanceCounter((LARGE _ INTEGER *)计数器);

  return (float)(反启动)/double(频率));

  }

  希望这有所帮助,

  谦信

  我不知道我在下面论坛看到的转帖的作者名字。

  对于关注性能的程序开发人员来说,好的计时组件既是好朋友,也是好老师。定时器可以作为一个程序组件,帮助程序员精确控制程序进程,也是一个强大的调试武器。有经验的程序员可以尽快确定程序的性能瓶颈,或者在不同算法之间进行令人信服的性能比较。

  在Windows平台下,常用的定时器有两种,一种是timeGetTime多媒体定时器,可以提供毫秒级的定时。但是这种精度对于许多应用来说还是太粗糙了。另一个是QueryPerformanceCount计数器,可以提供与不同系统的微秒计数。对于处理实时图形处理,多媒体数据流处理,或者实时系统构造的程序员来说,用好QueryPerformanceCount/QueryPerformanceFrequency是一项基本功。

  本文介绍了另一种直接利用奔腾CPU内部时间戳的高精度计时方法。以下讨论主要受益于本书《Windows图形编程》,第15-17页。有兴趣的读者可以直接参考这本书。有关RDTSC指令的详细讨论,请参考英特尔产品手册。本文只为抛砖。

  在Intel Pentium及以上的CPU中,有一个叫做“时间戳”的组件,它以64位无符号整数的格式记录CPU上电以来的时钟周期数。因为现在的CPU频率很高,这个组件可以达到纳秒级的计时精度。这种精度是以上两种方法无法比拟的。

  在奔腾以上的CPU中,提供了一个机器指令RDTSC(读时间戳计数器)来读取这个时间戳的数目,并将其保存在EDX:EAX寄存器对中。因为EDX:EAX寄存器对恰好是Win32平台上存储C语言中某个函数返回值的寄存器,所以我们可以把这个指令看成一个普通的函数调用。像这样:

  内联无符号__int64 GetCycleCount()

  {

  __asm RDTSC

  }

  但是,不行,因为C的嵌入式汇编程序不直接支持RDTSC,所以我们要直接嵌入_emit伪指令的机器码形式0X0F和0X31,如下:

  内联无符号__int64 GetCycleCount()

  {

  __asm _emit0x0F

  __asm _emit0x31

  }

  将来需要计数器时,可以调用GetCycleCount函数两次,就像使用通用的Win32 API一样,比较两个返回值之间的差异,如下所示:

  无符号长t;

  t=(无符号长整型)GetCycleCount();

  //做一些时间密集的事情.

  t -=(无符号长整型)GetCycleCount();

  055-79000第15页写了一个类来封装这个计数器。有兴趣的读者可以参考那个类的代码。为了使计时更加准确,作者做了一点改进,通过连续两次调用GetCycleCount函数来计算和节省RDTSC指令的执行时间,然后在每次计时后从实际计数中减去这一小段时间,从而得到更加准确的计时数字。但我个人认为这一点点改进意义不大。在我的机器上测量,这条指令需要几十到100多个周期,在赛扬800MHz机器上只有十分之一微秒。对于大多数应用来说,这个时间完全可以忽略不计;然而,这种补偿对于那些精确到纳秒的应用来说太粗糙了。

  这种方法的优点是:

  1.精度高。可以直接达到纳秒级的计时精度(1GHz CPU上每个时钟周期为一纳秒),这是其他计时方式无法企及的。

  2.成本低。TimeGetTime函数需要链接多媒体库winmm.lib,QueryPerformance*函数需要硬件支持(虽然我还没看到不支持的机器)和内核库支持,所以两者都只能在Windows平台下使用(DOS平台下的高精度计时请参考《Windows图形编程》,里面有控制timer 8253的详细说明)。但RDTSC指令是CPU指令,i386平台上所有奔腾以上机器都支持,甚至不受平台限制(相信这种方法在i386 UNIX和Linux下也适用,但没有条件测试),函数调用开销极小。

  3.它与CPU主频有直接对应的速率关系。一次计数相当于1/(CPU主频的Hz数)秒,所以只要知道CPU的主频,就可以直接计算出时间。这与QueryPerformanceCount不同,QueryPerformanceCount需要通过QueryPerformanceFrequency获取当前计数器每秒的计数次数,然后才能转换为时间。

  这种方法的缺点是:

  1.现有的C/C编译器大多不直接支持RDTSC指令的使用,直接嵌入机器码编程比较麻烦。

  2.数据抖动严重。事实上,对于任何测量方法来说,准确性和稳定性永远是一对矛盾。如果用低精度的timeGetTime进行计时,基本上每次计时的结果都是一样的;然而,RDTSC指令的结果每次都不一样,往往有数百甚至数千个缺口。这是这种方法的高精度的内在矛盾。

  关于这种方法的最大长度,我们可以简单地用下面的公式来计算:

  CPU通电后的秒数rate读取的周期数/CPU主频(Hz)

  一个64位无符号整数所能表示的最大数是1.810 ^ 19,在我的赛扬800上可以计时700年左右(书上说在200MHz奔腾上可以计时117年。我不知道这个数字是怎么推导出来的,和我的计算不一样)。无论如何,我们不必在意溢出。

  下面举几个小例子,简单比较一下三种计时方式的用法和准确度。

  //Timer1.cpp使用RDTSC指令的Timer类的定义//KTimer类可以在《图形程序开发人员指南》 P15中找到。

  //编译该行:cltimer1.cpp/linkuser32.lib.

  #包含stdio.h

  #包含“KTimer.h”

  主()

  {

  无符号t;

  KTimer定时器;

  计时器。start();

  睡眠(1000);

  t=定时器。stop();

  printf(持续时间:%d\n ,t);

  }

  //Timer2.cpp使用timeGetTime函数。

  //mmsys.h需要包含,但是由于Windows头文件关系复杂

  //单纯包含windows.h就是懒:)

  //编译该行:cltimer2.cpp/linkwinmm.lib.

  #包含windows.h

  #包含stdio.h

  主()

  {

  双字t1,T2;

  t1=time gettime();

  睡眠(1000);

  T2=time gettime();

  printf(开始时间:%u\n ,t1);

  printf(结束时间:%u\n ,T2);

  printf(持续时间:%u\n ,(T2-t1));

  }

  //Timer3.cpp使用QueryPerformanceCounter函数。

  //编译行:CL timer3.cpp /link KERNEl32.lib

  #包含windows.h

  #包含stdio.h

  主()

  {

  LARGE_INTEGER t1,t2,TC;

  QueryPerformanceFrequency(TC);

  printf(频率:%u\n ,tc。quad part);

  QueryPerformanceCounter(t1);

  睡眠(1000);

  QueryPerformanceCounter(T2);

  printf(开始时间:%u\n ,t1。quad part);

  printf(结束时间:%u\n ,t2。quad part);

  printf(持续时间:%u\n ,(t2。四部分- t1。quad part));

  //这里要加上这句话来计算时间(秒)。

  doubledTotalTime=(double)(t2。四部分-t1。四部分)/(双精度)tc。QuadPart//秒

  Printf(耗时:%f\n ,dTotalTime);

  }

  ////////////////////////////////////////////////

  //以上三个样本程序都是在测试睡眠1秒的时间。

  文件://测试/测试环境:赛扬800MHz/256M SDRAM

  //Windows 2000专业版SP2

  //微软Visual C 6.0 SP5

  ////////////////////////////////////////////////

  下面是定时器1使用高精度RDTSC指令的运行结果。

  持续时间:804586872

  下面是Timer2的运行结果,使用最粗糙的timeGetTime API。

  开始时间:20254254

  结束时间:20255255

  持续时间:1001

  下面是使用QueryPerformanceCount API运行Timer3的结果

  频率:3579545

  开始时间:3804729124

  结束时间:3808298836

  持续时间:3569712

  网上有一种说法是

  double dTotalTime=(double)(t2。四部分-t1。四部分)/(双精度)tc。四部分

  可能有问题。比如现在很多主板都有CPU频率自动调节的功能,主要是为了节能,尤其是笔记本,精度无法保证。我不确定这个说法是否准确,供大家研究。

  以上主要取自《Windows图形编程》。其实除了上面说的三种方法,还有一种常用的方法没有上面的准确,那就是使用GetTickCount函数。这种方法可以获得毫秒级的时间,具体用法如下:

  DWORDstartTime=GetTickCount();

  //dosomething

  DWORDtotalTime=GetTickCount()-start time;其中,不同的编译器对Sleep()函数的使用是不同的。我在网上搜了一下,解释如下:

  功能名称:睡眠

  能量:暂停执行一段时间。

  方法:无符号睡眠(无符号秒);

  在VC中使用头文件

  #包含windows.h

  在gcc编译器中,不同的gcc版本使用不同的头文件。

  #包括unistd.h

  注意,VC中Sleep的第一个英文字符是大写的‘s’

  在标准C语言中,它是Sleep,但是不要大写.下面的描述是大写的,这取决于你使用什么编译器。简单来说,VC用睡眠,其他都用睡眠。

  睡眠功能的一般形式:

  睡眠(未签名长);

  其中,Sleep()中的单位是毫秒,所以如果你想让函数停留1秒,应该是Sleep(1000);

  在Linux下,睡眠中的“s”是不写的。

  sleep()中的单位是秒,而不是毫秒。例子如下:

  #包含windows.h

  int main()

  {

  int a;

  a=1000

  睡眠(一);

  返回0;

  }

  参考资料:

  055-79000作者:张燕_qd

  055-79000,作者冯远

  《使用CPU时间戳进行高精度计时》,http://www.cppblog.com/humanchao/archive/2008/04/22/43322 . html

  出发地:http://www.dakaren.com/index.php/archives/768.htm

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: