【问题标题】:How do I measure a time interval in C?如何在 C 中测量时间间隔?
【发布时间】:2010-01-27 21:15:07
【问题描述】:

我想在 C 中测量时间,但我很难弄清楚,我想要的是这样的:

  • 启动计时器
  • 运行方法
  • 停止计时器
  • 报告所用时间(至少精确到微)

任何帮助将不胜感激。

(我正在使用mingw在windows中编译)

【问题讨论】:

标签: c timer


【解决方案1】:

提供 1 微秒分辨率的高分辨率计时器是特定于系统的,因此您必须使用不同的方法在不同的操作系统平台上实现这一点。您可能有兴趣查看以下文章,该文章基于以下描述的功能实现了一个跨平台的 C++ 计时器类:

  • [Song Ho Ahn - 高分辨率计时器][1]

Windows

Windows API 提供了极高分辨率的计时器函数:QueryPerformanceCounter(),它返回当前经过的滴答声,QueryPerformanceFrequency(),它返回每秒滴答声的数量。

例子:

#include <stdio.h>
#include <windows.h>                // for Windows APIs

int main(void)
{
    LARGE_INTEGER frequency;        // ticks per second
    LARGE_INTEGER t1, t2;           // ticks
    double elapsedTime;

    // get ticks per second
    QueryPerformanceFrequency(&frequency);

    // start timer
    QueryPerformanceCounter(&t1);

    // do something
    // ...

    // stop timer
    QueryPerformanceCounter(&t2);

    // compute and print the elapsed time in millisec
    elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
    printf("%f ms.\n", elapsedTime);
}

Linux、Unix 和 Mac

对于基于 Unix 或 Linux 的系统,您可以使用gettimeofday()。该函数在“sys/time.h”中声明。

例子:

#include <stdio.h>
#include <sys/time.h>                // for gettimeofday()

int main(void)
{
    struct timeval t1, t2;
    double elapsedTime;

    // start timer
    gettimeofday(&t1, NULL);

    // do something
    // ...

    // stop timer
    gettimeofday(&t2, NULL);

    // compute and print the elapsed time in millisec
    elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0;      // sec to ms
    elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0;   // us to ms
    printf("%f ms.\n", elapsedTime);
}

【讨论】:

  • 我觉得应该是struct timeval,而不仅仅是timeval。
  • struct timeval 为我工作(一些 GCC ARM 编译器)
  • 在 Android 上报告一个奇怪的大值。当 t2.tv_usec=925311 和 t1.tv_usec=861102 时,elapsedTime 报告为 262144000。这是在添加“struct”之后,如 rightaway717 所述。
  • 这个解决方案是针对C++的,但是问题是针对C的。为什么这个会被接受...?
  • 我需要一个 C 版本。这可能吗?
【解决方案2】:

在 Linux 上你可以使用clock_gettime():

clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start); // get initial time-stamp

// ... do stuff ... //

clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);   // get final time-stamp

double t_ns = (double)(end.tv_sec - start.tv_sec) * 1.0e9 +
              (double)(end.tv_nsec - start.tv_nsec);
                                                 // subtract time-stamps and
                                                 // multiply to get elapsed
                                                 // time in ns

【讨论】:

【解决方案3】:

以下是一组基于 gettimeofday() 系统调用的用于定时器管理的通用 C 函数。所有的计时器属性都包含在一个单一的 ticktimer 结构中 - 您想要的时间间隔、自计时器初始化以来的总运行时间、指向您想要调用的所需回调的指针、回调被调用的次数。回调函数如下所示:

void your_timer_cb (struct ticktimer *t) {
  /* do your stuff here */
}

要初始化和启动计时器,请调用 ticktimer_init(your_timer, interval, TICKTIMER_RUN, your_timer_cb, 0)。

在程序的主循环中调用ticktimer_tick(your_timer),它将决定是否经过适当的时间来调用回调。

要停止计时器,只需调用 ticktimer_ctl(your_timer, TICKTIMER_STOP)。

ticktimer.h:

#ifndef __TICKTIMER_H
#define __TICKTIMER_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

#define TICKTIMER_STOP         0x00
#define TICKTIMER_UNCOMPENSATE 0x00
#define TICKTIMER_RUN          0x01
#define TICKTIMER_COMPENSATE   0x02

struct ticktimer {
  u_int64_t tm_tick_interval;
  u_int64_t tm_last_ticked;
  u_int64_t tm_total;
  unsigned ticks_total;
  void (*tick)(struct ticktimer *);
  unsigned char flags;
  int id;
};

void ticktimer_init (struct ticktimer *, u_int64_t, unsigned char, void (*)(struct ticktimer *), int);
unsigned ticktimer_tick (struct ticktimer *);
void ticktimer_ctl (struct ticktimer *, unsigned char);
struct ticktimer *ticktimer_alloc (void);
void ticktimer_free (struct ticktimer *);
void ticktimer_tick_all (void);

#endif

ticktimer.c:

#include "ticktimer.h"

#define TIMER_COUNT 100

static struct ticktimer timers[TIMER_COUNT];
static struct timeval tm;

/*!
  @brief
    Initializes/sets the ticktimer struct.

  @param timer
    Pointer to ticktimer struct.
  @param interval
    Ticking interval in microseconds.
  @param flags
    Flag bitmask. Use TICKTIMER_RUN | TICKTIMER_COMPENSATE
    to start a compensating timer; TICKTIMER_RUN to start
    a normal uncompensating timer.
  @param tick
    Ticking callback function.
  @param id
    Timer ID. Useful if you want to distinguish different
    timers within the same callback function.
*/
void ticktimer_init (struct ticktimer *timer, u_int64_t interval, unsigned char flags, void (*tick)(struct ticktimer *), int id) {
  gettimeofday(&tm, NULL);
  timer->tm_tick_interval = interval;
  timer->tm_last_ticked = tm.tv_sec * 1000000 + tm.tv_usec;
  timer->tm_total = 0;
  timer->ticks_total = 0;
  timer->tick = tick;
  timer->flags = flags;
  timer->id = id;
}

/*!
  @brief 
    Checks the status of a ticktimer and performs a tick(s) if 
    necessary.

  @param timer
    Pointer to ticktimer struct.

  @return
    The number of times the timer was ticked.
*/
unsigned ticktimer_tick (struct ticktimer *timer) {
  register typeof(timer->tm_tick_interval) now;
  register typeof(timer->ticks_total) nticks, i;

  if (timer->flags & TICKTIMER_RUN) {
    gettimeofday(&tm, NULL);
    now = tm.tv_sec * 1000000 + tm.tv_usec;

    if (now >= timer->tm_last_ticked + timer->tm_tick_interval) {
      timer->tm_total += now - timer->tm_last_ticked;

      if (timer->flags & TICKTIMER_COMPENSATE) {
        nticks = (now - timer->tm_last_ticked) / timer->tm_tick_interval;
        timer->tm_last_ticked = now - ((now - timer->tm_last_ticked) % timer->tm_tick_interval);

        for (i = 0; i < nticks; i++) {
          timer->tick(timer);
          timer->ticks_total++;

          if (timer->tick == NULL) {
            break;
          }
        }

        return nticks;
      } else {
        timer->tm_last_ticked = now;
        timer->tick(timer);
        timer->ticks_total++;
        return 1;
      }
    }
  }

  return 0;
}

/*!
  @brief
    Controls the behaviour of a ticktimer.

  @param timer
    Pointer to ticktimer struct.
  @param flags
    Flag bitmask.
*/
inline void ticktimer_ctl (struct ticktimer *timer, unsigned char flags) {
  timer->flags = flags;
}

/*!
  @brief
    Allocates a ticktimer struct from an internal
    statically allocated list.

  @return
    Pointer to the newly allocated ticktimer struct
    or NULL when no more space is available.
*/
struct ticktimer *ticktimer_alloc (void) {
  register int i;

  for (i = 0; i < TIMER_COUNT; i++) {
    if (timers[i].tick == NULL) {
      return timers + i;
    }
  }

  return NULL;
}

/*!
  @brief
    Marks a previously allocated ticktimer struct as free.

  @param timer
    Pointer to ticktimer struct, usually returned by 
    ticktimer_alloc().
*/
inline void ticktimer_free (struct ticktimer *timer) {
  timer->tick = NULL;
}

/*!
  @brief
    Checks the status of all allocated timers from the 
    internal list and performs ticks where necessary.

  @note
    Should be called in the main loop.
*/
inline void ticktimer_tick_all (void) {
  register int i;

  for (i = 0; i < TIMER_COUNT; i++) {
    if (timers[i].tick != NULL) {
      ticktimer_tick(timers + i);
    }
  }
}

【讨论】:

    【解决方案4】:

    这是我编写的一个头文件,用于进行一些简单的性能分析(使用手动计时器):

    #ifndef __ZENTIMER_H__
    #define __ZENTIMER_H__
    
    #ifdef ENABLE_ZENTIMER
    
    #include <stdio.h>
    #ifdef WIN32
    #include <windows.h>
    #else
    #include <sys/time.h>
    #endif
    #ifdef HAVE_STDINT_H
    #include <stdint.h>
    #elif HAVE_INTTYPES_H
    #include <inttypes.h>
    #else
    typedef unsigned char uint8_t;
    typedef unsigned long int uint32_t;
    typedef unsigned long long uint64_t;
    #endif
    
    #ifdef __cplusplus
    extern "C" {
    #pragma }
    #endif /* __cplusplus */
    
    #define ZTIME_USEC_PER_SEC 1000000
    
    /* ztime_t represents usec */
    typedef uint64_t ztime_t;
    
    #ifdef WIN32
    static uint64_t ztimer_freq = 0;
    #endif
    
    static void
    ztime (ztime_t *ztimep)
    {
    #ifdef WIN32
        QueryPerformanceCounter ((LARGE_INTEGER *) ztimep);
    #else
        struct timeval tv;
    
        gettimeofday (&tv, NULL);
    
        *ztimep = ((uint64_t) tv.tv_sec * ZTIME_USEC_PER_SEC) + tv.tv_usec;
    #endif
    }
    
    enum {
        ZTIMER_INACTIVE = 0,
        ZTIMER_ACTIVE   = (1 << 0),
        ZTIMER_PAUSED   = (1 << 1),
    };
    
    typedef struct {
        ztime_t start;
        ztime_t stop;
        int state;
    } ztimer_t;
    
    #define ZTIMER_INITIALIZER { 0, 0, 0 }
    
    /* default timer */
    static ztimer_t __ztimer = ZTIMER_INITIALIZER;
    
    static void
    ZenTimerStart (ztimer_t *ztimer)
    {
        ztimer = ztimer ? ztimer : &__ztimer;
    
        ztimer->state = ZTIMER_ACTIVE;
        ztime (&ztimer->start);
    }
    
    static void
    ZenTimerStop (ztimer_t *ztimer)
    {
        ztimer = ztimer ? ztimer : &__ztimer;
    
        ztime (&ztimer->stop);
        ztimer->state = ZTIMER_INACTIVE;
    }
    
    static void
    ZenTimerPause (ztimer_t *ztimer)
    {
        ztimer = ztimer ? ztimer : &__ztimer;
    
        ztime (&ztimer->stop);
        ztimer->state |= ZTIMER_PAUSED;
    }
    
    static void
    ZenTimerResume (ztimer_t *ztimer)
    {
        ztime_t now, delta;
    
        ztimer = ztimer ? ztimer : &__ztimer;
    
        /* unpause */
        ztimer->state &= ~ZTIMER_PAUSED;
    
        ztime (&now);
    
        /* calculate time since paused */
        delta = now - ztimer->stop;
    
        /* adjust start time to account for time elapsed since paused */
        ztimer->start += delta;
    }
    
    static double
    ZenTimerElapsed (ztimer_t *ztimer, uint64_t *usec)
    {
    #ifdef WIN32
        static uint64_t freq = 0;
        ztime_t delta, stop;
    
        if (freq == 0)
            QueryPerformanceFrequency ((LARGE_INTEGER *) &freq);
    #else
    #define freq ZTIME_USEC_PER_SEC
        ztime_t delta, stop;
    #endif
    
        ztimer = ztimer ? ztimer : &__ztimer;
    
        if (ztimer->state != ZTIMER_ACTIVE)
            stop = ztimer->stop;
        else
            ztime (&stop);
    
        delta = stop - ztimer->start;
    
        if (usec != NULL)
            *usec = (uint64_t) (delta * ((double) ZTIME_USEC_PER_SEC / (double) freq));
    
        return (double) delta / (double) freq;
    }
    
    static void
    ZenTimerReport (ztimer_t *ztimer, const char *oper)
    {
        fprintf (stderr, "ZenTimer: %s took %.6f seconds\n", oper, ZenTimerElapsed (ztimer, NULL));
    }
    
    #ifdef __cplusplus
    }
    #endif /* __cplusplus */
    
    #else /* ! ENABLE_ZENTIMER */
    
    #define ZenTimerStart(ztimerp)
    #define ZenTimerStop(ztimerp)
    #define ZenTimerPause(ztimerp)
    #define ZenTimerResume(ztimerp)
    #define ZenTimerElapsed(ztimerp, usec)
    #define ZenTimerReport(ztimerp, oper)
    
    #endif /* ENABLE_ZENTIMER */
    
    #endif /* __ZENTIMER_H__ */
    

    ztime() 函数是您需要的主要逻辑——它获取当前时间并将其存储在以微秒为单位的 64 位 uint 中。然后,您可以稍后进行简单的数学运算以找出经过的时间。

    ZenTimer*() 函数只是辅助函数,用于获取指向简单计时器结构 ztimer_t 的指针,该结构记录开始时间和结束时间。 ZenTimerPause()/ZenTimerResume() 函数允许您暂停和恢复计时器,以防您想打印一些您不想计时的调试信息。

    您可以在http://www.gnome.org/~fejj/code/zentimer.h 找到原始头文件的副本,以防我弄乱了

    【讨论】:

      【解决方案5】:

      使用 time.h 库,尝试如下操作:

      long start_time, end_time, elapsed;
      
      start_time = clock();
      // Do something
      end_time = clock();
      
      elapsed = (end_time - start_time) / CLOCKS_PER_SEC * 1000;
      

      【讨论】:

      • CLK_TCK 已过时;改用CLOCKS_PER_SEC
      • clock() 没有达到 1 微秒的分辨率。一位消息人士称其分辨率约为 15 毫秒:songho.ca/misc/timer/timer.html
      • clock() 的粒度因系统不同而不同。
      • clock() 返回程序使用的处理器时间。使用sleepusleep 等可能会导致您的软件出现问题。
      【解决方案6】:

      如果您的 Linux 系统支持,clock_gettime(CLOCK_MONOTONIC) 应该是不受系统日期更改影响的高分辨率计时器(例如 NTP 守护程序)。

      【讨论】:

      • 他说他在 windows 上使用 mingw 而不是 linux
      【解决方案7】:

      对于上面和下面的 GNU 环境的很好的答案......

      但是...如果您不在操作系统上运行怎么办? (或者PC,或者你需要定时你的定时器中断自己?)这是一个直接使用x86 CPU时间戳计数器的解决方案......不是因为这是一个好的做法,或者应该永远这样做,在运行时一个操作系统...

      • 警告:仅适用于 x86,频率缩放已禁用。
      • 在 Linux 下,仅适用于非 Tickless 内核

      rdtsc.c:

      #include <sys/time.h>
      #include <time.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      typedef unsigned long long int64;
      
      static __inline__ int64 getticks(void)
      {
           unsigned a, d;
           asm volatile("rdtsc" : "=a" (a), "=d" (d));
           return (((int64)a) | (((int64)d) << 32));
      }
      
      int main(){
      
           int64 tick,tick1;
           unsigned time=0,mt;
      
           // mt is the divisor to give microseconds
      
           FILE *pf;
           int i,r,l,n=0;
           char s[100];
      
           // time how long it takes to get the divisors, as a test 
           tick = getticks();
      
           // get the divisors  - todo: for max performance this can 
           // output a new binary or library with these values hardcoded 
           // for the relevant CPU - if you use the equivalent assembler for
           // that CPU
           pf = fopen("/proc/cpuinfo","r");
           do {
            r=fscanf(pf,"%s",&s[0]);
            if (r<0) {
             n=5; break;
            } else if (n==0) {
             if (strcmp("MHz",s)==0) n=1;
            } else if (n==1) {
             if (strcmp(":",s)==0) n=2;
            } else if (n==2) {
             n=3;
            };
           } while (n<3);
           fclose(pf);
      
           s[9]=(char)0;
           strcpy(&s[4],&s[5]);
           mt=atoi(s);
      
           printf("#define mt %u // (%s Hz) hardcode this for your a CPU-specific binary ;-)\n",mt,s);
      
           tick1 = getticks();
           time = (unsigned)((tick1-tick)/mt);
           printf("%u ms\n",time);
      
           // time the duration of sleep(1) - plus overheads ;-)
           tick = getticks();
      
           sleep(1);
      
           tick1 = getticks();
           time = (unsigned)((tick1-tick)/mt);
           printf("%u ms\n",time);
      
           return 0;
      }
      

      编译并运行

      $ gcc rdtsc.c -o rdtsc && ./rdtsc

      它从 /proc/cpuinfo 读取 CPU 的除数,并显示读取该除数所需的时间(以微秒为单位),以及执行 sleep(1) 所需的时间(以微秒为单位)...假设 Mhz 等级为/proc/cpuinfo 总是包含 3 个小数位:-o

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-08
      • 1970-01-01
      相关资源
      最近更新 更多