我想用 C 语言测量时间,但我很难弄清楚,我想要的就是这样的:
如有任何帮助,我们将不胜感激。
(我正在使用mingw在windows中编译)
提供 1 微秒分辨率的高分辨率计时器是特定于系统的,因此您必须使用不同的方法在不同的操作系统平台上实现这一点。您可能有兴趣查看以下文章,它基于下面描述的功能实现了一个跨平台的 C++ 定时器类:
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);
}
clock_gettime()
:
clock_gettime(CLOCK_REALTIME, &start); // get initial time-stamp
// ... do stuff ... //
clock_gettime(CLOCK_REALTIME, &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
这是我编写的一个头文件,用于进行一些简单的性能分析(使用手动计时器):
#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()
函数允许您暂停和恢复计时器。
如果我弄乱了 的 html 转义,您可以在 http://www.gnome.org/~fejj/code/zentimer.h<'s or something. It's licensed under MIT/X11 so feel free to copy it into any project you do.
找到原始头文件的副本以下是一组基于 gettimeofday() 系统调用的用于定时器管理的通用 C 函数。所有计时器属性都包含在一个滴答计时器结构中 - 您想要的时间间隔、自计时器初始化以来的总运行时间、指向您要调用的所需回调的指针、调用回调的次数。回调函数如下所示:
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);
}
}
}
使用 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) 应该是一个不受系统日期更改影响的高分辨率计时器(例如 NTP 守护进程)。
GNU 环境以上和以下的很好的答案...
但是...如果您没有在操作系统上运行怎么办? (或者是一台 PC,或者您需要对计时器中断本身进行计时?)这是一个直接使用 x86 CPU 时间戳计数器的解决方案...并不是因为这是一个好的实践,或者应该在运行时这样做操作系统...
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) 所需的时间(以微秒为单位)...假设 /proc 中的 Mhz 等级/cpuinfo 始终包含 3 位小数 :-o
对于大多数性能测量,我们需要查看挂钟时间。为此,我在方法执行之前和之后使用 gettimeofday(),通过区分它们,您可以获得所需的结果,如下例所示。您可以调整 .6f 值以更改您想要的精度。您还可以通过搜索了解更多
gettimeofday()
方法。
#include <stdlib.h>
#include <sys/time.h>
#include <stdio.h>
int main()
{
struct timeval t;
double time1, time2;
gettimeofday(&t, NULL);
time1 = t.tv_sec + 1.0e-6 * t.tv_usec;
//Call your method here.
gettimeofday(&t, NULL);
time2 = t.tv_sec + 1.0e-6 * t.tv_usec;
printf("Operation Completed\n");
printf("Elapsed: %f seconds \n\n", (double)(time2 - time1));
return 0;
}