Domanda

Utilizzando solo ANSI C, c'è un modo per misurare il tempo con precisione millisecondi o più? Stavo navigando time.h ma ho trovato solo le funzioni di secondo di precisione.

È stato utile?

Soluzione

Non c'è funzione ANSI C che fornisce migliore dell'1 risoluzione secondo tempo, ma la funzione POSIX gettimeofday fornisce una risoluzione microsecondo. La funzione orologio misura soltanto la quantità di tempo che un processo ha speso in esecuzione e non è precisa su molti sistemi.

È possibile utilizzare questa funzione in questo modo:

struct timeval tval_before, tval_after, tval_result;

gettimeofday(&tval_before, NULL);

// Some code you want to time, for example:
sleep(1);

gettimeofday(&tval_after, NULL);

timersub(&tval_after, &tval_before, &tval_result);

printf("Time elapsed: %ld.%06ld\n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec);

Ciò restituisce Time elapsed: 1.000870 sulla mia macchina.

Altri suggerimenti

#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);

Io uso sempre la funzione clock_gettime (), il tempo di ritorno da l'orologio CLOCK_MONOTONIC. Il tempo restituito è la quantità di tempo, in secondi e nanosecondi, dal momento che un certo punto non specificato nel passato, come ad esempio l'avvio del sistema dell'epoca.

#include <stdio.h>
#include <stdint.h>
#include <time.h>

int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
  return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
           ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}

int main(int argc, char **argv)
{
  struct timespec start, end;
  clock_gettime(CLOCK_MONOTONIC, &start);

  // Some code I am interested in measuring 

  clock_gettime(CLOCK_MONOTONIC, &end);

  uint64_t timeElapsed = timespecDiff(&end, &start);
}

L'implementazione di una soluzione portatile

Come è stato già detto qui che non esiste una soluzione adeguata ANSI con sufficiente precisione per il problema misurazione del tempo, voglio scrivere circa i modi per ottenere un portatile e, se possibile, una soluzione di misurazione del tempo ad alta risoluzione.

orologio Monotonic vs. bolli di tempo

Generalmente ci parlando sono due modi di misurazione del tempo:

  • orologio monotono;
  • corrente (data) timestamp.

Il primo utilizza un contatore orologio monotona (a volte viene chiamato un contatore tick), che conta zecche con una frequenza predefinita, quindi se avete un valore zecche e la frequenza è noto, è possibile convertire facilmente le zecche al tempo trascorso. Non è in realtà garantito che un orologio monotona riflette l'ora corrente del sistema in qualsiasi modo, si può contare anche le zecche dal momento che un avvio del sistema. Ma garantisce che un orologio è sempre gestito in maniera crescente, indipendentemente dallo stato del sistema. Di solito la frequenza è legato ad una sorgente ad alta risoluzione hardware, è per questo che fornisce una precisione elevata (dipende da hardware, ma la maggior parte dell'hardware moderno non ha problemi con sorgenti di clock ad alta risoluzione).

Il secondo modo fornisce una (data) valore temporale in base al valore dell'orologio di sistema corrente. Esso può anche avere una risoluzione elevata, ma presenta un grave inconveniente: questo tipo di valore di tempo può essere influenzato da diverse regolazioni di tempo del sistema, cioè cambiamento di fuso orario, ora legale (DST) cambia, aggiornare server NTP, ibernazione sistema e così sopra. In alcune circostanze è possibile ottenere un valore di tempo trascorso negativo che può portare ad un comportamento indefinito. In realtà questo tipo di fonte il tempo è meno affidabile rispetto al primo.

Quindi la prima regola in tempo intervallo di misurazione è quello di utilizzare un orologio monotona, se possibile. Di solito ha una elevata precisione, ed è affidabile alla progettazione.

strategia di fallback

Quando si implementa una soluzione portatile vale la pena di prendere in considerazione una strategia di ripiego:. Utilizzare un orologio monotona se disponibile, e fallback in tanto approccio francobolli se non v'è nessun orologio monotona nel sistema

di Windows

C'è un grande articolo intitolato Acquisizione ad alta risoluzione time stamp su MSDN sulla misurazione del tempo su Windows, che descrive tutti i dettagli potete avere bisogno di conoscere il software e supporto hardware. Per acquisire una elevata precisione data e ora su Windows è necessario:

  • interrogare una frequenza del timer (impulsi al secondo) con QueryPerformanceFrequency :

    LARGE_INTEGER tcounter;
    LARGE_INTEGER freq;    
    
    if (QueryPerformanceFrequency (&tcounter) != 0)
        freq = tcounter.QuadPart;
    

    La frequenza del timer è fissato l'avvio del sistema in modo da è necessario per ottenere una sola volta.

  • interrogare l'attuale valore di zecche con QueryPerformanceCounter :

    LARGE_INTEGER tcounter;
    LARGE_INTEGER tick_value;
    
    if (QueryPerformanceCounter (&tcounter) != 0)
        tick_value = tcounter.QuadPart;
    
  • scalare le zecche per il tempo trascorso, vale a dire a microsecondi:

    LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);
    

Secondo Microsoft non si dovrebbe avere alcun problema con questo approccio su Windows XP e versioni successive nella maggior parte dei casi. Ma si può anche utilizzare due soluzioni di ripiego su Windows:

  • GetTickCount fornisce il numero di millisecondi che sono trascorsi dal momento che il sistema di era iniziato. Si avvolge ogni 49,7 giorni, quindi state attenti a misurare intervalli più lunghi.
  • GetTickCount64 è una versione a 64 bit di GetTickCount, ma è disponibile a partire da Windows Vista e versioni successive.

OS X (MacOS)

OS X (MacOS) ha i propri Mach unità di tempo assolute cui Represent un orologio monotona. Il modo migliore per iniziare è tecnico Q & A QA1398 di Apple articolo : Unità Mach tempo assoluto che descrive (con gli esempi di codice) come utilizzare Mach-specifico API per ottenere zecche monotone. V'è anche una questione locale, su di esso chiamato clock_gettime alternativa in Mac OS X , che alla fine può lasciare un po 'confuso che cosa fare con la possibile trabocco valore perché la frequenza contatore viene utilizzato sotto forma di numeratore e denominatore. Quindi, un breve esempio come ottenere il tempo trascorso:

  • ottenere il numeratore e il denominatore frequenza di clock:

    #include <mach/mach_time.h>
    #include <stdint.h>
    
    static uint64_t freq_num   = 0;
    static uint64_t freq_denom = 0;
    
    void init_clock_frequency ()
    {
        mach_timebase_info_data_t tb;
    
        if (mach_timebase_info (&tb) == KERN_SUCCESS && tb.denom != 0) {
            freq_num   = (uint64_t) tb.numer;
            freq_denom = (uint64_t) tb.denom;
        }
    }
    

    È necessario farlo una sola volta.

  • interrogare il valore tick corrente con mach_absolute_time:

    uint64_t tick_value = mach_absolute_time ();
    
  • scalare le zecche per il tempo trascorso, vale a dire a microsecondi, con numeratore e denominatore precedentemente interrogato:

    uint64_t value_diff = tick_value - prev_tick_value;
    
    /* To prevent overflow */
    value_diff /= 1000;
    
    value_diff *= freq_num;
    value_diff /= freq_denom;
    

    L'idea principale per impedire un trabocco è di scalare le zecche precisione desiderata prima di utilizzare il numeratore e denominatore. Come la risoluzione iniziale timer è in nanosecondi, ci dividiamo per 1000 per ottenere microsecondi. È possibile trovare lo stesso approccio utilizzato nel time_mac.c . Se davvero bisogno di una precisione nanosecondo considerare la lettura del Come posso usare mach_absolute_time senza traboccare? .

Linux e UNIX

La chiamata clock_gettime è il tuo modo migliore su qualsiasi sistema POSIX-friendly. Si può interrogare il tempo da diverse sorgenti di clock, e quello che ci serve è CLOCK_MONOTONIC. Non tutti i sistemi che hanno _POSIX_MONOTONIC_CLOCK supporto >= 0, quindi la prima cosa che devi fare è quello di verificare la disponibilità:

  • se 0 è definito da un valore sysconf significa che gettimeofday è avaiable;
  • se CLOCK_SGI_CYCLE è definito per gethrtime vuol dire che si dovrebbe inoltre controllare se funziona in fase di esecuzione, suggerisco di usare system_time:

    #include <unistd.h>
    
    #ifdef _SC_MONOTONIC_CLOCK
    if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
        /* A monotonic clock presents */
    }
    #endif
    
  • altrimenti un orologio monotona non è supportata e si dovrebbe utilizzare una strategia di ripiego (vedi sotto).

L'utilizzo di DosTmrQueryFreq è piuttosto semplice:

  • ottenere il valore del tempo:

    #include <time.h>
    #include <sys/time.h>
    #include <stdint.h>
    
    uint64_t get_posix_clock_time ()
    {
        struct timespec ts;
    
        if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
            return (uint64_t) (ts.tv_sec * 1000000 + ts.tv_nsec / 1000);
        else
            return 0;
    }
    

    Ho ridimensionato il tempo di microsecondi qui.

  • calcolare la differenza con il valore di tempo ricevuto in precedenza nello stesso modo:

    uint64_t prev_time_value, time_value;
    uint64_t time_diff;
    
    /* Initial time */
    prev_time_value = get_posix_clock_time ();
    
    /* Do some work here */
    
    /* Final time */
    time_value = get_posix_clock_time ();
    
    /* Time difference */
    time_diff = time_value - prev_time_value;
    

La migliore strategia di fallback è quello di utilizzare la chiamata DosTmrQueryTime: non è una monotona, ma fornisce piuttosto una buona risoluzione. L'idea è la stessa con <=>, ma per ottenere un valore di tempo è necessario:

#include <time.h>
#include <sys/time.h>
#include <stdint.h>

uint64_t get_gtod_clock_time ()
{
    struct timeval tv;

    if (gettimeofday (&tv, NULL) == 0)
        return (uint64_t) (tv.tv_sec * 1000000 + tv.tv_usec);
    else
        return 0;
}

Ancora una volta, il valore di tempo viene ridotta a microsecondi.

SGI IRIX

IRIX ha la chiamata <=>, ma manca <=>. Invece ha una propria sorgente di clock monotona definita come <=> che si dovrebbe usare al posto di <=> con <=>.

Solaris e HP-UX

Solaris ha la sua interfaccia ad alta risoluzione timer <=> che restituisce il valore del timer corrente in nanosecondi. Anche se le versioni più recenti di Solaris possono avere <=>, è possibile attenersi a <=> se è necessario supportare le vecchie versioni di Solaris.

L'uso è semplice:

#include <sys/time.h>

void time_measure_example ()
{
    hrtime_t prev_time_value, time_value;
    hrtime_t time_diff;

    /* Initial time */
    prev_time_value = gethrtime ();

    /* Do some work here */

    /* Final time */
    time_value = gethrtime ();

    /* Time difference */
    time_diff = time_value - prev_time_value;
}

HP-UX manca <=>, ma supporta <=> che si dovrebbe usare nello stesso modo come su Solaris.

BeOS

BeOS ha anche una propria interfaccia ad alta risoluzione timer <=> che restituisce il numero di microsecondi sono trascorsi il computer è stato avviato.

Esempio di utilizzo:

#include <kernel/OS.h>

void time_measure_example ()
{
    bigtime_t prev_time_value, time_value;
    bigtime_t time_diff;

    /* Initial time */
    prev_time_value = system_time ();

    /* Do some work here */

    /* Final time */
    time_value = system_time ();

    /* Time difference */
    time_diff = time_value - prev_time_value;
}

OS / 2

OS / 2 ha una propria API per recuperare ad alta precisione time stamp:

  • query una frequenza timer (zecche per unità) con <=> (per il compilatore GCC):

    #define INCL_DOSPROFILE
    #define INCL_DOSERRORS
    #include <os2.h>
    #include <stdint.h>
    
    ULONG freq;
    
    DosTmrQueryFreq (&freq);
    
  • interrogare l'attuale valore di tick con <=>:

    QWORD    tcounter;
    unit64_t time_low;
    unit64_t time_high;
    unit64_t timestamp;
    
    if (DosTmrQueryTime (&tcounter) == NO_ERROR) {
        time_low  = (unit64_t) tcounter.ulLo;
        time_high = (unit64_t) tcounter.ulHi;
    
        timestamp = (time_high << 32) | time_low;
    }
    
  • scalare le zecche per il tempo trascorso, vale a dire a microsecondi:

    uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);
    

implementazione Esempio

Si può dare un'occhiata alla biblioteca plibsys che implementa tutte le strategie sopra descritto (vedi ptimeprofiler * .c per i dettagli).

timespec_get da C11

Restituisce fino a nanosecondi, arrotondato alla risoluzione di attuazione.

appare come un imbroglio ANSI da POSIX'clock_gettime.

Esempio: un printf viene effettuata ogni 100 ms su Ubuntu 15.10:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static long get_nanos(void) {
    struct timespec ts;
    timespec_get(&ts, TIME_UTC);
    return (long)ts.tv_sec * 1000000000L + ts.tv_nsec;
}

int main(void) {
    long nanos;
    long last_nanos;
    long start;
    nanos = get_nanos();
    last_nanos = nanos;
    start = nanos;
    while (1) {
        nanos = get_nanos();
        if (nanos - last_nanos > 100000000L) {
            printf("current nanos: %ld\n", nanos - start);
            last_nanos = nanos;
        }
    }
    return EXIT_SUCCESS;
}

Il C11 N1570 standard draft 7.27.2.5 "La funzione timespec_get dice":

  

Se la base è TIME_UTC, l'organo tv_sec è impostato al numero di secondi trascorsi un   attuazione epoca definito, troncato a un valore intero e l'elemento tv_nsec è   impostare il numero intero di nanosecondi, arrotondato alla risoluzione del clock di sistema. (321)

     

321) Anche se un oggetto timespec struct descrive volte con risoluzione nanosecondo, la disposizione   risoluzione dipende dal sistema e può anche essere maggiore di 1 secondo.

C ++ 11 anche ottenuto std::chrono::high_resolution_clock: C ++ multipiattaforma ad alta risoluzione Timer

glibc 2.21 realizzazione

Può essere trovato in sysdeps/posix/timespec_get.c come:

int
timespec_get (struct timespec *ts, int base)
{
  switch (base)
    {
    case TIME_UTC:
      if (__clock_gettime (CLOCK_REALTIME, ts) < 0)
        return 0;
      break;

    default:
      return 0;
    }

  return base;
}

così chiaramente:

  • solo TIME_UTC è attualmente supportato

  • in avanti per __clock_gettime (CLOCK_REALTIME, ts), che è un'API POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html

    Linux x86-64 ha una chiamata di sistema man clock_gettime.

    Si noti che questa non è una prova di fail-micro-analisi comparativa metodo perché:

    • getrusage() dice che questa misura possa avere discontinuità se si cambia un po 'impostazione del tempo di sistema mentre il programma viene eseguito. Questo dovrebbe essere un evento raro, naturalmente, e si potrebbe essere in grado di ignorarlo.

    • questa misura il tempo a muro, quindi se lo scheduler decide di dimenticare il vostro compito, apparirà a correre più a lungo.

    Per queste ragioni <=> potrebbe essere una migliore migliore strumento di benchmarking POSIX, nonostante la sua bassa microsecondo la massima precisione.

    Maggiori informazioni su: Misura tempo in Linux -? tempo vs orologio vs getrusage vs clock_gettime vs gettimeofday vs timespec_get

Il meglio di precisione si può eventualmente ottenere è attraverso l'uso del x86-only istruzione "rdtsc", in grado di fornire una risoluzione a livello di clock (ne deve naturalmente tener conto del costo della rdtsc dirsi, che può essere misurata facilmente all'avvio dell'applicazione).

La cattura principale qui sta misurando il numero di clock al secondo, che non dovrebbe essere troppo difficile.

La risposta accettata è buono enough.But la mia soluzione è più simple.I appena prova a Linux, usare GCC (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.

Alse uso gettimeofday, il tv_sec è la parte della seconda, e le tv_usec è microsecondi , non millisecondi .

long currentTimeMillis() {
  struct timeval time;
  gettimeofday(&time, NULL);

  return time.tv_sec * 1000 + time.tv_usec / 1000;
}

int main() {
  printf("%ld\n", currentTimeMillis());
  // wait 1 second
  sleep(1);
  printf("%ld\n", currentTimeMillis());
  return 0;
 }

E 'di stampa:

1522139691342 1522139692342, esattamente un secondo.

Sotto le finestre:

SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top