Gibt es eine Möglichkeit, die Zeit nur mit ANSI C mit einer Genauigkeit von Millisekunden oder mehr zu messen? Ich habe time.h durchsucht, aber nur Funktionen mit zweiter Genauigkeit gefunden.
Gibt es eine Möglichkeit, die Zeit nur mit ANSI C mit einer Genauigkeit von Millisekunden oder mehr zu messen? Ich habe time.h durchsucht, aber nur Funktionen mit zweiter Genauigkeit gefunden.
Antworten:
Es gibt keine ANSI C-Funktion, die eine bessere Auflösung als 1 Sekunde gettimeofday
bietet, aber die POSIX-Funktion bietet eine Mikrosekundenauflösung. Die Uhrfunktion misst nur die Zeit, die ein Prozess für die Ausführung aufgewendet hat, und ist auf vielen Systemen nicht genau.
Sie können diese Funktion folgendermaßen verwenden:
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);
Dies kehrt Time elapsed: 1.000870
auf meinem Computer zurück.
timeval::tv_usec
es immer unter einer Sekunde ist, es ist eine Schleife. Dh um Zeitunterschiede von mehr als 1 Sekunde zu nehmen, sollten Sie:long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
timersub
Funktion enthalten. Wir können die tval_result
Werte (tv_sec und tv_usec) unverändert verwenden.
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
CLOCKS_PER_SEC / 1000
möglicherweise ungenau ist, was sich auf das Endergebnis auswirken kann (obwohl es meiner Erfahrung CLOCKS_PER_SEC
nach immer ein Vielfaches von 1000 war). Das Tun (1000 * clock()) / CLOCKS_PER_SEC
ist weniger anfällig für Ungenauigkeiten bei der Teilung, andererseits ist es anfälliger für Überläufe. Nur einige Punkte zu beachten.
Ich benutze immer die Funktion clock_gettime () und gebe die Zeit von der Uhr CLOCK_MONOTONIC zurück. Die zurückgegebene Zeit ist die Zeit in Sekunden und Nanosekunden seit einem nicht näher bezeichneten Zeitpunkt in der Vergangenheit, z. B. dem 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);
}
clock_gettime(CLOCK_MONOTONIC, ...)
und es sogar das Feature-Test-Makro gibt _POSIX_MONOTONIC_CLOCK
.
Implementierung einer tragbaren Lösung
Da hier bereits erwähnt wurde, dass es keine richtige ANSI-Lösung mit ausreichender Genauigkeit für das Zeitmessproblem gibt, möchte ich darüber schreiben, wie man eine tragbare und wenn möglich hochauflösende Zeitmesslösung erhält.
Monotone Uhr gegen Zeitstempel
Generell gibt es zwei Möglichkeiten der Zeitmessung:
Der erste verwendet einen monotonen Taktzähler (manchmal wird er als Tick-Zähler bezeichnet), der Ticks mit einer vordefinierten Frequenz zählt. Wenn Sie also einen Tick-Wert haben und die Frequenz bekannt ist, können Sie Ticks einfach in verstrichene Zeit umwandeln. Es ist tatsächlich nicht garantiert, dass eine monotone Uhr die aktuelle Systemzeit in irgendeiner Weise widerspiegelt. Sie kann auch Ticks seit einem Systemstart zählen. Es garantiert jedoch, dass eine Uhr unabhängig vom Systemstatus immer stärker hochgefahren wird. Normalerweise ist die Frequenz an eine hochauflösende Hardwarequelle gebunden, weshalb sie eine hohe Genauigkeit bietet (abhängig von der Hardware, aber der Großteil der modernen Hardware hat keine Probleme mit hochauflösenden Taktquellen).
Der zweite Weg liefert einen (Datums-) Zeitwert basierend auf dem aktuellen Systemuhrwert. Es kann auch eine hohe Auflösung haben, hat jedoch einen großen Nachteil: Diese Art von Zeitwert kann durch verschiedene Systemzeitanpassungen beeinflusst werden, z. B. Zeitzonenänderung, Änderung der Sommerzeit (DST), Aktualisierung des NTP-Servers, System-Ruhezustand usw. auf. Unter bestimmten Umständen können Sie einen negativen Wert für die verstrichene Zeit erhalten, der zu einem undefinierten Verhalten führen kann. Tatsächlich ist diese Art von Zeitquelle weniger zuverlässig als die erste.
Die erste Regel bei der Zeitintervallmessung ist daher, wenn möglich eine monotone Uhr zu verwenden. Es hat normalerweise eine hohe Präzision und ist vom Design her zuverlässig.
Fallback-Strategie
Bei der Implementierung einer tragbaren Lösung sollte eine Fallback-Strategie in Betracht gezogen werden: Verwenden Sie eine monotone Uhr, falls verfügbar, und greifen Sie auf Zeitstempel zurück, wenn das System keine monotone Uhr enthält.
Windows
Es gibt einen großartigen Artikel mit dem Titel " Erfassen hochauflösender Zeitstempel auf MSDN zur Zeitmessung unter Windows", in dem alle Details beschrieben werden, die Sie möglicherweise zur Software- und Hardwareunterstützung benötigen. Um einen hochpräzisen Zeitstempel unter Windows zu erhalten, sollten Sie:
Abfragen einer Timer-Frequenz (Ticks pro Sekunde) mit QueryPerformanceFrequency :
LARGE_INTEGER tcounter;
LARGE_INTEGER freq;
if (QueryPerformanceFrequency (&tcounter) != 0)
freq = tcounter.QuadPart;
Die Timer-Frequenz ist beim Systemstart festgelegt, sodass Sie sie nur einmal abrufen müssen.
Fragen Sie den aktuellen Ticks-Wert mit QueryPerformanceCounter ab :
LARGE_INTEGER tcounter;
LARGE_INTEGER tick_value;
if (QueryPerformanceCounter (&tcounter) != 0)
tick_value = tcounter.QuadPart;
Skalieren Sie die Ticks auf die verstrichene Zeit, dh auf Mikrosekunden:
LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);
Laut Microsoft sollten Sie unter Windows XP und späteren Versionen in den meisten Fällen keine Probleme mit diesem Ansatz haben. Sie können aber auch zwei Fallback-Lösungen unter Windows verwenden:
GetTickCount
, ist jedoch ab Windows Vista und höher verfügbar.OS X (macOS)
OS X (macOS) verfügt über eigene Mach-Absolutzeiteinheiten, die eine monotone Uhr darstellen. Der beste Einstieg ist der Apple-Artikel Technische Fragen und Antworten QA1398: Mach Absolute Time Units, in dem (anhand der Codebeispiele) beschrieben wird, wie mithilfe der Mach-spezifischen API monotone Ticks abgerufen werden. Es gibt auch eine lokale Frage mit dem Namen clock_gettime-Alternative in Mac OS X, die Sie am Ende möglicherweise etwas verwirrt darüber macht, was Sie mit dem möglichen Wertüberlauf tun sollen, da die Zählerfrequenz in Form von Zähler und Nenner verwendet wird. Ein kurzes Beispiel, wie man die verstrichene Zeit erhält:
Holen Sie sich den Taktfrequenzzä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 müssen das nur einmal tun.
Fragen Sie den aktuellen Tick-Wert ab mit mach_absolute_time
:
uint64_t tick_value = mach_absolute_time ();
Skalieren Sie die Ticks mit dem zuvor abgefragten Zähler und Nenner auf die verstrichene Zeit, dh auf Mikrosekunden:
uint64_t value_diff = tick_value - prev_tick_value;
/* To prevent overflow */
value_diff /= 1000;
value_diff *= freq_num;
value_diff /= freq_denom;
Die Hauptidee, um einen Überlauf zu verhindern, besteht darin, die Ticks auf die gewünschte Genauigkeit zu verkleinern, bevor Zähler und Nenner verwendet werden. Da die anfängliche Timer-Auflösung in Nanosekunden angegeben ist, teilen wir sie durch 1000
Mikrosekunden. Sie finden den gleichen Ansatz wie in time_mac.c von Chromium . Wenn Sie wirklich eine Genauigkeit von Nanosekunden benötigen, lesen Sie bitte Wie kann ich mach_absolute_time verwenden, ohne überzulaufen? .
Linux und UNIX
Der clock_gettime
Anruf ist der beste Weg auf jedem POSIX-freundlichen System. Es kann die Zeit von verschiedenen Uhrquellen abfragen, und die, die wir brauchen, ist CLOCK_MONOTONIC
. Nicht alle Systeme, die clock_gettime
Unterstützung CLOCK_MONOTONIC
bieten. Daher müssen Sie zunächst die Verfügbarkeit überprüfen:
_POSIX_MONOTONIC_CLOCK
ein Wert definiert ist, >= 0
bedeutet dies, dass dieser CLOCK_MONOTONIC
verfügbar ist.Wenn dies _POSIX_MONOTONIC_CLOCK
definiert ist, 0
bedeutet dies, dass Sie zusätzlich prüfen sollten, ob es zur Laufzeit funktioniert. Ich empfehle Folgendes sysconf
:
#include <unistd.h>
#ifdef _SC_MONOTONIC_CLOCK
if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
/* A monotonic clock presents */
}
#endif
Die Verwendung von clock_gettime
ist ziemlich einfach:
Holen Sie sich den Zeitwert:
#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 die Zeit hier auf Mikrosekunden reduziert.
Berechnen Sie die Differenz zum vorherigen Zeitwert auf dieselbe 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 Fallback-Strategie besteht darin, den gettimeofday
Anruf zu verwenden: Er ist nicht monoton, bietet jedoch eine recht gute Auflösung. Die Idee ist die gleiche wie bei clock_gettime
, aber um 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 verkleinert.
SGI IRIX
IRIX hat den clock_gettime
Anruf, aber es fehlt CLOCK_MONOTONIC
. Stattdessen hat er seine eigene monotone Taktquelle definiert als CLOCK_SGI_CYCLE
die sollten Sie statt verwenden CLOCK_MONOTONIC
mit clock_gettime
.
Solaris und HP-UX
Solaris verfügt über eine eigene hochauflösende Timer-Schnittstelle, gethrtime
die den aktuellen Timer-Wert in Nanosekunden zurückgibt. Obwohl die neueren Versionen von Solaris möglicherweise vorhanden sind clock_gettime
, können Sie sich daran halten, 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
was Sie auf die gleiche Weise wie unter Solaris verwenden sollten.
BeOS
BeOS verfügt außerdem über eine eigene hochauflösende Timer-Schnittstelle, system_time
die die Anzahl der Mikrosekunden zurückgibt, die seit dem Start des Computers vergangen sind.
Anwendungsbeispiel:
#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 zum Abrufen hochpräziser Zeitstempel:
Abfrage einer Timerfrequenz (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);
Fragen Sie den aktuellen Ticks-Wert ab 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 Sie die Ticks auf die verstrichene Zeit, dh auf Mikrosekunden:
uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);
Beispielimplementierung
Sie können sich die plibsys- Bibliothek ansehen, die alle oben beschriebenen Strategien implementiert (Details siehe ptimeprofiler * .c).
timespec_get
: stackoverflow.com/a/36095407/895245
timespec_get
ist nicht monoton.
timespec_get
von C11
Gibt bis zu Nanosekunden zurück, gerundet auf die Auflösung der Implementierung.
Sieht aus wie eine ANSI-Abzocke von POSIX ' clock_gettime
.
Beispiel: a printf
wird unter Ubuntu 15.10 alle 100 ms ausgeführt:
#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;
}
Der C11 N1570 Standardentwurf 7.27.2.5 "Die Funktion timespec_get sagt":
Wenn base TIME_UTC ist, wird das Mitglied tv_sec auf die Anzahl der Sekunden seit einer durch die Implementierung definierten Epoche gesetzt, auf einen ganzen Wert abgeschnitten, und das Mitglied tv_nsec wird auf die ganzzahlige Anzahl von Nanosekunden gesetzt, gerundet auf die Auflösung des Systemtakts. (321)
321) Obwohl ein Struktur-Zeitspezifikationsobjekt Zeiten mit einer Auflösung von Nanosekunden beschreibt, ist die verfügbare Auflösung systemabhängig und kann sogar größer als 1 Sekunde sein.
C ++ 11 hat auch std::chrono::high_resolution_clock
: C ++ Plattformübergreifender hochauflösender Timer
Implementierung von glibc 2.21
Kann unter gefunden werden sysdeps/posix/timespec_get.c
als:
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
Es __clock_gettime (CLOCK_REALTIME, ts)
wird an eine POSIX-API weitergeleitet: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
Linux x86-64 hat einen clock_gettime
Systemaufruf.
Beachten Sie, dass dies keine ausfallsichere Mikro-Benchmarking-Methode ist, weil:
man clock_gettime
sagt, dass diese Maßnahme Diskontinuitäten aufweisen kann, wenn Sie eine Systemzeiteinstellung ändern, während Ihr Programm ausgeführt wird. Dies sollte natürlich ein seltenes Ereignis sein, und Sie können es möglicherweise ignorieren.
Dies misst die Wandzeit. Wenn der Planer also beschließt, Ihre Aufgabe zu vergessen, wird sie anscheinend länger ausgeführt.
Aus diesen Gründen getrusage()
könnte es ein besseres, besseres POSIX-Benchmarking-Tool sein, trotz der geringeren maximalen Genauigkeit von Mikrosekunden.
Weitere Informationen unter: Zeit unter Linux messen - Zeit gegen Uhr gegen getrusage gegen clock_gettime gegen gettimeofday gegen timespec_get?
Die beste Präzision, die Sie möglicherweise erzielen können, ist die Verwendung des x86-Befehls "rdtsc", der eine Auflösung auf Taktebene bietet (ne muss natürlich die Kosten des rdtsc-Aufrufs selbst berücksichtigen, an denen leicht gemessen werden kann Anwendungsstart).
Der Hauptfang hierbei ist die Messung der Anzahl der Uhren pro Sekunde, die nicht zu schwer sein sollte.
Die akzeptierte Antwort ist gut genug. Aber meine Lösung ist einfacher. Ich teste nur unter Linux und verwende gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.
Alse verwenden gettimeofday
, das tv_sec
ist der Teil der zweiten, und das 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 druckt:
1522139691342
1522139692342
genau eine Sekunde.