En utilisant uniquement ANSI C, existe-t-il un moyen de mesurer le temps avec une précision de quelques millisecondes ou plus? Je parcourais time.h mais je n'ai trouvé que des fonctions de seconde précision.
En utilisant uniquement ANSI C, existe-t-il un moyen de mesurer le temps avec une précision de quelques millisecondes ou plus? Je parcourais time.h mais je n'ai trouvé que des fonctions de seconde précision.
Réponses:
Il n'y a pas de fonction ANSI C qui offre une résolution temporelle supérieure à 1 seconde, mais la fonction POSIX gettimeofday
fournit une résolution en microsecondes. La fonction d'horloge ne mesure que le temps qu'un processus a passé à s'exécuter et n'est pas précise sur de nombreux systèmes.
Vous pouvez utiliser cette fonction comme ceci:
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);
Cela revient Time elapsed: 1.000870
sur ma machine.
timeval::tv_usec
prend toujours moins d'une seconde, c'est en boucle. C'est-à-dire que pour prendre des différences de temps supérieures à 1 s, vous devez:long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
timersub
fonction. Nous pouvons utiliser les tval_result
valeurs (tv_sec et tv_usec) telles quelles.
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
CLOCKS_PER_SEC / 1000
pourrait éventuellement être inexacte, ce qui pourrait affecter le résultat final (bien que dans mon expérience CLOCKS_PER_SEC
ait toujours été un multiple de 1000). Faire (1000 * clock()) / CLOCKS_PER_SEC
est moins sensible à l'inexactitude de la division, mais d'un autre côté est plus susceptible de déborder. Juste quelques problèmes à considérer.
J'utilise toujours la fonction clock_gettime (), renvoyant l'heure de l'horloge CLOCK_MONOTONIC. Le temps renvoyé est la durée, en secondes et en nanosecondes, depuis un moment non spécifié dans le passé, tel que le démarrage du système de l'époque.
#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, ...)
et il y a même la macro de test de fonctionnalités _POSIX_MONOTONIC_CLOCK
.
Mettre en œuvre une solution portable
Comme il a déjà été mentionné ici qu'il n'y a pas de solution ANSI appropriée avec une précision suffisante pour le problème de la mesure du temps, je veux écrire sur les moyens d'obtenir une solution de mesure du temps portable et, si possible, à haute résolution.
Horloge monotone et horodatage
De manière générale, il existe deux méthodes de mesure du temps:
Le premier utilise un compteur d'horloge monotone (parfois appelé compteur de ticks) qui compte les ticks avec une fréquence prédéfinie, donc si vous avez une valeur de ticks et que la fréquence est connue, vous pouvez facilement convertir les ticks en temps écoulé. Il n'est en fait pas garanti qu'une horloge monotone reflète en aucune façon l'heure actuelle du système, elle peut également compter les ticks depuis un démarrage du système. Mais cela garantit qu'une horloge est toujours exécutée de manière croissante quel que soit l'état du système. Habituellement, la fréquence est liée à une source matérielle haute résolution, c'est pourquoi elle offre une grande précision (dépend du matériel, mais la plupart du matériel moderne n'a aucun problème avec les sources d'horloge haute résolution).
La seconde méthode fournit une valeur d'heure (date) basée sur la valeur actuelle de l'horloge système. Il peut également avoir une résolution élevée, mais il présente un inconvénient majeur: ce type de valeur d'heure peut être affecté par différents ajustements de l'heure du système, c'est-à-dire le changement de fuseau horaire, le changement d'heure d'été (DST), la mise à jour du serveur NTP, l'hibernation du système, etc. sur. Dans certaines circonstances, vous pouvez obtenir une valeur de temps écoulé négative qui peut conduire à un comportement indéfini. En fait, ce type de source horaire est moins fiable que la première.
Ainsi, la première règle dans la mesure d'intervalle de temps est d'utiliser une horloge monotone si possible. Il a généralement une haute précision et est fiable de par sa conception.
Stratégie de repli
Lors de la mise en œuvre d'une solution portable, il vaut la peine d'envisager une stratégie de repli: utiliser une horloge monotone si disponible et une approche de repli vers l'horodatage s'il n'y a pas d'horloge monotone dans le système.
les fenêtres
Il existe un excellent article intitulé Acquérir des horodatages haute résolution sur MSDN sur la mesure du temps sous Windows, qui décrit tous les détails que vous pourriez avoir besoin de connaître sur la prise en charge logicielle et matérielle. Pour acquérir un horodatage de haute précision sous Windows, vous devez:
interroger une fréquence de minuterie (ticks par seconde) avec QueryPerformanceFrequency :
LARGE_INTEGER tcounter;
LARGE_INTEGER freq;
if (QueryPerformanceFrequency (&tcounter) != 0)
freq = tcounter.QuadPart;
La fréquence du minuteur est fixée au démarrage du système, vous n'avez donc besoin de l'obtenir qu'une seule fois.
interrogez la valeur actuelle des graduations avec QueryPerformanceCounter :
LARGE_INTEGER tcounter;
LARGE_INTEGER tick_value;
if (QueryPerformanceCounter (&tcounter) != 0)
tick_value = tcounter.QuadPart;
mettre à l'échelle les graduations en fonction du temps écoulé, c'est-à-dire en microsecondes:
LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);
Selon Microsoft, vous ne devriez avoir aucun problème avec cette approche sur Windows XP et les versions ultérieures dans la plupart des cas. Mais vous pouvez également utiliser deux solutions de secours sous Windows:
GetTickCount
, mais elle est disponible à partir de Windows Vista et supérieur.OS X (macOS)
OS X (macOS) a ses propres unités de temps absolues Mach qui représentent une horloge monotone. La meilleure façon de commencer est l'article d'Apple Q&R technique QA1398: Mach Absolute Time Units qui décrit (avec les exemples de code) comment utiliser l'API spécifique à Mach pour obtenir des ticks monotones. Il y a aussi une question locale à ce sujet appelée alternative clock_gettime dans Mac OS X qui à la fin peut vous laisser un peu confus quoi faire avec le dépassement de valeur possible car la fréquence du compteur est utilisée sous la forme de numérateur et de dénominateur. Donc, un petit exemple comment obtenir le temps écoulé:
obtenir le numérateur et le dénominateur de la fréquence d'horloge:
#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;
}
}
Vous ne devez le faire qu'une seule fois.
interroger la valeur de graduation actuelle avec mach_absolute_time
:
uint64_t tick_value = mach_absolute_time ();
mettre à l'échelle les graduations en fonction du temps écoulé, c'est-à-dire en microsecondes, en utilisant le numérateur et le dénominateur interrogés précédemment:
uint64_t value_diff = tick_value - prev_tick_value;
/* To prevent overflow */
value_diff /= 1000;
value_diff *= freq_num;
value_diff /= freq_denom;
L'idée principale pour éviter un débordement est de réduire les graduations à la précision souhaitée avant d'utiliser le numérateur et le dénominateur. Comme la résolution initiale de la minuterie est en nanosecondes, nous la divisons par 1000
pour obtenir des microsecondes. Vous pouvez trouver la même approche utilisée dans time_mac.c de Chromium . Si vous avez vraiment besoin d'une précision à la nanoseconde, envisagez de lire le Comment puis-je utiliser mach_absolute_time sans déborder? .
Linux et UNIX
L' clock_gettime
appel est votre meilleur moyen sur n'importe quel système compatible POSIX. Il peut interroger l'heure à partir de différentes sources d'horloge, et celle dont nous avons besoin est CLOCK_MONOTONIC
. Tous les systèmes ne sont pas pris en clock_gettime
charge CLOCK_MONOTONIC
, la première chose à faire est donc de vérifier sa disponibilité:
_POSIX_MONOTONIC_CLOCK
est défini sur une valeur, >= 0
cela signifie qu'il CLOCK_MONOTONIC
est disponible;si elle _POSIX_MONOTONIC_CLOCK
est définie, 0
cela signifie que vous devez en outre vérifier si cela fonctionne au moment de l'exécution, je suggère d'utiliser sysconf
:
#include <unistd.h>
#ifdef _SC_MONOTONIC_CLOCK
if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
/* A monotonic clock presents */
}
#endif
L'utilisation de clock_gettime
est assez simple:
obtenir la valeur de temps:
#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;
}
J'ai réduit le temps en microsecondes ici.
calculer la différence avec la valeur de temps précédente reçue de la même manière:
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 meilleure stratégie de repli est d'utiliser l' gettimeofday
appel: ce n'est pas un monotone, mais il offre une assez bonne résolution. L'idée est la même qu'avec clock_gettime
, mais pour obtenir une valeur de temps, vous devez:
#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;
}
Encore une fois, la valeur de temps est réduite en microsecondes.
SGI IRIX
IRIX a l' clock_gettime
appel, mais il en manque CLOCK_MONOTONIC
. Au lieu de cela, il a sa propre source d'horloge monotone définie que CLOCK_SGI_CYCLE
vous devez utiliser au lieu de CLOCK_MONOTONIC
avec clock_gettime
.
Solaris et HP-UX
Solaris possède sa propre interface de minuterie haute résolution gethrtime
qui renvoie la valeur actuelle du minuteur en nanosecondes. Bien que les nouvelles versions de Solaris puissent avoir clock_gettime
, vous pouvez vous en tenir àgethrtime
si vous avez besoin de prendre en charge les anciennes versions de Solaris.
L'utilisation est simple:
#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 manque clock_gettime
, mais il prend en charge gethrtime
ce que vous devez utiliser de la même manière que sur Solaris.
BeOS
BeOS possède également sa propre interface de minuterie haute résolution system_time
qui renvoie le nombre de microsecondes écoulées depuis le démarrage de l'ordinateur.
Exemple d'utilisation:
#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 possède sa propre API pour récupérer des horodatages de haute précision:
interroger une fréquence de minuterie (ticks par unité) avec DosTmrQueryFreq
(pour le compilateur GCC):
#define INCL_DOSPROFILE
#define INCL_DOSERRORS
#include <os2.h>
#include <stdint.h>
ULONG freq;
DosTmrQueryFreq (&freq);
interroger la valeur actuelle des graduations avec 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;
}
mettre à l'échelle les graduations en fonction du temps écoulé, c'est-à-dire en microsecondes:
uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);
Exemple d'implémentation
Vous pouvez jeter un oeil à la bibliothèque plibsys qui implémente toutes les stratégies décrites ci-dessus (voir ptimeprofiler * .c pour plus de détails).
timespec_get
: stackoverflow.com/a/36095407/895245
timespec_get
n'est pas monotone.
timespec_get
à partir de C11
Renvoie jusqu'à nanosecondes, arrondi à la résolution de l'implémentation.
On dirait une arnaque ANSI de POSIX ' clock_gettime
.
Exemple: a printf
est effectué toutes les 100 ms sur 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;
}
Le projet de norme 7.27.2.5 C11 N1570 "La fonction timespec_get dit":
Si base est TIME_UTC, le membre tv_sec est défini sur le nombre de secondes depuis une époque définie par l'implémentation, tronqué à une valeur entière et le membre tv_nsec est défini sur le nombre entier de nanosecondes, arrondi à la résolution de l'horloge système. (321)
321) Bien qu'un objet struct timespec décrive des temps avec une résolution nanoseconde, la résolution disponible dépend du système et peut même être supérieure à 1 seconde.
C ++ 11 a également obtenu std::chrono::high_resolution_clock
: Minuterie haute résolution multiplateforme C ++
implémentation de la glibc 2.21
Peut être trouvé sous sysdeps/posix/timespec_get.c
comme:
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;
}
si clairement:
seulement TIME_UTC
est actuellement pris en charge
il transmet à __clock_gettime (CLOCK_REALTIME, ts)
, qui est une API POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
Linux x86-64 a un clock_gettime
appel système.
Notez qu'il ne s'agit pas d'une méthode de micro-benchmarking infaillible car:
man clock_gettime
indique que cette mesure peut présenter des discontinuités si vous modifiez certains paramètres d'heure système pendant l'exécution de votre programme. Cela devrait bien sûr être un événement rare et vous pourrez peut-être l'ignorer.
cela mesure le temps du mur, donc si le planificateur décide d'oublier votre tâche, elle semblera fonctionner plus longtemps.
Pour ces raisons getrusage()
pourrait s'agir d'un meilleur outil d'analyse comparative POSIX, malgré sa précision maximale inférieure à la microseconde.
Plus d'informations sur: Mesurer le temps sous Linux - temps vs horloge vs getrusage vs clock_gettime vs gettimeofday vs timespec_get?
La meilleure précision que vous puissiez obtenir est l'utilisation de l'instruction "rdtsc" uniquement x86, qui peut fournir une résolution au niveau de l'horloge (ne doit bien sûr prendre en compte le coût de l'appel rdtsc lui-même, qui peut être mesuré facilement sur démarrage de l'application).
Le problème principal ici est de mesurer le nombre d'horloges par seconde, ce qui ne devrait pas être trop difficile.
La réponse acceptée est assez bonne, mais ma solution est plus simple: je viens de tester sous Linux, utilisez gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.
Alse use gettimeofday
, the tv_sec
is the part of second, and the tv_usec
is microsecondes , not millisecondes .
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;
}
Il imprime:
1522139691342
1522139692342
, exactement une seconde.