Est-ce que gettimeofday () est garanti à une résolution d'une microseconde?

Donc, je me retrouve à porter un jeu, qui a été écrit à l'origine pour l'API Win32, sur Linux (bien, en portant le port OS X du port Win32 sur Linux). J'ai implémenté QueryPerformanceCounter en donnant les uSeconds 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é avec QueryPerformanceFrequency() donnant une constante constante comme la fréquence, fonctionne bien sur ma machine , me donnant une variable 64 bits qui contient uSeconds depuis le démarrage du programme. Donc est-ce portable? Je ne veux pas découvrir que ça marche différemment si le noyau a été compilé d'une certaine manière ou quelque chose comme ça. Je vais bien être non-portable à quelque chose d'autre que Linux, cependant.

0
ajouté édité
Vues: 11

10 Réponses

D'après mon expérience et d'après ce que j'ai lu sur Internet, la réponse est «non», ce n'est pas garanti. Cela dépend de la vitesse du processeur, du système d'exploitation, de la saveur de Linux, etc.

0
ajouté

La résolution réelle de gettimeofday() dépend de l'architecture matérielle. Les processeurs Intel ainsi que les machines SPARC offrent des minuteurs haute résolution qui mesurent des microsecondes. D'autres architectures matérielles reviennent à la minuterie du système, qui est généralement réglée sur 100 Hz. Dans de tels cas, la résolution temporelle sera moins précise.

I obtained this answer from High Resolution Time Measurement and Timers, Part I

0
ajouté

Peut être. Mais vous avez de plus gros problèmes. gettimeofday() peut entraîner des temporisations incorrectes s'il y a des processus sur votre système qui changent le timer (ie, ntpd). Sur un linux "normal", cependant, je crois que la résolution de gettimeofday() est 10us. Il peut sauter en avant et en arrière et le temps, par conséquent, en fonction des processus qui s'exécutent sur votre système. Cela rend effectivement la réponse à votre question non.

Vous devriez regarder dans clock_gettime (CLOCK_MONOTONIC) pour les intervalles de temps. Il souffre de plusieurs problèmes moins en raison de choses comme les systèmes multi-core et les paramètres d'horloge externe.

Regardez aussi la fonction clock_getres() .

0
ajouté
Il a été introduit en 2001, mais pas obligatoire avant POSIX 2008.
ajouté l'auteur R.., source
De la FAQ Linux pour lock_gettime (voir la réponse de David Schlosnagle) "CLOCK_MONOTONIC ... est ajusté en fréquence par NTP via adjtimex (). Dans le futur (j'essaye toujours d'obtenir le patch) il y aura un CLOCK_MONOTONIC_RAW qui ne sera pas être modifié du tout, et aura une corrélation linéaire avec les compteurs de matériel. " Je ne pense pas que l'horloge _RAW ait jamais été intégrée au noyau (à moins qu'elle ne soit rebaptisée _HR, mais mes recherches suggèrent que les efforts soient abandonnés aussi).
ajouté l'auteur Tony Delroy, source
@ vitaly.v.ch c'est POSIX donc ce n'est pas seulement Linux et 'newist'? même les distributions 'Enterprise' comme Red Hat Enterprise Linux sont basées sur 2.6.18 qui a clock_gettime donc non, pas très nouveau .. (la date de la page de RHEL est 2004-mars-12 donc ça fait longtemps) sauf si vous êtes parler de noyaux VRAIMENT FREAKING VIEUX WTF voulez-vous dire?
ajouté l'auteur Spudd86, source
clock_gettime est présent uniquement sur le nouveau Linux. autre système ont seulement gettimeofday ()
ajouté l'auteur vitaly.v.ch, source
clock_gettime a été inclus dans POSIX en 2001. pour autant que je sache actuellement clock_gettime() implémenté sous Linux 2.6 et qnx. mais Linux 2.4 est actuellement utilisé dans de nombreux systèmes de production.
ajouté l'auteur vitaly.v.ch, source

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

Starts http://source.winehq.org/source/dlls/kernel32/cpu.c#L312

leads to http://source.winehq.org/source/dlls/ntdll/time.c#L448

0
ajouté

Haute résolution, faible temps de transfert pour les processeurs Intel

Si vous utilisez du matériel Intel, voici comment lire le compteur d'instructions en temps réel du processeur. Il vous indiquera le nombre de cycles CPU exécutés depuis le démarrage du processeur. C'est probablement le compteur le plus fin que vous pouvez obtenir pour la mesure du rendement.

Notez que c'est le nombre de cycles CPU. Sur Linux, vous pouvez obtenir la vitesse du processeur à partir de / proc / cpuinfo et diviser pour obtenir le nombre de secondes. Convertir ceci en double est assez pratique.

Quand je cours ceci sur ma boîte, je reçois

11867927879484732
11867927879692217
it took this long to call printf: 207485

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

#include 
#include 

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);
}
0
ajouté
Votre code ne devrait-il pas réutiliser CPUID après la première instruction RDTSC et avant d'exécuter le code référencé? Sinon, comment arrêter l'exécution du code référencé avant / en parallèle avec le premier RDTSC , et par conséquent sous-représenté dans le delta RDTSC ?
ajouté l'auteur Tony Delroy, source
Notez que le TSC n'est peut-être pas toujours synchronisé entre les cœurs, il peut s'arrêter ou changer de fréquence lorsque le processeur passe en mode de puissance plus faible (et vous n'avez aucun moyen de le savoir) et n'est généralement pas fiable. Le noyau est capable de détecter quand il est fiable, détecter d'autres alternatives comme HPET et ACPI PM timer, et sélectionner automatiquement le meilleur. C'est une bonne idée de toujours utiliser le noyau pour le chronométrage, sauf si vous êtes vraiment sûr que le TSC est stable et monotone.
ajouté l'auteur CesarB, source
Le TSC sur les plates-formes Core et au-dessus d'Intel est synchronisé sur plusieurs processeurs et incrémente à une fréquence constante indépendamment des états de gestion de l'alimentation. Voir le manuel du développeur de logiciels Intel, vol. 3 Section 18.10. Cependant, la vitesse à laquelle le compteur augmente n'est pas la même que la fréquence du processeur. Le TSC s'incrémente à la fréquence maximale résolue de la plate-forme, qui est égale au produit de la fréquence de bus évolutive et du rapport de bus maximal résolu? Manuel du développeur de logiciels Intel, vol.
ajouté l'auteur sstock, source
Vous pouvez obtenir la fréquence de bus échelonnable et le rapport de bus maximal résolu en interrogeant les registres spécifiques au modèle (MSR) de la CPU comme suit: Fréquence de bus évolutive == MSR_FSB_FREQ [2: 0] id 0xCD, Rapport de bus maximal résolu == MSR_PLATFORM_ID [ 12: 8] id 0x17. Consulter Intel SDM Vol.3 Annexe B.1 pour interpréter les valeurs de registre. Vous pouvez utiliser les outils msr sur Linux pour interroger les registres. kernel.org/pub/linux/utils/cpu/msr-tools
ajouté l'auteur sstock, source

@Bernard:

Je dois admettre que la plupart de vos exemples sont allés droit au-dessus de ma tête. 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 correct. D'un point de vue pratique, nous l'utilisons tous les jours dans mon entreprise, et nous courons sur un assez large éventail de boîtes, tout de 2-8 cœurs. Bien sûr, YMMV, etc, mais il semble être un faible et fiable (parce qu'il ne fait pas de contexte passer dans l'espace système) de chronométrage.

Généralement, comment cela fonctionne est:

  • déclare que le bloc de code est assembleur (et volatile, donc le l'optimiseur le laissera tranquille).
  • exécute l'instruction CPUID. En plus d'obtenir des informations sur le processeur (que nous ne faisons rien avec) il synchronise le tampon d'exécution du CPU de sorte que les timings ne sont pas affectés par l'exécution hors de l'ordre.
  • exécute l'exécution de rdtsc (lecture de l'horodatage). Cela va chercher le nombre de Cycles machine exécutés depuis la réinitialisation du processeur. C'est un 64 bits valeur, donc avec les vitesses actuelles du processeur, il s'enroulera tous les 194 ans ou plus. Fait intéressant, dans la référence originale Pentium, ils notent qu'il enveloppe autour de chaque 5800 ans ou plus.
  • les deux dernières lignes stockent les valeurs des registres dans les variables hi et lo, et mettez cela 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, donc nous exécutons le "cpuid" instruction qui en plus de vous donner quelques informations à propos de l'unité centrale de traitement synchronise également toute exécution d'instruction hors de l'ordre.

  • La plupart des OS synchronisent les compteurs sur les processeurs quand ils démarrent, donc la réponse est bonne à quelques nanosecondes.

  • Le commentaire d'hibernation est probablement vrai, mais en pratique vous probablement ne se soucient pas des horaires à travers les limites d'hibernation.

  • en ce qui concerne les vitesses: les nouveaux processeurs Intel compensent la vitesse change et renvoie un compte ajusté. J'ai fait un scan rapide sur certaines des boîtes sur notre réseau et trouvé une seule boîte ne l'avait pas: un Pentium 3 en cours d'exécution de vieux serveur de base de données. (Ce sont des boîtes Linux, donc j'ai vérifié avec: grep constant_tsc / proc / cpuinfo)

  • Je ne suis pas sûr des processeurs AMD, nous sommes avant tout un magasin Intel, même si je connais certains de nos gourous systèmes de bas niveau a fait un Évaluation AMD.

J'espère que cela satisfait votre curiosité, c'est un (IMHO) intéressant domaine de programmation sous-étudié. Vous savez quand Jeff et Joel étaient parler de savoir si un programmeur devrait savoir C? J'ai été criant à eux, "hey oubliez ce truc de haut niveau C ... assembleur est ce que vous devriez apprendre si vous voulez savoir ce que l'ordinateur est Faire!"

0
ajouté
Pour référence, la question que j'ai posée (dans une réponse séparée - avant les commentaires) était: "Je dois admettre que la plupart de votre exemple est allé directement au-dessus de ma tête, il compile, et semble fonctionner, cependant. Systèmes SMP ou SpeedStep? "
ajouté l'auteur Bernard, source
... Les gens du noyau ont essayé d'amener les gens à cesser d'utiliser rdtsc pendant un certain temps ... et évitent généralement de l'utiliser dans le noyau parce que c'est peu fiable.
ajouté l'auteur Spudd86, source

Donc ça dit explicitement des microsecondes, mais dit que la résolution de l'horloge système n'est pas spécifiée. Je suppose que la résolution dans ce contexte signifie comment le plus petit montant, il sera jamais incrémenté?

La structure de données est définie comme ayant des microsecondes comme unité de mesure, mais cela ne signifie pas que l'horloge ou le système d'exploitation est réellement capable de mesurer cela finement.

Comme d'autres personnes l'ont suggéré, gettimeofday() est mauvais parce que le réglage de l'heure peut provoquer un décalage de l'horloge et annuler votre calcul. clock_gettime (CLOCK_MONOTONIC) est ce que vous voulez, et clock_getres() vous dira la précision de votre horloge.

0
ajouté
@ mpez0 ce n'est pas
ajouté l'auteur Spudd86, source
Alors que se passe-t-il dans votre code lorsque gettimeofday() saute en avant ou en arrière avec l'heure d'été?
ajouté l'auteur mpez0, source
clock_gettime est présent uniquement sur le nouveau Linux. autre système ont seulement gettimeofday ()
ajouté l'auteur vitaly.v.ch, source

This answer mentions problems with the clock being adjusted. Both your problems guaranteeing tick units and the problems with the time being adjusted are solved in C++11 with the library.

L'horloge std :: chrono :: steady_clock est garantie de ne pas être ajustée, et de plus elle avance à un rythme constant par rapport au temps réel, donc des technologies comme SpeedStep ne doivent pas l'affecter.

Vous pouvez obtenir des unités de typesafe en convertissant à l'une des spécialisations std :: chrono :: duration , telles que std :: chrono :: microseconds . Avec ce type, il n'y a pas d'ambiguïté sur les unités utilisées par la valeur de tick. Cependant, gardez à l'esprit que l'horloge n'a pas nécessairement cette résolution. Vous pouvez convertir une durée en attosecondes sans réellement avoir une horloge précise.

0
ajouté

La lecture du RDTSC n'est pas fiable dans les systèmes SMP, car chaque CPU maintient son propre compteur et chaque compteur n'est pas garanti par synchronisation par rapport à une autre CPU.

Je pourrais suggérer d'essayer clock_gettime (CLOCK_REALTIME) . Le manuel posix indique que cela devrait être mis en œuvre sur tous les systèmes conformes. Il peut fournir un nombre de nanosecondes, mais vous devrez probablement vérifier clock_getres (CLOCK_REALTIME) sur votre système pour voir quelle est la résolution réelle.

0
ajouté
clock_getres (CLOCK_REALTIME) ne donnera pas la vraie résolution. Il retourne toujours "1 ns" (une nanoseconde) quand les hrtimers sont disponibles, vérifiez le code include / linux / hrtimer.h pour le code define HIGH_RES_NSEC 1 (plus sur stackoverflow.com/a/23044075/196561 )
ajouté l'auteur osgx, source
0
ajouté