度量一个程序的执行时间通常有哪几种方法,计量时间的方法
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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。