【问题标题】:Code execution time in LinuxLinux中的代码执行时间
【发布时间】:2014-03-11 01:27:33
【问题描述】:

我试图获取特定代码段(可能是循环或函数等)的执行时间。我听说命令time 或函数clock() 可以完成这项工作。但我的要求是以毫秒/微秒为单位的精度。所以我写了这样的东西。

int main()
{
    struct timeval ts1, ts2;
    long long time1, time2, diff;
    int i,var;

    scanf("%d",&var);
    gettimeofday(&ts1, NULL);
    time1 = (ts1.tv_sec * 1000000) + ts1.tv_usec;

    for (i=0; i<var; i++); // <-- Trying to measure execution time for the loop

    gettimeofday(&ts2, NULL);
    time2 = (ts2.tv_sec * 1000000) + ts2.tv_usec;

    printf("-------------------------\n");
    diff = time2 - time1;
    printf("total %ld microseconds\n", diff);
    printf("%ld seconds\n", diff/1000000);
    diff %= 1000000;
    printf("%ld milliseconds\n", diff/1000);
    diff %= 1000;
    printf("%ld microseconds\n", diff);
    printf("-------------------------\n");
    return 0;
}

我有两个顾虑

  1. 上面的代码是否可靠,是否符合我的意图?我不太确定;)
  2. 当我编译优化级别为 -O2 的代码时,这根本不起作用。我知道 -O2 会化妆,但我怎么看发生了什么?如果我可以选择 1,谁能建议如何恢复 O2 问题?

感谢您的帮助!谢谢。

【问题讨论】:

  • 你是想计算一些代码行之间的时间差,还是获取真正的 CPU 执行时间?
  • 使用clock_gettime。另见here。记得链接-lrt
  • 优化器可能正在移除循环,因为从未使用过i。检查生成的汇编代码。
  • 我正在尝试获取某些代码行的实时运行时间。这就是我在这里尝试的。甚至认为我也对 CPU 执行时间感兴趣..
  • 您需要小心减去那些包含两个分量的时间结构。我在下面的回答中给出了clock_gettime() 使用的timespec 结构的示例,如果您想继续使用它们,可以适应timevals。我最初改编自 a GNU example for timeval structs 的版本。

标签: c++ c linux performance


【解决方案1】:

这个 NanoTimer 类(头文件)应该可以完成这项工作。 使用 startTimer()/stopTimer()。请注意,在此相对分辨率下计算经过的时间需要一些时间,因此如果只执行 startTimer(); 将永远不会有 0 值;停止定时器();中间没有任何代码。 还有很多其他因素会影响经过的时间,所以你应该重复几次具体的措施,取最低的值。

class NanoTimer
{
    struct timespec ts_;
    u_int64_t startTimer_;
    u_int64_t totalTimer_;
public:
    NanoTimer()
    {
        totalTimer_ = 0;
        startTimer_ = 0;
    }

    u_int64_t getNanoSecTimer(void)
    {
        clock_gettime(CLOCK_REALTIME, &ts_);
        return ts_.tv_sec * 1000000000 +  ts_.tv_nsec;
    }

    void startTimer(void)
    {
        startTimer_ = getNanoSecTimer();
    }
    void stopTimer(void)
    {
        //assert(startTimer_ > 0);
        totalTimer_ += getNanoSecTimer() - startTimer_;
        startTimer_ = 0;
    }
    inline u_int32_t getTotalSeconds()
    {
        return totalTimer_/1000000000;
    }
    inline u_int32_t getTotalMilliseconds()
    {
        return totalTimer_/1000000;
    }
    inline u_int32_t getTotalMicroseconds()
    {
        return totalTimer_/1000;
    }
    inline u_int32_t getTotalNanoseconds()
    {
        return totalTimer_;
    }
    inline u_int32_t getCurrentSeconds()
    {
        return (totalTimer_ + (startTimer_ > 0 ? getNanoSecTimer() - startTimer_ : 0)) / 1000000000;
    }
};

【讨论】:

    【解决方案2】:

    您显示的上述代码是获取自gettimeofday() 仅返回挂钟时间以来经过的实时时间。至于不使用优化级别 -O2,请将 i 声明为 volatile int i,这将阻止优化到 i

    【讨论】:

      【解决方案3】:

      您可以使用示例代码!!。该代码不会导致计算成本的开销

      #include <sys/time.h>
      #include <sys/types.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <sys/resource.h>
      
      void timing(double* wcTime, double* cputime)
      {
          struct timeval tp;
      
          gettimeofday(&tp, NULL);
          *wcTime=(double) (tp.tv_sec + tp.tv_usec/1000000.0);
      
          struct rusage ruse;
          getrusage(RUSAGE_SELF, &ruse);
          *cpuTime=(double)(ruse.ru_utime.tv_sec+ruse.ru_utime.tv_usec / 1000000.0);
      }
      

      使用:

      double  wcs,    //  Wall Clock Start
              wce,    //  Wall Clock End
              ccs,    //  CPU Clock Start
              cce;    //  CPU Clock End
      timing(&wcs, &ccs);
      
      //  COMPUTATION CODE
      
      timing(&wce, &cce);
      
      cout << "CPU RUNTIME:       " << cce - ccs << endl
           << "WALL CLOCK TIME:   " << wce - wcs << endl;
      

      【讨论】:

        【解决方案4】:

        可变速率 CPU 时钟和对热余量的利用让我越来越怀疑,对于运行时间不足以加热内核的功能,以秒为单位的挂钟计时可能不如循环计数有用。

        如果我在检测自己的代码,我倾向于使用以下代码:

        static __inline__ uint64_t rdtsc(void)
        {
            uint32_t hi, lo;
            __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
            return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 );
        }
        

        使用它,我可以记录函数调用前后的 TSC 值,将两者相减,得到所用的周期数。

        如果你想要挂钟时间,你可以使用来自time.hclock_gettime(),如果不准确,它会给你纳秒级的分辨率,并使用以下减去两个(之前和之后)struct timespec对象:

        #define NSEC_PER_SEC 1000000000
        static int timespec_subtract(result, x, y)
        struct timespec *result, *x, *y;
        {
            /* Perform the carry for the later subtraction by updating y. */
            if (x->tv_nsec < y->tv_nsec) {
                int nsec = (y->tv_nsec - x->tv_nsec) / NSEC_PER_SEC + 1;
                y->tv_nsec -= NSEC_PER_SEC * nsec;
                y->tv_sec += nsec;
            }
            if (x->tv_nsec - y->tv_nsec > NSEC_PER_SEC) {
                int nsec = (x->tv_nsec - y->tv_nsec) / NSEC_PER_SEC;
                y->tv_nsec += NSEC_PER_SEC * nsec;
                y->tv_sec -= nsec;
            }
        
            /* Compute the time remaining to wait.
               tv_nsec is certainly positive. */
            result->tv_sec = x->tv_sec - y->tv_sec;
            result->tv_nsec = x->tv_nsec - y->tv_nsec;
        
            /* Return 1 if result is negative. */
            return x->tv_sec < y->tv_sec;
        }
        

        也就是说,我倾向于使用 perf 并完全避免插桩。

        【讨论】:

          猜你喜欢
          • 2012-10-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-28
          • 1970-01-01
          相关资源
          最近更新 更多