计算程序运行时间timetclockt_第1页
计算程序运行时间timetclockt_第2页
计算程序运行时间timetclockt_第3页
计算程序运行时间timetclockt_第4页
计算程序运行时间timetclockt_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、计算程序运行时间time t clock t计算程序运行时间(time_t,clock_t)转载我们有时需要得到程序的运行时间,但我们也要知道,根本不可能精 确测量某一个程序运行的确切时间,文献中说的很明白,现摘录如下。我们平时常用的测量运行时间的方法并不是那么精确的,换句话说,想精 确获取程序运行时间并不是那么容易的。也许你会想,程序不就是一条条指令 么,每一条指令序列都有固定执行时间,为什么不好算?真实情况下,我们的计算机并不是只运行一个程序的,进程的切换,各种中断,共享的多用户,网络 流量,高速缓存的访问,转移预测等,都会对计时产生影响。文献中还提到:对于进程调度来讲,花费的时间分为两部

2、分,第一是计时 器中断处理的时间,也就是当且仅当这个时间间隔的时候,操作系统会选择, 是继续当前进程的执行还是切换到另外一个进程中去。第二是进程切换时间, 当系统要从进程A切换到进程B时,它必须先进入内核模式将进程 A的状态保 存,然后恢复进程B的状态。因此,这个切换过程是有内核活动来消耗时间的。 具体到进程的执行时间,这个时间也包括内核模式和用户模式两部分,模式之 间的切换也是需要消耗时间,不过都算在进程执行时间中了。那么有哪些方法能统计程序的运行时间呢 ?通过查找一些资料并结合自己的 实践体会,摘录和总结了下面几种方法。一、Linux 的 time 命令Linux系统下统计程序运行实践最简

3、单直接的方法就是使用time命令,文献1,2中详细介绍了 time命令的用法。此命令的用途在于测量特定指令执行 时所需消耗的时间及系统资源等资讯,在统计的时间结果中包含以下数据:实际时间(real time):从命令行执行到运行终止的消逝时间;用户CPU寸间(user CPU time):命令执行完成花费的系统 CPU寸间,即命 令在用户态中执行时间的总和;系统CPU寸间(system CPU time):命令执行完成花费的系统 CPU寸间,即 命令在核心态中执行时间的总和。其中,用户CPU寸间和系统CPU寸间之和为CPU寸间,即命令占用CPU执 行的时间总和。实际时间要大于 CPU寸间,因为

4、Linux是多任务操作系统,往 往在执行一条命令时,系统还要处理其他任务。另一个需要注意的问题是即使 每次执行相同的命令,所花费的时间也不一定相同,因为其花费的时间与系统 运行相关。二、间隔计数上面介绍的time命令能测量特定进程执行时所消耗的时间,它是怎么做到 的呢?操作系统用计时器来记录每个进程使用的累计时间,原理很简单,计时器 中断发生时,操作系统会在当前进程列表中寻找哪个进程是活动的,一旦发现 进程A正在运行立马就给进程 A的计数值增加计时器的时间间隔(这也是引起较 大误差的原因)。当然不是统一增加的,还要确定这个进程是在用户空间活动还 是在内核空间活动,如果是用户模式,就增加用户时间

5、,如果是内核模式,就 增加系统时间。这种方法的原理虽然简单但不精确。如果一个进程的运行时间 很短,短到和系统的计时器间隔一个数量级,用这种方法测出来的结果必然是 不够精确的,头尾都有误差。不过,如果程序的时间足够长,这种误差有时能 够相互弥补,一些被高估一些被低估,平均下来刚好。从理论上很难分析这个 误差的值,所以一般只有程序达到秒的数量级时用这种方法测试程序时间才有 意义。这种方法最大的优点是它的准确性不是非常依赖于系统负载。实现方法之一就是上面介绍的time命令,之二是使用tms结构体和times 函数。在Linux中,提供了一个times函数,原型是这个tms的结构体为struct tm

6、sclock_t tms_utime ; /user time clock_t tms_stime ; /system time clock_t tms_cutime ; /user time of reaped children clock_t tms_cstime /system time of reaped childre n这里的cutime和cstime,都是对已经终止并回收的时间的累计,也就是 说,times不能监视任何正在进行中的子进程所使用的时间。使用times函数需要包含头文件sys/times.h 。三、周期计数为了给计时测量提供更高的准确度,很多处理器还包含一个运行在始终

7、周 期级别的计时器,它是一个特殊的寄存器,每个时钟周期它都会自动加1。这个周期计数器呢,是一个64位无符号数,直观理解,就是如果你的处理器是 1GHZ勺,那么需要570年,它才会从2的64次方绕回到0,所以你大可不必考 虑溢出的问题。但是这种方法是依赖于硬件的。首先,并不是每种处理器都有 这样的寄存器的;其次,即使大多数都有,实现机制也不一样,因此,我们无 法用统一的,与平台无关的接口来使用它们。这下,就要使用汇编了。当然, 在这里实际用的是C语言的嵌入汇编:void counter(unsigned*hi,unsigned*lo)asm("rdtsc; movl%edx,%;mov

8、l%eax,%'1 : "=r"(*hi),"=r"(*lo): : "%edx","%eax") ; 第一行的指令负责读取周期计数器,后面的指令表示将其转移到指定地点 或寄存器。这样,我们将这段代码封装到函数中,就可以在需要测量的代码前 后均加上这个函数即可。最后得到的hi和lo值都是两个,除了相减得到间隔值外,还要进行一些处理,在此不表。不得不提出的是,周期计数方式还有一个问题,就是我们得到了两次调用 counter之间总的周期数,但我们不知道是哪个进程使用了这些周期,或者说 处理器是在内核还是在用户模

9、式中。间隔计数的好处就是它是操作系统控制给 进程计时的,我们可以知道具体哪个进程呢个模式;但是周期计数只测量经过 的时间,他不管是哪个进程使用的。所以,用周期计数的话必须很小心。举个 例子:double time()start_c oun ter();P();get_c oun ter();这样一段程序,如果机器的负载很重,会导致p运行时间很长,而其实p函数本身是不需要运行这么长时间的,而是上下文切换等过程将它的时间拖长 了。而且,转移预测和高速缓存的命中率,对这个计数值也会有影响。通常情 况下,为了减少高速缓存不命中给我们程序执行时间带来的影响,可以执行这 样的代码:double time_

10、warm(void)P();start_c oun ter();P();get_c oun ter();它让指令高速缓存和数据高速缓存都得到了warm-up。接下来又有问题。如果我们的应用是属于那种每次执行都希望访问新的数据的那种呢?在这种情况下,我们希望让指令高速缓存warm-up,而数据高速缓存不能warm-up,很明显,time-warm函数低估我们的运行时间了。进一步修改:double time_cold(void)p();clear_cache();start_c oun ter();p();get_c oun ter();注意,程序中加入了一个清除数据缓存的函数,这个函数的具体实现

11、很简单,依情况而定,比如举个例子:volatile int tmp ;static int dummyN ; N是需要清理缓存的字节数void clear_cache(void)int i,sum=0for(i=1 ; i N ; i+)dummyi=2 ;for(i=1 ; i N ; i+)sum+=dummyi;tmp=sum具体原理很简单,定义一个数组并在其上执行一个计算,计算过程中的数 据会覆盖高速数据缓存中原有的数据。每一次的store和load都会让高速数据缓存cache这个数组,而定义为volatile 的tmp则保证这段代码不会被优化。这样做,是不是就万无一失了呢?不是的,因

12、为大多数处理器,L2高速缓 存是不分指令和数据的,这样 clear_cache会让所有p的指令也被清除,只不 过:L1缓存中的指令还会保留而已。其实上面提到的诸多原因,都是我们不能控制的,我们无法控制让高速缓 存去加载什么,不去加载什么,加载时去掉什么。保留什么。而且,这些误差 通常都是会过高估计真实的运行时间。那么具体使用时,有没有什么办法来改 善这种情况呢?有,就是The K-Best Measurement Scheme。这其实很麻烦,所 以在具体实践中都不用它。四、gettimeofday函数计时gettimeofday是一个库函数,包含在time.h中。它的功能是查询系统时 钟,以确

13、定当前的日期和时间。相对于间隔计数的小适用范围和周期计数的麻 烦性,gettimeofday是一个可移植性更好相对较准确的方法。它的原型如下:struct timevallong tv_sec ; / 秒域long tv_usec ; II 微妙域int gettimeofday(struct timeval*tv,NULL);这个机制呢,具体的实现方式在不同系统上是不一样的,而且具体的精确 程度是和系统相关的:比如在 Linux下,是用周期计数来实现这个函数的,所 以和周期计数的精确度差不多,但是在 Win dows NT下,是使用间隔计数实现的, 精确度就很低了。具体使用,就是在要计算运行

14、时间的程序段之前和之后分别加上 gettimeofday(&tvstart,NULL)、gettimeofday(&tvend,NULL),然后计算:(tve nd.tv_sec-tvstart.tv_sec)+(tve nd.tv_usec- tvstart.tv_usec)/1000000就得到了以秒为单位的计时时间。五、clock函数clock也是一个库函数,仍然包含在time.h中,函数原型是:clock_t clock(void) ;功能:返回自程序开始运行的处理器时间,如果无可用信息,返回-1。转换返回值若以秒计需除以CLOCKS_PER_SECON注:如果编译器是

15、POSIX兼容 的, CLOCKS_PER_SECOND为 1000000。)使用clock函数也比较简单:在要计时程序段前后分别调用clock函数,用后一次的返回值减去前一次的返回值就得到运行的处理器时间,然后再转换 为秒。举例如下:double totaltimestarttime=clock()en dtime=clock();totaltime=(double)(e ndtime-starttime)/(double)CLOCKS_PER_SEC);六、time函数在time.h中还包含另一个时间函数:time。文献对其进行了详细的介绍。通过time()函数来获得日历时间(Calend

16、ar Time),其原型为:time_t time(time_t*timer)。通过difftime 函数可以计算前后两次的时间差:double difftime(time_t time1,time_t timeO)。用 time_t 表示的时间(日历时间)是从一个时间点(例如:1970年1月1日0时0分0秒)到此时的秒数,则此函数 的前后两次时间差也是以秒为单位。比如:time_t startT,e ndT ;double totalT ;startT=time(NULL);en dT=time(NULL);totalT=difftime(startT,e ndT)关于此函数的其他应用请参见

17、文献总结:使用相应的方法,调用相应的函数,还需要关注它们可以表示的范围和精 度,这样才能"挑肥拣瘦"。先来看看时间函数中经常用到的两个数据类型的定 义:clock_t 的定义#ifndef _CLOCK_T_DEFINED typedef long clock_t ; #define _CLOCK_T_DEFINED#e ndiftime_t 的定义#ifndef _TIME_T_DEFINED typedef long time_t ;#defi ne _TIME_T_DEFINED#endif long 型数据的取值范围是-2147483648+2147483647 所以, gettimeofday函数取得的时间最大值为 2147483647+2147483647/1000000=2147485794.483647 s,大约为 68.096 年; clock函数取得的时间最大值为 2147483647

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论