首页 测试 体会 查看内容

CSI-V:测试程序的执行时间

2014-4-25 15:52| 发布者: peter_zhang| 查看: 380| 评论: 0

摘要:   前言  人们经常会问:“程序X在机器Y上运行得有多快?”,而我们一般的回答都是给定一个可以估算出该程序执行时间的一个大概的描述,比如:程序在N分钟跑出了多少的数据;据此我们可以推测程序的运行性能。而 ...
static unsignedcyc_hi = 0;static unsignedcyc_lo = 0;//这个方法使用rdtsc指令将edx和eax的值放置到指定的变量中voidaccess_counter(unsigned *hi,unsigned* lo){__asm{CPUIDRDTSCmov ebx,himov [ebx],edxmov ebx,lomov [ebx],eax}}//这个方法记录当前周期计数器的值,为计算做准备voidstart_counter(){access_counter(&cyc_hi,&cyc_lo);}//这个方法返回自上次调用start_counter后经历的时钟周期数doubleget_counter(){unsigned ncyc_hi,ncyc_lo;unsigned hi,lo,borrow;double result;access_counter(&ncyc_hi,&ncyc_lo);lo = ncyc_lo-cyc_lo;borrow = lo>ncyc_lo;hi=ncyc_hi-cyc_hi-borrow;result = (double)hi*(1<<30)*4+lo;if(result<0){fprintf(stderr,"Error:counterreturns neg value %.0fn",result);}return result;}//测量处理器的主频double ghz(intverbose,int sleeptime){double rate;start_counter();Sleep(sleeptime);//note:Windows API Sleep的参数以ms为单位rate = get_counter()/(1e6*sleeptime);if(verbose)printf("processor clock rate= %.1fGhzn",rate);return rate;}  当然,有了以上的C接口,我们也可以这样使用它们来测量我们的程序段:int main(){start_counter();int sum = 0,i;for(i=0;i<20000;i++){sum+=i;}printf("cycle counter:%.1fn",get_counter());getchar();return 0;}  其实,在windows API提供了类似的方法来获取高精度的时间值。这个方法的官方定义和声明如下:  BOOLQueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount);  这个方法返回当前高精度性能计数器的值。其中LARGE_INTEGER结构的定义如下:typedef union _LARGE_INTEGER {struct {DWORD LowPart;LONG  HighPart;};LONGLONG QuadPart} LARGE_INTEGER, *PLARGE_INTEGER;  从结构上来看,其相关的值也是一个64位的周期计数器。可以推测该API也是使用周期计数的方式来测量时间的。同时,跟其相关的另一个API :QueryPerformanceFrequency  BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrenquency);  这个方法返回高精度性能计数器每秒的计数值。如果硬件不支持,函数调用就会失败。  但是,需要注意的是,在多处理器计算机上,我们需要将测试程序所在的线程绑定在某一CPU上,而避免其在cpu间进行迁移,这是由于在多cpu系统中,不同的CPU由RDTSC指令读取到的CPU周期数可能不同,用两个不同的CPU得到的周期数做计算会得到无意义的值。因此,我们需要用到SetThreadAffinityMask方法将某一线程和CPU绑定,其相关内容介绍请参见MSDN。  在linux系统中,也提供了一个高精度的计时方法clock_gettime,其原型为:int clock_gettime(clockid_t clk_id, struct timespect *tp);clockid_t 用于指定计时时钟的类型,timespec结构为:struct timespec{Time_t tv_sec;Long tv_nsec ;}  注:该方法提供了纳秒级的精度值。  上下文切换的影响  可能我们已经认为,使用周期计数器测量程序的执行时间将会非常接近程序的实际运行时间,但实际上是不一定的。想象这样情况,如果在两次调用计数器例程之间,有另外某个进程执行了,或者如果是机器负载很重,程序段的运行时间特别长,这将导致测量到的时间和实际需要的运行时间差距很大。这是由于在进程切换中间导致额外的时间产生,如果负载过重,进程切换过于频繁,那么执行时间的差异就会越大。  高速缓存和其他因素的影响  在前面我们提到过高速缓存和分支预测也会对计时造成一定的影响。这里我简单的说明下。  一个程序在第一次执行中,其所需要的数据并没加载到高速缓存(cache-1和cache-2,或者叫片内和片外高速缓存),这时程序执行会从主存中加载数据。而再最近的时间内再一次执行该程序时就从高速缓存加载,而我们知道从高速缓存加载的速度要比内存块10倍左右。所以导致测量的结果出现误差。关于分支预测,这里涉及到微处理硬件,现代大多的处理器都可以对指令进行冒险加载,即对一个判断分支提前做出预测,如果预测正确就继续执行,否则,会带来一定的时间处罚。当然不管是高速缓存还是分支预测对于我们测量程序执行时间相对来说都很小。
  4.周期计数器  为了给计时测量提供更高的精确度,许多处理器还包含一个运行在时钟周期级的计时器。这个计时器是一个特殊的寄存器,每个时钟周期它都会加1.可以用特殊的机器指令来读取这个值。  IA32周期计数器  在IA32体系结构中,周期计数器是一个64位无符号数。对于一个运行时钟为1GHz的处理器,每570年,这个计数器才会从264-1绕回到0。IA32计数器使用rdtsc(readtimestamp counter,读时间戳计数器)指令来访问的。这条指令没有参数。它将寄存器%edx设置为计数器的高32位,而寄存器%eax设置为低32位。据此我们可以提供一个C程序的接口,来实现使用周期计数的方式测量程序的执行时间。  在这里,我参考书中代码给出了windows下的测量接口,需要注意的是并不是所有的处理器都有支持类似的计数器,因此这是依赖于硬件平台的。

鲜花

握手

雷人

路过

鸡蛋

扫一扫关注最新动态

毒镜头:老镜头、摄影器材资料库、老镜头样片、摄影
爱评测 aipingce.com  
返回顶部