Wie die Zeit in Millisekunden mit ANSI C messen?
-
21-08-2019 - |
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.
Lösung
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, dassCLOCK_MONOTONIC
ist verfügbar; -
Wenn
_POSIX_MONOTONIC_CLOCK
definiert ist es zu0
bedeutet, dass Sie sollten zusätzlich prüfen, ob es zur Laufzeit funktioniert, schlage ich vorsysconf
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.htmlLinux 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);