Il y a beaucoup d'informations confuses et/ou erronées sur le TSC, alors j'ai pensé essayer d'en clarifier certaines.
Lorsqu'Intel a introduit le TSC pour la première fois (dans les processeurs Pentium d'origine), il était clairement documenté qu'il comptait les cycles (et non le temps). Cependant, à l'époque, les processeurs fonctionnaient principalement à une fréquence fixe, de sorte que certaines personnes ont ignoré le comportement documenté et l'ont utilisé pour mesurer le temps à la place (notamment les développeurs du noyau Linux). Leur code s'est cassé dans les processeurs ultérieurs qui ne fonctionnent pas à une fréquence fixe (en raison de la gestion de l'alimentation, etc.). À cette époque, d'autres fabricants de processeurs (AMD, Cyrix, Transmeta, etc.) étaient confus et certains ont implémenté TSC pour mesurer les cycles et certains l'ont implémenté pour mesurer le temps, et certains l'ont rendu configurable (via un MSR).
Ensuite, les systèmes "multi-puces" sont devenus plus courants pour les serveurs ; et même plus tard, le multicœur a été introduit. Cela a conduit à des différences mineures entre les valeurs TSC sur différents cœurs (en raison de temps de démarrage différents); mais plus important encore, cela a également entraîné des différences majeures entre les valeurs TSC sur différents processeurs causées par des processeurs fonctionnant à des vitesses différentes (en raison de la gestion de l'alimentation et/ou d'autres facteurs).
Les gens qui essayaient de mal l'utiliser dès le départ (les gens qui l'utilisaient pour mesurer le temps et non les cycles) se sont beaucoup plaints et ont finalement convaincu les fabricants de processeurs de normaliser en faisant en sorte que le TSC mesure le temps et non les cycles.
Bien sûr, c'était un gâchis - par ex. il faut beaucoup de code juste pour déterminer ce que le TSC mesure réellement si vous prenez en charge tous les processeurs 80x86 ; et différentes technologies de gestion de l'alimentation (y compris des choses comme SpeedStep, mais aussi des choses comme les états de veille) peuvent affecter le TSC de différentes manières sur différents processeurs ; AMD a donc introduit un indicateur "TSC invariant" dans CPUID pour indiquer au système d'exploitation que le TSC peut être utilisé pour mesurer correctement le temps.
Tous les processeurs Intel et AMD récents sont comme ça depuis un moment maintenant - TSC compte le temps et ne mesure pas du tout les cycles. Cela signifie que si vous souhaitez mesurer les cycles, vous devez utiliser des compteurs de surveillance des performances (spécifiques au modèle). Malheureusement, les compteurs de surveillance des performances sont encore pires (en raison de leur nature spécifique au modèle et de leur configuration compliquée).
Tant que votre thread reste sur le même cœur de processeur, l'instruction RDTSC continuera à renvoyer un nombre croissant jusqu'à ce qu'elle se termine. Pour un processeur 2 GHz, cela se produit après 292 ans, ce n'est donc pas un vrai problème. Vous ne le verrez probablement pas arriver. Si vous prévoyez de vivre aussi longtemps, assurez-vous que votre ordinateur redémarre, disons, tous les 50 ans.
Le problème avec RDTSC est que vous n'avez aucune garantie qu'il démarre au même moment sur tous les cœurs d'un ancien processeur multicœur et aucune garantie qu'il démarre au même moment sur tous les processeurs d'une ancienne carte multi-CPU. .
Les systèmes modernes n'ont généralement pas de tels problèmes, mais le problème peut également être contourné sur des systèmes plus anciens en définissant l'affinité d'un thread afin qu'il ne s'exécute que sur un seul processeur. Ce n'est pas bon pour les performances de l'application, donc on ne devrait généralement pas le faire, mais pour mesurer les ticks, c'est très bien.
(Un autre "problème" est que beaucoup de gens utilisent le RDTSC pour mesurer le temps, ce qui n'est pas ce qu'il fait, mais vous avez écrit que vous vouliez des cycles CPU, donc c'est bien. Si vous faites utilisez RDTSC pour mesurer le temps, vous pourriez avoir des surprises lorsque l'économie d'énergie ou l'hyperboost ou quoi que la multitude de techniques de changement de fréquence se déclenche. Pour le temps réel, le clock_gettime
syscall est étonnamment bon sous Linux.)
J'écrirais simplement rdtsc
à l'intérieur du asm
déclaration, qui fonctionne très bien pour moi et est plus lisible qu'un code hexadécimal obscur. En supposant qu'il s'agisse du bon code hexadécimal (et puisqu'il ne plante pas et renvoie un nombre toujours croissant, il semble que oui), votre code est bon.
Si vous voulez mesurer le nombre de ticks qu'un morceau de code prend, vous voulez une différence de tick , il vous suffit de soustraire deux valeurs du compteur toujours croissant. Quelque chose comme uint64_t t0 = rdtsc(); ... uint64_t t1 = rdtsc() - t0;
Notez que si des mesures très précises isolées du code environnant sont nécessaires, vous devez sérialiser, c'est-à-dire bloquer le pipeline, avant d'appeler rdtsc
(ou utilisez rdtscp
qui n'est pris en charge que sur les nouveaux processeurs). La seule instruction de sérialisation qui peut être utilisée à chaque niveau de privilège est cpuid
.
En réponse à la question supplémentaire dans le commentaire :
Le TSC démarre à zéro lorsque vous allumez l'ordinateur (et le BIOS réinitialise tous les compteurs de tous les processeurs à la même valeur, bien que certains BIOS d'il y a quelques années ne le fassent pas de manière fiable).
Ainsi, du point de vue de votre programme, le compteur a démarré "une heure inconnue dans le passé", et il augmente toujours à chaque tic d'horloge que le CPU voit. Par conséquent, si vous exécutez l'instruction renvoyant ce compteur maintenant et à tout moment plus tard dans un processus différent, il renverra une valeur supérieure (à moins que le processeur n'ait été suspendu ou éteint entre-temps). Différentes exécutions du même programme obtiennent des nombres plus importants, car le compteur ne cesse de croître. Toujours.
Maintenant, clock_gettime(CLOCK_PROCESS_CPUTIME_ID)
est une autre affaire. Il s'agit du temps CPU que le système d'exploitation a accordé au processus. Il commence à zéro lorsque votre processus démarre. Un nouveau processus commence également à zéro. Ainsi, deux processus s'exécutant l'un après l'autre obtiendront des nombres très similaires ou identiques, sans jamais augmenter.
clock_gettime(CLOCK_MONOTONIC_RAW)
est plus proche du fonctionnement de RDTSC (et sur certains systèmes plus anciens, il est implémenté avec). Il renvoie une valeur qui augmente toujours. De nos jours, il s'agit généralement d'un HPET. Cependant, c'est vraiment le moment , et non des coches . Si votre ordinateur passe en état de faible consommation (par exemple, fonctionne à 1/2 fréquence normale), il toujours avancer au même rythme.
déjà de bonnes réponses, et Damon l'a déjà mentionné d'une certaine manière dans sa réponse, mais j'ajouterai ceci à partir de l'entrée réelle du manuel x86 (volume 2, 4-301) pour RDTSC :
Charge la valeur actuelle du compteur d'horodatage du processeur (un MSR 64 bits) dans les registres EDX:EAX. Le registre EDX est chargé avec les 32 bits de poids fort du MSR et le registre EAX est chargé avec les 32 bits de poids faible. (Sur les processeurs prenant en charge l'architecture Intel 64, les 32 bits d'ordre supérieur de chacun des RAX et RDX sont effacés.)
Le processeur incrémente de manière monotone le compteur d'horodatage MSR à chaque cycle d'horloge et le remet à 0 chaque fois que le processeur est réinitialisé. Voir « Compteur d'horodatage » au chapitre 17 du Manuel du développeur du logiciel des architectures Intel® 64 et IA-32, Volume 3B , pour des détails spécifiques sur le comportement du compteur d'horodatage.