106 votes

La résolution de gettimeofday() est-elle garantie à la microseconde ?

Je me retrouve donc à porter un jeu, qui a été écrit à l'origine pour l'API Win32, vers Linux (enfin, à porter le portage OS X du portage Win32 vers Linux). J'ai implémenté QueryPerformanceCounter en donnant les uSecondes depuis le démarrage du processus :

BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
    gettimeofday(&currentTimeVal, NULL);
    performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
    performanceCount->QuadPart *= (1000 * 1000);
    performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);

    return true;
}

Ceci, couplé à QueryPerformanceFrequency() en donnant une constante de 1000000 comme fréquence, cela fonctionne bien sur ma machine ce qui me donne une variable 64 bits qui contient uSeconds depuis le démarrage du programme. Ainsi, Est-ce que c'est portable ? Je ne veux pas découvrir qu'il fonctionne différemment si le noyau a été compilé d'une certaine manière ou quoi que ce soit d'autre. Je suis d'accord avec le fait qu'il ne soit pas portable sur autre chose que Linux, cependant.

63voto

Louis Brandy Points 4844

Peut-être. Mais vous avez de plus gros problèmes. gettimeofday() peut donner lieu à des chronométrages incorrects s'il existe des processus sur votre système qui modifient le chronomètre (par exemple, ntpd). Sur un linux "normal", cependant, je crois que la résolution de gettimeofday() est de 10us. Il peut faire des sauts en avant et en arrière et le temps, par conséquent, en fonction des processus en cours sur votre système. La réponse à votre question est donc effectivement négative.

Vous devriez vous pencher sur clock_gettime(CLOCK_MONOTONIC) pour les intervalles de temps. Il souffre de plusieurs problèmes moindres dus à des choses comme les systèmes multicœurs et les paramètres d'horloge externes.

Consultez également le clock_getres() fonction.

45voto

Mark Harrison Points 77152

Timing haute résolution, faible surcharge pour les processeurs Intel

Si vous êtes sur du matériel Intel, voici comment lire le compteur d'instructions en temps réel du CPU. Il vous indiquera le nombre de cycles du CPU exécutés depuis le démarrage du processeur. Il s'agit probablement du compteur le plus fin que vous puissiez obtenir pour mesurer les performances.

Notez qu'il s'agit du nombre de cycles du processeur. Sous Linux, vous pouvez obtenir la vitesse du CPU à partir de /proc/cpuinfo et la diviser pour obtenir le nombre de secondes. Convertir ce nombre en double est assez pratique.

Quand j'exécute ceci sur ma boîte, j'obtiens

11867927879484732
11867927879692217
it took this long to call printf: 207485

Voici le Guide du développeur Intel qui donne des tonnes de détails.

#include <stdio.h>
#include <stdint.h>

inline uint64_t rdtsc() {
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx");
    return (uint64_t)hi << 32 | lo;
}

main()
{
    unsigned long long x;
    unsigned long long y;
    x = rdtsc();
    printf("%lld\n",x);
    y = rdtsc();
    printf("%lld\n",y);
    printf("it took this long to call printf: %lld\n",y-x);
}

19voto

Mark Harrison Points 77152

@Bernard :

Je dois admettre que la plupart de votre exemple m'a échappé. Il compile et semble fonctionner, cependant. Est-ce sûr pour les systèmes SMP ou SpeedStep ?

C'est une bonne question... Je pense que le code est bon. D'un point de vue pratique, nous l'utilisons dans mon entreprise tous les jours, et nous fonctionnons sur un large éventail de boîtes, tout de 2-8 cœurs. Bien sûr, YMMV, etc, mais il semble être fiable et peu coûteux (parce qu'il ne fait pas d'erreur). (car il n'y a pas de changement de contexte dans l'espace système). de synchronisation.

En général, cela fonctionne comme suit :

  • déclarer le bloc de code en tant qu'assembleur (et volatil, pour que l'optimiseur optimiseur le laissera tranquille).
  • exécuter l'instruction CPUID. En plus d'obtenir des informations sur le CPU (dont nous ne faisons rien), elle synchronise le tampon d'exécution du CPU afin que les temps ne soient pas affectés par une exécution dans le désordre.
  • exécuter l'exécution rdtsc (read timestamp). Cela permet de récupérer le nombre de cycles machine exécutés depuis la réinitialisation du processeur. Il s'agit d'une valeur de 64 bits 64 bits, donc avec les vitesses actuelles du processeur, elle se répète tous les 194 ans environ. Il est intéressant de noter que dans la référence originale du Pentium, il est noté qu'il s'enroule tous les 5800 ans environ. 5800 ans environ.
  • les deux dernières lignes stockent les valeurs des registres dans les variables les variables hi et lo, et les mettent dans la valeur de retour de 64 bits.

Notes spécifiques :

  • L'exécution dans le désordre peut entraîner des résultats incorrects, nous exécutons donc la commande "cpuid" qui, en plus de vous donner des informations sur le processeur sur le processeur, synchronise également toute exécution d'instruction hors de l'ordre.

  • La plupart des systèmes d'exploitation synchronisent les compteurs sur les processeurs lorsqu'ils démarrent. la réponse est bonne à quelques nano-secondes près.

  • Le commentaire sur l'hibernation est probablement vrai, mais en pratique vous pratique, vous ne vous souciez probablement pas des délais au-delà des limites de l'hibernation.

  • concernant le speedstep : Les processeurs Intel les plus récents compensent les et renvoient un compte ajusté. J'ai fait un scan rapide sur sur certaines des machines de notre réseau et je n'ai trouvé qu'une seule machine qui qui ne l'avait pas : un Pentium 3 faisant tourner un vieux serveur de base de données. (ce sont des machines Linux, donc j'ai vérifié avec : grep constant_tsc /proc/cpuinfo)

  • Je ne suis pas sûr pour les processeurs AMD, nous sommes principalement un magasin Intel, bien que je sache que certains de nos gourous des systèmes de bas niveau ont fait une évaluation d'AMD.

J'espère que cela aura satisfait votre curiosité, c'est un domaine intéressant et (IMHO) et sous-étudié de la programmation. Vous savez quand Jeff et Joel étaient parlaient de savoir si oui ou non un programmeur devrait connaître le C ? J'étais Je leur criais, "hey, oubliez ce truc de haut niveau en C... l'assembleur est ce que vous devriez apprendre si vous voulez savoir ce que l'ordinateur est fait !"

14voto

David Schlosnagle Points 2113

Vous pourriez être intéressé par FAQ sur Linux pour clock_gettime(CLOCK_REALTIME)

11voto

Vincent Robert Points 16530

Wine utilise en fait gettimeofday() pour implémenter QueryPerformanceCounter() et il est connu pour faire fonctionner de nombreux jeux Windows sur Linux et Mac.

Début des travaux http://source.winehq.org/source/dlls/kernel32/cpu.c#L312

conduit à http://source.winehq.org/source/dlls/ntdll/time.c#L448

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X