Как измерить временной интервал в C?
Я хотел бы измерить время в C, и мне сложно разобраться, все, что я хочу, это что-то вроде этого:
- запуск таймера
- запустить метод
- остановить таймер
- сообщите о времени (по крайней мере, к микро точности)
Любая помощь будет оценена.
(я компилирую в windows с помощью mingw)
Ответы
Ответ 1
Таймеры с высоким разрешением, обеспечивающие разрешение 1 микросекунды, зависят от системы, поэтому вам придется использовать разные методы для достижения этой цели на разных платформах ОС. Возможно, вам интересна проверка следующей статьи, которая реализует кросс-платформенный класс таймера С++ на основе функций, описанных ниже:
Окно
В Windows API предусмотрены функции таймера с чрезвычайно высоким разрешением: QueryPerformanceCounter()
, который возвращает текущие истекшие тики и 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.
Ответ 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
Вот заголовочный файл, который я написал, чтобы выполнить простую профилировку производительности (используя ручные таймеры):
#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 вовремя, что я испортил html-экранирование < или что-то. Он лицензирован в рамках MIT/X11, поэтому не стесняйтесь копировать его в любой проект, который вы делаете.
Ответ 4
Ниже приведена группа универсальных функций C для управления таймером на основе системного вызова gettimeofday(). Все свойства таймера содержатся в одной структуре 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);
}
}
}
Ответ 5
Здесь представлено решение для GNU/Linux, которое использует счетчик временных меток процессора x86:
- Предостережение: работает только с ядрами x86 и без тиков...
- Общая информация: кто может рассказать нам, что вернулось время, - это ядро без ключа?
- Подсказка: это не будет в режиме реального времени
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 и показывает, сколько времени потребовалось, чтобы прочитать, что в микросекундах, а также сколько времени требуется для выполнения sleep (1) в миллисекундах.
... Предполагая, что рейтинг Mhz в /proc/cpuinfo всегда содержит 3 десятичных знака: -o
Ответ 6
Возьмите lok в этот, однако, если вам нужен точный расчет, я думаю, что вам нужно использовать определенные библиотеки в вашей операционной системе.
Ответ 7
Используя библиотеку 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;
Ответ 8
Если ваша система Linux поддерживает его, clock_gettime (CLOCK_MONOTONIC) должен быть таймером с высоким разрешением, на который не влияют системные изменения даты (например, демоны NTP).