Frage

Mit nur ANSI C, ist es eine Möglichkeit, Zeit mit Millisekunden Genauigkeit oder mehr zu messen? Ich war Surfen time.h aber ich fand nur zweite Präzision Funktionen.

War es hilfreich?

Lösung

Es gibt keine ANSI-C-Funktion, die besser als 1 Sekunde Zeitauflösung, aber die POSIX-Funktion

Andere Tipps

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

Ich benutze immer die clock_gettime () Funktion, die Zeit von der CLOCK_MONOTONIC Uhr zurück. Die Zeit zurückgegeben wird, ist die Zeit in Sekunden und Nanosekunden, da einige nicht näher bezeichnete Punkt in der Vergangenheit, wie Systemstart der Epoche.

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

Die Implementierung eine tragbare Lösung

Wie bereits hier erwähnt wurde, dass es keine richtige ANSI-Lösung mit ausreichender Genauigkeit für die Zeitmessung Problem, möchte ich über die Art und Weise schreiben, wie ein tragbares zu erhalten und, wenn möglich, eine hochauflösende Zeitmesslösung.

Monotone Uhr gegen Zeitstempel

Im Allgemeinen gibt es zwei Möglichkeiten der Zeitmessung:

  • monotone Uhr;
  • Strom (Datum) Zeitstempel.

Die erste verwendet einen monotonen Taktzähler (manchmal ist es ein Tick-Zähler genannt), die mit einer vorgegebenen Frequenz zählt Zecken, wenn Sie also einen Zecken-Wert und die Frequenz bekannt ist, kann man leicht Zecken auf die verstrichene Zeit konvertieren. Es ist eigentlich nicht garantiert, dass eine monotone Uhr die aktuelle Systemzeit in keiner Weise reflektiert, kann es auch Zecken, da ein Systemstart zählen. Aber es ist sichergestellt, dass eine Uhr immer in zunehmender Mode läuft bis unabhängig vom Systemzustand. Normalerweise ist die Frequenz hoch auflösende Quelle an eine Hardware gebunden ist, das ist, warum es eine hohe Genauigkeit liefert (abhängig von der Hardware, aber die meisten der modernen Hardware hat keine Probleme mit hochauflösenden Taktquellen).

Der zweite Weg einen (Datum) Zeitwert auf dem aktuellen Systemtaktwert. Es kann auch eine hohe Auflösung hat, aber es hat einen großen Nachteil: diese Art von Zeit-Wert kann durch verschiedene Systemzeiteinstellungen, dh die Zeitzone ändern, Sommerzeit (DST) ändern, NTP-Server zu aktualisieren, System des Ruhezustand und so beeinflusst werden auf. Unter bestimmten Umständen können Sie einen negativen verstrichene Zeit Wert erhalten, die zu einem undefinierten Verhalten führen kann. Eigentlich ist diese Art von Zeitquelle ist weniger zuverlässig als die erste.

So ist die erste Regel in Intervallmesszeit ist eine monotone Uhr zu verwenden, wenn möglich. Es hat in der Regel eine hohe Präzision, und es ist zuverlässig durch Design.

Fallback-Strategie

Wenn eine tragbare Lösung Implementierung lohnt es sich, eine Ausweichstrategie zu berücksichtigen:. Verwenden, um eine monotone Uhr, wenn verfügbar und Rückgriff auf Zeitmarken-Ansatz, wenn es keine monotone Uhr im System

Fenster

Es ist ein großer Artikel mit dem Titel Erwerb von hochauflösenden Zeitmarken auf MSDN über die Zeitmessung auf Windows, die Sie müssen wissen, über Software und Hardware-Support alle Einzelheiten beschreiben. Um eine hohe Präzision Zeitstempel auf Windows erwerben sollten Sie:

  • abfragen, um eine Timer-Frequenz (Ticks pro Sekunde) mit Queryperformance :

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

    Die Timer-Frequenz auf dem Systemstart festgelegt ist, so dass Sie es nur einmal benötigen.

  • abfragen, um die aktuellen Zecken mit Queryperformancecounter :

    LARGE_INTEGER tcounter;
    LARGE_INTEGER tick_value;
    
    if (QueryPerformanceCounter (&tcounter) != 0)
        tick_value = tcounter.QuadPart;
    
  • skalieren die Zecken auf die verstrichene Zeit, das heißt bis Mikrosekunden:

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

Laut Microsoft sollten Sie keine Probleme haben mit diesem Ansatz auf Windows XP und späteren Versionen in den meisten Fällen. Sie können aber auch zwei Rückfalllösungen unter Windows verwenden:

  • GetTickCount liefert die Anzahl der Millisekunden, die seit dem System vergangen sind wurde gestartet. Es wickelt alle 49,7 Tage, also seien Sie vorsichtig bei der Messung längeren Intervallen.
  • GetTickCount64 ist eine 64-Bit-Version von GetTickCount, aber es ist ab Windows Vista verfügbar und oben.

OS X (macOS)

OS X (macOS) hat seine eigene Mach absoluten Zeiteinheiten, die einen monotonen Takt darstellen. Der beste Weg zu beginnen ist der Artikel Technischen Q & A QA1398: Mach Absoluter Zeiteinheiten , die (mit den Codebeispielen) beschreibt, wie Mach-spezifische API zu bekommen monotone Zecken verwenden. Es gibt auch eine lokale Frage über sie clock_gettime Alternative in Mac OS X genannt die am Ende können Sie verlassen ein bisschen verwirrt, was mit dem möglichen Wert Überlauf zu tun, weil die Zählerfrequenz in Form von Zähler und Nenner verwendet wird. Also, ein kurzes Beispiel, wie die verstrichene Zeit zu bekommen:

  • erhält die Taktfrequenz Zähler und Nenner:

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

    Sie brauchen das nur einmal zu tun.

  • abfragen, um den aktuellen Tick-Wert mit mach_absolute_time:

    uint64_t tick_value = mach_absolute_time ();
    
  • skalieren die Zecken auf die verstrichene Zeit, das heißt bis Mikrosekunden, wobei vorher abgefragt Zähler und Nenner:

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

    Die Grundidee einen Überlauf zu verhindern, ist die Zecken, um die gewünschte Genauigkeit zu skalieren, bevor Sie den Zähler und Nenner mit. Da die anfängliche Timer-Auflösung in Nanosekunden ist, teilen wir es von 1000 Mikrosekunden zu erhalten. Sie können den gleichen Ansatz in time_mac.c . Wenn Sie wirklich eine Nanosekunde Genauigkeit benötigen betrachten das Lesen der Wie kann ich mach_absolute_time ohne überzulaufen? .

Linux und UNIX

Der clock_gettime Anruf ist die beste Art und Weise auf jedem POSIX freundlichen System. Es kann einige Zeit aus verschiedenen Taktquellen abfragen, und das, was wir brauchen, ist CLOCK_MONOTONIC. Nicht alle Systeme, die clock_gettime Unterstützung CLOCK_MONOTONIC haben, so das erste, was Sie tun müssen, um die Verfügbarkeit überprüfen:

  • wenn _POSIX_MONOTONIC_CLOCK auf einen Wert >= 0 definiert ist, bedeutet dies, dass CLOCK_MONOTONIC ist verfügbar;
  • Wenn _POSIX_MONOTONIC_CLOCK definiert ist es zu 0 bedeutet, dass Sie sollten zusätzlich prüfen, ob es zur Laufzeit funktioniert, schlage ich vor sysconf zu verwenden:

    #include <unistd.h>
    
    #ifdef _SC_MONOTONIC_CLOCK
    if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
        /* A monotonic clock presents */
    }
    #endif
    
  • sonst eine monotone Uhr nicht unterstützt wird und Sie eine Ausweichstrategie verwendet werden soll (siehe unten).

Die Verwendung von clock_gettime ist ziemlich geradlinig:

  • den Zeitwert erhalten:

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

    Ich habe hier die Zeit zu Mikrosekunden verkleinert.

  • berechnet man die Differenz mit dem vorherigen Zeitwert erhielt die gleiche Art und Weise:

    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;
    

Die beste Rücklenkungsstrategie ist es, die gettimeofday Aufruf zu verwenden: es ist keine monotone, aber es bietet eine sehr gute Auflösung. Die Idee ist die gleiche wie bei clock_gettime, aber einen Zeitwert zu erhalten, sollten Sie:

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

Auch hier wird der Zeitwert auf Mikrosekunden herunterskaliert.

SGI IRIX

IRIX hat den clock_gettime Ruf, aber es fehlt CLOCK_MONOTONIC. Stattdessen hat er seine eigene monotone Taktquelle als CLOCK_SGI_CYCLE definiert, die Sie anstelle von CLOCK_MONOTONIC mit clock_gettime verwenden sollten.

Solaris und HP-UX

Solaris hat seine eigene hochauflösende Timer-Schnittstelle gethrtime, die den aktuellen Zeitgeberwert in Nanosekunden zurück. Obwohl die neueren Versionen von Solaris clock_gettime haben, können Sie halten Sie sich an gethrtime wenn Sie alte Solaris-Versionen unterstützen müssen.

Die Verwendung ist einfach:

#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 fehlt clock_gettime, aber es unterstützt gethrtime, die Sie in der gleichen Weise wie unter Solaris verwenden sollten.

BeOS

BeOS hat auch eine eigene hochauflösende Timer-Schnittstelle system_time, die die Anzahl von Mikrosekunden zurück haben verstrichen ist, seit dem Computer gestartet wurde.

Beispiel Nutzung:

#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 verfügt über eine eigene API hochgenauen Zeitstempel abzurufen:

  • abfragen, um eine Timer-Frequenz (Ticks pro Einheit) mit DosTmrQueryFreq (für GCC Compiler):

    #define INCL_DOSPROFILE
    #define INCL_DOSERRORS
    #include <os2.h>
    #include <stdint.h>
    
    ULONG freq;
    
    DosTmrQueryFreq (&freq);
    
  • abfragen, um den aktuellen Wert Ticks mit DosTmrQueryTime:

    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;
    }
    
  • skalieren die Zecken auf die verstrichene Zeit, das heißt bis Mikrosekunden:

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

Beispiel Implementierung

Sie können einen Blick auf die plibsys Bibliothek, die alle oben beschriebenen Strategien implementiert (siehe ptimeprofiler * .c für Details).

timespec_get von C11

Gibt bis zu Nanosekunden, um die Auflösung der Implementierung abgerundet.

Sieht aus wie eine ANSI Gaunerei von POSIX‘clock_gettime.

Beispiel: ein printf alle 100ms auf Ubuntu 15.10 getan wird:

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

Die C11 N1570 Standardentwurf 7.27.2.5 "Die timespec_get Funktion sagt":

  

Wenn die Basis TIME_UTC, das tv_sec Mitglied ist auf die Anzahl der Sekunden seit einem Set   Implementierung definierte Epoche, abgeschnitten zu einem ganzen Wert und das tv_nsec Enden   auf die ganzzahligen Anzahl von Nanosekunden, um die Auflösung des Systemtaktes aufgerundet. (321)

     

321) Obwohl eine Struktur timespec Objekt mal mit Nanosekunde Auflösung beschreibt, die zur Verfügung stehenden   Auflösung ist systemabhängig und kann sogar größer sein als 1 Sekunde.

C ++ 11 bekam auch std::chrono::high_resolution_clock: C ++ Cross-Platform-High-Resolution Timer

glibc 2.21 Implementierung

Kann unter sysdeps/posix/timespec_get.c wie:

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

so klar:

  • nur TIME_UTC wird derzeit unterstützt

  • nach vorne __clock_gettime (CLOCK_REALTIME, ts), die ein POSIX-API ist: http : //pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html

    Linux x86-64 hat einen clock_gettime Systemaufruf.

    Beachten Sie, dass dies nicht ein ausfallsichere Mikro-Benchmark-Methode, weil:

    • man clock_gettime sagt, dass diese Maßnahme Diskontinuitäten haben können, wenn Sie einige Systemzeit-Einstellung ändern, während Ihr Programm läuft. Dies sollte ein seltenes Ereignis natürlich sein, und man könnte es ignorieren kann.

    • Diese misst Wand Zeit, so dass, wenn der Planer über Ihre Aufgabe zu vergessen entscheidet, wird es länger.

    • erscheinen laufen

    Aus diesen Gründen getrusage() könnte ein besseren besser POSIX-Benchmarking-Tool sein, obwohl es niedrige Mikrosekunde höchste Präzision.

    Weitere Informationen unter: Measure Zeit in Linux - Zeit vs Uhr vs getrusage vs clock_gettime vs gettimeofday vs timespec_get

Die beste Präzision, die Sie möglicherweise erhalten können, ist durch die Verwendung der x86-only „RDTSC“ Anweisung, die Uhr-Ebene Auflösung (ne muss natürlich berücksichtigen die Kosten für die selbst RDTSC rufen zur Verfügung stellen kann, die sein kann leicht auf Anwendungsstart gemessen).

Der Haupt Haken dabei ist, die Anzahl der Takte pro Sekunde gemessen wird, die nicht zu schwer sein sollte.

Die akzeptierte Antwort ist gut enough.But meine Lösung ist mehr simple.I nur Test unter Linux verwenden gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.

Alse Verwendung gettimeofday ist die tv_sec der Teil der zweiten und der tv_usec ist Mikrosekunden , nicht Millisekunden .

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

Es Druck:

1522139691342 1522139692342, genau eine Sekunde.

Unter Windows:

SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top