Comment mesurer le temps en millisecondes en utilisant ANSI C?

En utilisant uniquement C ANSI, existe-t-il un moyen de mesurer le temps avec une précision en millisecondes ou plus? Je parcourais le temps.h mais je n’ai trouvé que des fonctions de seconde précision.

Il n’y a pas de fonction C ANSI qui fournit une résolution temporelle supérieure à 1 seconde, mais la fonction POSIX gettimeofday fournit une résolution en microsecondes. La fonction horloge ne mesure que la durée d’exécution d’un processus 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); 

Ceci renvoie le Time elapsed: 1.000870 sur ma machine.

 #include  clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000); 

J’utilise toujours la fonction clock_gettime (), en retournant l’heure de l’horloge CLOCK_MONOTONIC. Le temps renvoyé correspond à la durée, en secondes et en nanosecondes, depuis un point non spécifié du passé, tel que le démarrage du système de l’époque.

 #include  #include  #include  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 interestd in measuring clock_gettime(CLOCK_MONOTONIC, &end); uint64_t timeElapsed = timespecDiff(&end, &start); } 

Implémenter une solution portable

Comme il a déjà été mentionné ici qu’il n’existe pas de solution ANSI adéquate avec une précision suffisante pour le problème de mesure du temps, je souhaite vous expliquer comment 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:

  • horloge monotone;
  • horodatage actuel (date).

Le premier utilise un compteur d’horloge monotone (parfois appelé compteur de ticks) qui compte les ticks à une fréquence prédéfinie. Si vous avez une valeur de ticks et que la fréquence est connue, vous pouvez facilement convertir les ticks en temps écoulé. En fait, il n’est pas garanti qu’une horloge monotone reflète l’heure actuelle du système, elle peut aussi compter les ticks depuis le démarrage du système. Mais cela garantit qu’une horloge est toujours plus rapide, quel que soit l’état du système. En général, la fréquence est liée à une source haute résolution matérielle, c’est pourquoi elle fournit une grande précision (dépend du matériel, mais la plupart des matériels modernes ne rencontrent aucun problème avec les sources d’horloge haute résolution).

La seconde méthode fournit une valeur de date (date) basée sur la valeur d’horloge système actuelle. Il peut également avoir une haute résolution, mais il présente un inconvénient majeur: ce type de valeur temporelle peut être affecté par différents ajustements de l’heure système, par exemple, changement de fuseau horaire, changement d’heure d’été, mise à jour du serveur NTP, 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 de temps est moins fiable que le premier.

Donc, la première règle dans la mesure de l’intervalle de temps est d’utiliser une horloge monotone si possible. Il est généralement très précis et fiable.

Stratégie de repli

Lors de l’implémentation d’une solution portable, il convient d’envisager une stratégie de repli: utilisez une horloge monotone si disponible et l’approche par 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é Acquisition d’horodatages à haute résolution sur MSDN concernant la mesure du temps sur Windows, qui décrit tous les détails à connaître sur le support logiciel et matériel. Pour acquérir un horodatage de haute précision sous Windows, vous devez:

  • interroger une fréquence de timer (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 sur le démarrage du système, vous devez donc l’obtenir une seule fois.

  • interroger la valeur de ticks actuelle avec QueryPerformanceCounter :

     LARGE_INTEGER tcounter; LARGE_INTEGER tick_value; if (QueryPerformanceCounter (&tcounter) != 0) tick_value = tcounter.QuadPart; 
  • redimensionnez les tics au temps écoulé, c’est-à-dire en microsecondes:

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

Selon Microsoft, vous ne devriez pas avoir de 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 indique le nombre de millisecondes écastings depuis le démarrage du système. Il enveloppe tous les 49,7 jours, donc soyez prudent en mesurant des intervalles plus longs.
  • GetTickCount64 est une version 64 bits de GetTickCount , mais elle est disponible à partir de Windows Vista et versions GetTickCount .

OS X (macOS)

OS X (macOS) possède ses propres unités de temps absolues Mach qui représentent une horloge monotone. La meilleure façon de commencer est l’article Apple Q & R QA1398: Unités absolues Mach qui décrit (avec les exemples de code) comment utiliser une API spécifique à Mach pour obtenir des ticks monotones. Il y a aussi une question locale à ce sujet appelée clock_gettime alternative dans Mac OS X qui à la fin peut vous laisser un peu perplexe sur ce qu’il faut faire avec le dépassement de valeur possible car la fréquence du compteur est utilisée sous forme de numérateur et de dénominateur. Donc, un court exemple sur la façon d’obtenir le temps écoulé:

  • obtenir le numérateur de fréquence d’horloge et le dénominateur:

     #include  #include  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 devez le faire une seule fois.

  • interroger la valeur de tick actuelle avec mach_absolute_time :

     uint64_t tick_value = mach_absolute_time (); 
  • Échelonner les graduations en fonction du temps écoulé, c’est-à-dire en microsecondes, en utilisant le numérateur et le dénominateur précédemment interrogés:

     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 du temporisateur est en nanosecondes, nous la divisons par 1000 pour obtenir des microsecondes. Vous pouvez trouver la même approche que dans time_mac.c de Chromium . Si vous avez vraiment besoin d’une précision à la nanoseconde, envisagez de lire la page Comment puis-je utiliser mach_absolute_time sans déborder? .

Linux et UNIX

L’appel clock_gettime est votre meilleur moyen sur tout système compatible POSIX. Il peut interroger le temps de différentes sources d’horloge, et celui dont nous avons besoin est CLOCK_MONOTONIC . Tous les systèmes ayant clock_gettime prennent clock_gettime charge CLOCK_MONOTONIC . La première chose à faire est donc de vérifier sa disponibilité:

  • si _POSIX_MONOTONIC_CLOCK est défini sur une valeur >= 0 cela signifie que CLOCK_MONOTONIC est disponible;
  • Si _POSIX_MONOTONIC_CLOCK est défini sur 0 cela signifie que vous devez également vérifier si cela fonctionne à l’exécution, je suggère d’utiliser sysconf :

     #include  #ifdef _SC_MONOTONIC_CLOCK if (sysconf (_SC_MONOTONIC_CLOCK) > 0) { /* A monotonic clock presents */ } #endif 
  • sinon, une horloge monotone n’est pas prise en charge et vous devez utiliser une stratégie de repli (voir ci-dessous).

L’utilisation de clock_gettime est assez simple:

  • obtenir la valeur de temps:

     #include  #include  #include  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 consiste à utiliser l’appel gettimeofday : ce n’est pas un monotone, mais cela donne une bonne résolution. L’idée est la même qu’avec clock_gettime , mais pour obtenir une valeur de temps, vous devez:

 #include  #include  #include  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’appel clock_gettime , mais il manque CLOCK_MONOTONIC . Au lieu de cela, il a sa propre source d’horloge monotone définie comme CLOCK_SGI_CYCLE que vous devez utiliser à la place de CLOCK_MONOTONIC avec clock_gettime .

Solaris et HP-UX

Solaris possède sa propre interface de timer à 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 restr gethrtime à gethrtime si vous devez prendre en charge d’anciennes versions de Solaris.

L’utilisation est simple:

 #include  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 ne clock_gettime pas de clock_gettime , mais prend en charge gethrtime que vous devez utiliser de la même manière que sur Solaris.

BeOS

BeOS possède également sa propre interface de temporisation haute résolution system_time qui renvoie le nombre de microsecondes écastings depuis le démarrage de l’ordinateur.

Exemple d’utilisation:

 #include  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 a sa propre API pour récupérer des horodatages de haute précision:

  • interroger une fréquence de temporisation (ticks par unité) avec DosTmrQueryFreq (pour le compilateur GCC):

     #define INCL_DOSPROFILE #define INCL_DOSERRORS #include  #include  ULONG freq; DosTmrQueryFreq (&freq); 
  • interroger la valeur actuelle des ticks 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; } 
  • redimensionnez les tics au temps écoulé, c'est-à-dire en microsecondes:

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

Exemple d'implémentation

Vous pouvez consulter la bibliothèque plibsys qui implémente toutes les stratégies décrites ci-dessus (voir ptimeprofiler * .c pour plus de détails).

La meilleure précision que vous pouvez obtenir est l’utilisation de l’instruction “rdtsc” uniquement x86, qui peut fournir une résolution au niveau de l’horloge (il faut bien sûr prendre en compte le coût de l’appel rdtsc lui-même, qui peut être facilement mesuré sur démarrage de l’application).

La prise principale consiste à mesurer le nombre d’horloges par seconde, ce qui ne devrait pas être trop difficile.

timespec_get de C11 renvoie jusqu’à nanosecondes, arrondi à la résolution de l’implémentation.

On dirait un ripoff ANSI de POSIX ‘ clock_gettime .

Exemple: un printf est effectué toutes les 100ms sur Ubuntu 15.10:

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

La version standard C11 N1570 7.27.2.5 La fonction timespec_get indique:

Si base est TIME_UTC, le membre tv_sec est défini sur le nombre de secondes écastings depuis une époque définie par l’implémentation, tronquée sur une valeur entière et le membre tv_nsec sur le nombre entier de nanosecondes, arrondi à la résolution de l’horloge système. (321)

321) Bien qu’un object struct timespec décrit des temps avec une résolution en nanosecondes, la résolution disponible dépend du système et peut même dépasser 1 seconde.

C ++ 11 a également obtenu std::chrono::high_resolution_clock : Minuteur haute résolution cross-platform C ++

glibc 2.21 l’ implémente 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:

  • seul TIME_UTC est actuellement pris en charge

  • il passe à __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 système clock_gettime .

    Notez que ce n'est pas une méthode de micro-benchmarking à l'épreuve des défaillances car:

    • man clock_gettime dit que cette mesure peut avoir des discontinuités si vous modifiez un paramètre de l'heure du système pendant que votre programme s'exécute. Cela devrait être un événement rare bien sûr, et vous pourrez peut-être l'ignorer.

    • Cela mesure le temps du mur, donc si le planificateur décide d'oublier votre tâche, il semblera fonctionner plus longtemps.

    Pour ces raisons, getrusage() pourrait être un meilleur outil d'parsing comparative POSIX, malgré sa précision maximale inférieure à la microseconde.

    Plus d'informations sur: Mesurer le temps dans Linux - temps vs horloge vs getrusage vs clock_gettime vs gettimeofday vs timespec_get?

La réponse acceptée est suffisante. Mais ma solution est plus simple. Je teste sous Linux, utilisez gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.

Alse utilise gettimeofday , tv_sec est la partie de seconde, et tv_usec est en microsecondes , pas en 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.

Sous windows:

 SYSTEMTIME t; GetLocalTime(&t); swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);