2010-01-27 6 views
41

Я хотел бы измерить время в C, и я имею трудное время выяснить это, все, что я хочу что-то вроде этого:Как измерить временной интервал в C?

  • запустить таймер
  • запустить метод
  • остановить таймер
  • отчет время, потраченное (по крайней мере, к микро точности)

Любая помощь будет оценена.

(я компиляция в окнах с использованием MinGW)

+0

дата-время или времени центрального процессора? для последнего см., например, http://stackoverflow.com/questions/1380136/fast-elapsed-time-on-linux/1380663#1380663 – Christoph

ответ

88

Таймеры с высоким разрешением, обеспечивающие разрешение 1 микросекунды, являются системными, поэтому для достижения этой цели на разных платформах ОС вам придется использовать разные методы. Вы можете быть заинтересованы в проверке из следующей статьи, которая реализует кросс-платформенный C++ класс таймера на основе функций, описанных ниже:


Окна

В Windows API предусмотрены функции таймера с чрезвычайно высоким разрешением: QueryPerformanceCounter(), который возвращает текущие истекшие тики и QueryPerformanceFrequency(), wh ich возвращает количество тиков в секунду.

Пример:

#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; 
} 

Следует отметить, что приведенные выше примеры должны быть скомпилированы с C++, который mingw опор.

+7

Я думаю, что это должно быть struct timeval, а не только timeval. – rightaway717

+1

'struct timeval' работал для меня (некоторый компилятор GCC ARM) – Micka

+0

В Android сообщается о странно большой стоимости. Когда t2.tv_usec = 925311 и t1.tv_usec = 861102, elapsedTime сообщается как 262144000. Это после добавления «struct», как указано в rightaway717. – Nicholas

0

Возьмите лки на this one однако если вы хотите точный расчет, я думаю, вы должны использовать специальные библиотеки на вашей операционной системе.

+0

Хотя эта ссылка может ответить на вопрос, лучше включить основные части ответьте здесь и укажите ссылку для справки. Ответные ссылки могут стать недействительными, если связанная страница изменится. –

0

Использования библиотеки 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; 
+0

'CLK_TCK' устарело; вместо этого используйте 'CLOCKS_PER_SEC' – Christoph

+0

Правильно! Ред. Благодаря! – Aaron

+3

'clock()' не достигает разрешения 1 микросекунды. Один источник утверждает, что его разрешение составляет около 15 мс: http://www.songho.ca/misc/timer/timer.html –

2

Вот файл заголовка я написал, чтобы сделать несколько простого профилирования производительности (с использованием ручных таймеров):

#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, поэтому не стесняйтесь копировать его в любой проект, который вы делаете.

0

Если ваша система Linux поддерживает его, clock_gettime (CLOCK_MONOTONIC) должен быть таймером с высоким разрешением, на который не влияют системные изменения даты (например, демоны NTP).

+0

Он сказал, что он использует mingw на windows not linux – Shedokan

16

В 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 
+0

было бы хорошо, если бы вы могли объяснить, как это работает. – fireant

+0

Почему проголосовали? См. Http://linux.die.net/man/3/clock_gettime для получения дополнительной информации о clock_gettime. –

2

Ниже группа универсальных функций C для управления таймером на основе системного вызова gettimeofday(). Все свойства таймера содержатся в одной структуре ticktimer - требуемый интервал, общее время работы с момента инициализации таймера, указатель на требуемый обратный вызов, который вы хотите вызвать, количество раз, когда вызывался обратный вызов. Функция обратного вызова будет выглядеть следующим образом:

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

Для инициализации и запуска таймера, вызовите ticktimer_init (your_timer, интервал, 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); 
    } 
    } 
} 
1

Вот решение для GNU/Linux, который использует счетчик x86 CPU временной метки:

  • РИСКОВАННАЯ: Только работает на x86 и ядрах без колонок ...
  • Общая информация: кто может рассказать нам, что такое t iming возвращается на ядро ​​без ключа?
  • Подсказка: это не будет в реальном масштабе времени

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; 
} 

компилировать и запускать с

$ НКУ rdtsc.c -o RDTSC & &./ RDTSC

Он читает правильный делитель для вашего CPU из/Proc/CPUInfo и показывает, сколько времени потребовалось, чтобы прочитать, что в микросекундах, а также, сколько времени требуется, чтобы выполнить сон (1) в миллисекундах.

... Если предположить, что рейтинг Mhz в/Proc/CPUInfo всегда содержит 3 знаков после запятой: -о

+0

Todo: см., Где/proc/cpuinfo получает его ... но я предполагаю, что это время ... так что лучше всего прочитать его в более прямом формате, откуда ядро ​​хранит его ... – dagelf

+0

Кто увлекается обновить это, чтобы читать из ядра напрямую? http://lxr.linux.no/linux+v3.8.1/arch/x86/kernel/cpu/proc.c http://lxr.linux.no/linux+v3.8.1/drivers/cpufreq/cpufreq. C# L1217 - может даже заставить его работать на частотных устройствах масштабирования ... – dagelf

+0

http://lxr.linux.no/linux+v3.8.1/arch/x86/kernel/tsc.c#L959 – dagelf

Смежные вопросы