测试程序执行时间

      本文介绍两种测试程序时间的方法,一种是通过间隔计数,另一种通过周期计数器。针对这两种方法,分别给出Windows和 UNIX 下的实现。

       首先介绍这两种方法的含义,摘自《深入理解计算机系统》。间隔计数:操作系统维护者每个进程使用的用户时间量和系统时间量的计数值,当计时器中断发生时,操作系统会确定哪个进程是活动的,并且对那个进程的一个计数值增加计时器间隔时间。如果系统是在内核模式中执行的,那么就增加系统时间,否则增加用户时间。这种方法一般使用clock函数实现;周期计数器:处理器内部包含一个运行在时钟周期级的计数器,每个时钟周期它都会家1。可以利用特殊的机器指令来读这个计数器的值。如果要测试一段代码的时间,只需在代码段前后分别获取计数器的值,然后将这两个计数器的值相减,除以处理器频率,就可以得到这段代码的运行时间。

       当然了,这里给出的方法适用于负载轻的系统,要在负载很重的系统获得准确的计时本身就特别困难。另外程序的运行时间受到上下文切换、高速缓存、分支预测等的影响,所以本文介绍的测试方法及实现仅仅是一种参考,尚需完善。

      下面给出Windows中的实现,一共有三个版本,都用C++实现并进行了封装。第一、二种都是属于周期计数器法,第三种是间隔计数法。三个版本的使用方式一样,除了版本二需要设置处理器频率。如下所示:

[cpp] view plaincopyprint?
  1. Runtime rt;  
  2. //rt.SetHZ(2327500000.0); //版本二需设置处理器频率,我电脑的CPU频率是2.33GHz  
  3. rt.Begin();      //代码段执行前  
  4. P();             //要测试的代码段  
  5. rt.End();        //代码段执行后  
  6. rt.GetRuntime(); //获得运行时间  

     版本一,利用Windows提供的API函数实现。这种方法的精度非常高。属于周期计数器法。

[cpp] view plaincopyprint?
  1. #include <windows.h>  
  2. class Runtime  
  3. {  
  4. private:  
  5.     LARGE_INTEGER m_begin;       
  6.     LARGE_INTEGER m_end;   
  7. public:  
  8.     inline void Begin() { QueryPerformanceCounter(&m_begin); }  
  9.     inline void End() { QueryPerformanceCounter(&m_end); }  
  10.     inline double GetRuntime()  
  11.     {  
  12.         LARGE_INTEGER frequency;       
  13.         QueryPerformanceFrequency(&frequency); //获得机器内部计时器的时钟频率  
  14.         return (double)(m_end.QuadPart - m_begin.QuadPart)/frequency.QuadPart;  
  15.     }  
  16. };  

     版本二,直接用汇编代码实现,利用的是IA32的周期计数器。因此局限于IA32架构。属于周期计数器法。

[cpp] view plaincopyprint?
  1. #include <ctime>  
  2. using namespace std;  
  3.   
  4. class Runtime  
  5. {  
  6. private:  
  7.     unsigned m_hi, m_lo;  
  8.     unsigned m_nhi, m_nlo;  
  9.     double m_hz;  
  10. public:  
  11.     inline void SetHZ(double hz) { m_hz = hz; }  
  12.     inline void Begin()   
  13.     {   
  14.         unsigned hi, lo;  
  15.         __asm{   
  16.             rdtsc;   
  17.             mov hi,edx;  
  18.             mov lo,eax   
  19.         };   
  20.         m_hi = hi;  
  21.         m_lo = lo;  
  22.     }  
  23.     inline void End()   
  24.     {   
  25.         unsigned hi, lo;  
  26.         __asm{   
  27.             rdtsc;   
  28.             mov hi,edx;   
  29.             mov lo,eax   
  30.         };   
  31.         m_nhi = hi;  
  32.         m_nlo = lo;  
  33.     }  
  34.     inline double GetRuntime()   
  35.     {  
  36.         unsigned hi, lo, borrow;  
  37.         lo = m_nlo - m_lo;           //低位差  
  38.         borrow = lo > m_nlo;         //需向高位借位  
  39.         hi = m_nhi - m_hi - borrow;  //高位差  
  40.         return (hi * (1 << 30) * 4 + lo)/m_hz;  
  41.     }  
  42. };  

     版本三,利用ANSI C定义的clock函数实现,测量程序总的运行时间。注意一点clock函数返回的是时钟滴答,为了表示成秒数需除以CLOCK_PER_SEC。

[cpp] view plaincopyprint?
  1. #include <ctime>  
  2. using namespace std;  
  3.   
  4. class Runtime  
  5. {  
  6. private:  
  7.     clock_t m_begin;       
  8.     clock_t m_end;   
  9. public:  
  10.     inline void Begin() { m_begin = clock(); }  
  11.     inline void End() { m_end = clock(); }  
  12.     inline double GetRuntime() { return (m_end - m_begin)/ (double)CLOCKS_PER_SEC; }  
  13. };  

      给完Windows的实现,接着来介绍UNIX下的实现。第一种为周期计数器法,第二种为间隔计数法,第三种利用gettimeofday函数实现。三个版本的使用方式一样,除了版本1需定义处理器频率。如下所示:

[cpp] view plaincopyprint?
  1. begin_time();  
  2. P();   
  3. end_time();  
  4. get_runtime();  

     版本一,直接用汇编代码实现,利用的是IA32的周期计数器。因此局限于IA32架构。属于周期计数器法。

[cpp] view plaincopyprint?
  1. #include <time.h>  
  2. #include <unistd.h>  
  3.   
  4. #define CPUHZ 2327500000.0   //处理器频率  
  5. unsigned cyc_hi = 0, cyc_lo = 0;  
  6. unsigned ncyc_hi = 0, ncyc_lo = 0;  
  7.   
  8. void access_counter(unsigned *hi, unsigned *lo)  
  9. {  
  10.     asm("rdtsc; movl %%edx,%0; movl %%eax,%1"  
  11.             : "=r" (*hi), "=r" (*lo)  
  12.             :  
  13.             : "%edx""%eax");  
  14. }  
  15. void begin_time() { access_counter(&cyc_hi, &cyc_lo); }  
  16. void end_time() { access_counter(&ncyc_hi, &ncyc_lo); }  
  17. double get_runtime()  
  18. {  
  19.     unsigned hi, lo, borrow;  
  20.     lo = ncyc_lo - cyc_lo;  
  21.     borrow = lo > ncyc_lo;  
  22.     hi = ncyc_hi - cyc_hi - borrow;  
  23.     return ((double)hi * (1 << 30) * 4 + lo)/ CPUHZ;  
  24. }  

     版本二,利用ANSI C定义的clock函数实现,测量程序总的运行时间。

[cpp] view plaincopyprint?
  1. #include <time.h>  
  2. clock_t begin, end;  
  3. void begin_time() { begin = clock(); }  
  4. void end_time() { end = clock(); }  
  5. double get_runtime() { return (end - begin)/ (double)CLOCKS_PER_SEC; }  

     版本三,利用gettimeofday函数实现,弥补了IA32周期计数器只能在IA32 系统上工作的缺陷。

[cpp] view plaincopyprint?
  1. #include <sys/time.h>  
  2. #include <unistd.h>  
  3. struct timeval begin;  
  4. struct timeval end;  
  5.   
  6. void begin_time() { gettimeofday(&begin, NULL); }  
  7. void end_time() { gettimeofday(&end, NULL); }  
  8. double get_runtime()  
  9. {  
  10.     long sec, usec;  
  11.     sec = end.tv_sec - begin.tv_sec;  
  12.     usec = end.tv_usec - begin.tv_usec;  
  13.     return (double)sec + 1e-6 * usec;  
  14. }  




转自:http://blog.csdn.net/wuzhekai1985/article/details/6686694

你可能感兴趣的:(测试程序执行时间)