Question

Je porte un jeu, initialement écrit pour l'API Win32, vers Linux (enfin, je porte le port OS X du port Win32 vers 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é à QueryPerformanceFrequency() donner une constante de 1 000 000 comme 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 cela fonctionne différemment si le noyau a été compilé d'une certaine manière ou quelque chose comme ça.Cependant, je suis d'accord avec le fait qu'il ne soit pas portable sur autre chose que Linux.

Était-ce utile?

La solution

Peut être.Mais vous avez de plus gros problèmes. gettimeofday() peut entraîner des timings incorrects s'il existe des processus sur votre système qui modifient la minuterie (par exemple, ntpd).Sur un Linux "normal", cependant, je crois que la résolution de gettimeofday() est 10us.Il peut donc avancer, reculer et remonter le temps, en fonction des processus en cours d'exécution sur votre système.Cela rend effectivement la réponse à votre question non.

Tu devrais examiner clock_gettime(CLOCK_MONOTONIC) pour les intervalles de temps.Il souffre de plusieurs problèmes de moins dus à des éléments tels que les systèmes multicœurs et les paramètres d'horloge externe.

Examinez également le clock_getres() fonction.

Autres conseils

Haute résolution et faible temps système 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.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 CPU.Sous Linux, vous pouvez obtenir la vitesse du processeur à partir de /proc/cpuinfo et diviser pour obtenir le nombre de secondes.Le convertir en double est très pratique.

Quand je lance ceci sur ma box, j'obtiens

11867927879484732
11867927879692217
it took this long to call printf: 207485

Ici se trouve le Guide du développeur Intel cela 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);
}

@Bernard :

Je dois admettre que la plupart de vos exemples me sont passés par-dessus la tête.Il compile et semble cependant fonctionner.Est-ce sûr pour les systèmes SMP ou SpeedStep ?

C'est une bonne question...Je pense que le code est ok.D'un point de vue pratique, nous l'utilisons dans mon entreprise tous les jours, et nous courons sur une assez large éventail de boîtes, tout du 2 à 8 cœurs.Bien sûr, YMMV, etc., mais cela semble être une méthode de synchronisation fiable et faible (car elle ne fait pas un changement de contexte dans l'espace du système).

En général, comment cela fonctionne :

  • Déclarez le bloc de code à assembler (et volatile, donc l'optimiseur le laissera tranquille).
  • exécutez l'instruction CPUID.En plus d'obtenir des informations CPU (avec lesquelles nous ne faisons rien), il synchronise le tampon d'exécution du CPU afin que les horaires ne soient pas affectés par l'exécution hors service.
  • exécutez l'exécution rdtsc (lecture de l'horodatage).Cela récupère le nombre de cycles de machine exécutés depuis la réinitialisation du processeur.Il s'agit d'une valeur de 64 bits, donc avec les vitesses CPU actuelles, elle s'enroulera tous les 194 ans environ.Fait intéressant, dans la référence du Pentium d'origine, ils notent qu'il s'enroule tous les 5800 ans environ.
  • Les deux dernières lignes stockent les valeurs des registres dans les variables HI et LO, et mettez-les dans la valeur de retour 64 bits.

Notes spécifiques :

  • L'exécution hors service peut provoquer des résultats incorrects, nous exécutons donc l'instruction "CPUID" qui, en plus de vous donner des informations sur le CPU, synchronise également toute exécution d'instruction hors commande.

  • La plupart des OS synchronisent les compteurs sur les CPU lorsqu'ils commencent, donc la réponse est bonne à quelques nano-secondes.

  • Le commentaire d'hibernating est probablement vrai, mais dans la pratique, vous ne vous souciez probablement pas des horaires à travers les limites de l'hibernation.

  • concernant le speedstep :Les processeurs Intel plus récents compensent les changements de vitesse et renvoient un décompte ajusté.J'ai fait une analyse rapide sur certaines cases de notre réseau et j'ai trouvé une seule boîte qui ne l'avait pas:un Pentium 3 exécutant un ancien serveur de base de données.(ce sont des boîtiers Linux, j'ai donc vérifié avec :grep constant_tsc /proc/cpuinfo)

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

J'espère que cela satisfait votre curiosité, c'est une zone de programmation intéressante et (à mon humble avis).Vous savez quand Jeff et Joel parlaient de savoir si un programmeur devrait connaître C?Je leur criais dessus, "Hé oublie ce truc de haut niveau ...L'assembleur est ce que vous devez apprendre si vous voulez savoir ce que fait l'ordinateur! "

Vous etes peut etre intéressé FAQ Linux pour clock_gettime(CLOCK_REALTIME)

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

Départs http://source.winehq.org/source/dlls/kernel32/cpu.c#L312

mène à http://source.winehq.org/source/dlls/ntdll/time.c#L448

Il est donc explicitement indiqué en microsecondes, mais 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 sera-t-il jamais augmenté ?

La structure des données est définie comme ayant les 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 avec précision.

Comme d'autres l'ont suggéré, gettimeofday() est mauvais car le réglage de l'heure peut provoquer un décalage de l'horloge et perturber votre calcul. clock_gettime(CLOCK_MONOTONIC) c'est ce que tu veux, et clock_getres() vous indiquera la précision de votre horloge.

La résolution réelle de gettimeofday() dépend de l'architecture matérielle.Les processeurs Intel ainsi que les machines SPARC offrent des minuteries haute résolution qui mesurent les microsecondes.D'autres architectures matérielles s'appuient sur 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.

J'ai obtenu cette réponse de Mesure du temps et minuteries haute résolution, partie I

Cette réponse mentionne des problèmes avec le réglage de l'horloge.Vos problèmes de garantie des unités de tick et les problèmes d'ajustement du temps sont résolus en C++11 avec le <chrono> bibliothèque.

L'horloge std::chrono::steady_clock est garanti de ne pas être ajusté, et de plus, il avancera à 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 type sécurisé en les convertissant en l'un des std::chrono::duration spécialisations, telles que std::chrono::microseconds.Avec ce type, il n'y a aucune ambiguïté sur les unités utilisées par la valeur du tick.Gardez toutefois à l’esprit que l’horloge n’a pas forcément cette résolution.Vous pouvez convertir une durée en attosecondes sans avoir une horloge aussi précise.

D’après mon expérience et 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 version Linux, etc.

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 d'être synchronisé par rapport à un autre CPU.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top