我如何测量C中的时间间隔?

我想用C来衡量时间,而且我正在经历一个艰难的时期,我想要的就是这样的事情:

  • 启动一个计时器
  • 运行一个方法
  • 停止计时器
  • 报告所花费的时间(至less微精度)

任何帮助,将不胜感激。

(我使用mingw在windows中编译)

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

  • Song Ho Ahn – 高分辨率定时器

视窗

Windows API提供了非常高分辨率的定时器函数: QueryPerformanceCounter() ,返回当前stream逝的滴答, QueryPerformanceFrequency() ,返回每秒的滴答数。

例:

 #include <iostream> #include <windows.h> // for Windows APIs using namespace std; int main() { 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; cout << elapsedTime << " ms.\n"; return 0; } 

Linux,Unix和Mac

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

例:

 #include <iostream> #include <sys/time.h> // for gettimeofday() using namespace std; int main() { 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 cout << elapsedTime << " ms.\n"; return 0; } 

请注意,上述示例需要使用mingw支持的C ++进行编译。

在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 

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

 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)的主循环中,它将决定是否已经过去适当的时间来调用callback。

要停止计时器,只需调用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); } } } 

这里是我写的一个头文件来做一些简单的性能分析(使用手动定时器):

 #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中。 然后,你可以稍后做简单的math来找出经过的时间。

ZenTimer*()函数只是帮助函数,用来指向一个简单的计时器struct ztimer_t ,它logging开始时间和结束时间。 例如, ZenTimerPause() / ZenTimerResume()函数允许您暂停和恢复计时器,以防打印某些您不想计时的debugging信息。

你可以在http://www.gnome.org/~fejj/code/zentimer.hfind一个原始头文件的副本,但是我弄错了<或者什么东西。; 它在MIT / X11下获得许可,所以可以随意将其复制到任何项目中。

以下是使用x86 CPU时间戳计数器的GNU / Linux解决scheme:

  • 警告:只适用于x86和非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,ut,mt; // ut is the divisor to give microseconds // mt gives milliseconds 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 - a kind-of ludicrous notion considering // that this will only work on x86 compatible cpus anyways where // performance is the least of your issues... // ... curse of the assembly coder ;-) 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); l=strlen(s); s[l-4]=s[l-3]; s[l-3]=s[l-2]; s[l-2]=s[l-1]; s[l-1]=(char)0; mt=atoi(s); s[l-4]=(char)0; ut=atoi(s); printf("%s Mhz - ut = %u, mt = %u // hardcode these for your a CPU-specific binary ;-)\n",s,ut,mt); tick1 = getticks(); time = (unsigned)((tick1-tick)/ut); printf("%u us\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读取正确的除数,并显示以微秒为单位读取所花费的时间以及以毫秒为单位执行睡眠所需的时间(1)。

假设/ proc / cpuinfo中的Mhz等级总是包含3个小数位:-o

如果你想精确的计算,我想你必须在你的操作系统上使用特定的库。

使用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; 

如果你的Linux系统支持它,clock_gettime(CLOCK_MONOTONIC)应该是一个不受系统date变化影响的高分辨率定时器(例如NTP守护进程)。