Accueil
Rechercher:
sur developpez.com sur les forums
Forums | Tutoriels | F.A.Q's | Participez | Hébergement | Contacts
Club Emploi Blogs   TV   Dév. Web PHP XML Python Autres 2D-3D-Jeux Sécurité Windows Linux PC Mac
Accueil Conception Java DotNET Visual Basic  C  C++ Delphi MS-Office SQL & SGBD Oracle  4D  Business Intelligence

Lire la fréquence du processeur

29 Mars 2003 (mis à jour le 4 avril 2003)

Par Haypo

Sommaire :

Introduction

Je vais tenter de vous expliquer comment lire la fréquence du processeur, accéder à l'instruction RDTSC (Read Timestamp Counter), et créer un chroomètre ultra-précis sous Linux ou Windows. Ce code ne fonctionne que sur les processeurs *compatibles Intel Pentium* à cause de l'instruction RDTSC. Ne vous inquiétez pas votre (et mon) AMD Athlon la supporte parfaitement ;-)

Ne perdez pas votre temps à copier/coller le code source, un code complet en C est donné à la fin de cette page.

Les extraits de codes pour Linux fonctionnent avec GCC, les extraits pour Windows fonctionnent avec Borland C++ Builder. Le code à télécharger fonctionne également sous Visual C++. Ce dernier nécessite des bouts de code pour gérer les nombres en 64 bits, ce qui complique l'article.

Si vous avez des suggestions et des questions : contactez-moi !

Lecture de la fréquence du CPU sous Linux

Sous Linux, c'est facile car le noyau crée un fichier (/proc/cpuinfo) contenant la fréquence du processeur. La ligne utile est "cpu MHz : <fréquence>".

Exemple de fichier cpuinfo
processor      : 0
vendor_id      : GenuineIntel
cpu family     : 6
model          : 7
model name     : Pentium III (Katmai)
stepping       : 3
cpu MHz        : 451.030
cache size     : 512 KB
fdiv_bug       : no
hlt_bug        : no
f00f_bug       : no
coma_bug       : no
fpu            : yes
fpu_exception  : yes
cpuid level    : 2
wp             : yes
flags          : fpu vme de pse tsc msr pae mce cx8 sep mtrr pge mca cmov pat pse36 mmx fxsr sse
bogomips       : 897.84

On voit ici que c'est un processeur Intel Pentium III ayant une fréquence de 450 Mhz (la fréquence calculée est de 451.030 MHz).

Le texte ("cpu MHz") et la valeur sont séparés par des espaces et/ou des tabulations, un double point (":"), puis un espace ou une tabulation.

Ce qui va nous donner le code suivant :

#define NOMFICH_CPUINFO "/proc/cpuinfo"

// Lit la fréquence du processeur
// Renvoie 0 en cas d'échec
int LitFrequenceCpu (double* frequence)
{
  const char* prefixe_cpu_mhz = "cpu MHz";
  FILE* F;
  char ligne[300+1];
  char *pos;
  int ok=0;

  // Ouvre le fichier
  F = fopen(NOMFICH_CPUINFO, "r");
  if (!F) return 0;

  // Lit une ligne apres l'autre
  while (!feof(F))
  {
    // Lit une ligne de texte
    fgets (ligne, sizeof(ligne), F);

    // C'est la ligne contenant la frequence?
    if (!strncmp(ligne, prefixe_cpu_mhz, strlen(prefixe_cpu_mhz)))
    {
      // Oui, alors lit la frequence
      pos = strrchr (ligne, ':') +2;
      if (!pos) break;
      if (pos[strlen(pos)-1] == '\n') pos[strlen(pos)-1] = '\0';
      strcpy (ligne, pos);
      strcat (ligne,"e6");
      *frequence = atof (ligne);
      ok = 1;
      break;
    }
  }
  fclose (F);
  return ok;
}

J'ai choisi le format double, car il fonctionne avec tous les compilateurs et a une précision suffisante.

Lire la fréquence du CPU sous Windows

Sous Windows, c'est plus complexe : il faut utiliser l'instruction RDTSC des processeurs compatibles Intel Pentium (sur les autres processeur, je ne sais pas comment faire), et les fonctions QueryPerformanceFrequency et QueryPerformanceCounter.

C'est un problème physique assez simple : on a deux compteurs qu'on va appeler WIN (compteur Windows) et CPU (compteur processeur RDTSC). On sait mesure une durée avec les deux compteurs :

  • Twin est la différence de temps du compteur WIN, mesurée avec deux appels à QueryPerformanceCounter.
  • Tcpu est la différence de temps du compteur CPU, mesurée avec deux appels à RDTSC.

Par contre, on ne connait que la fréquence du compteur WIN, donnée par la fonction QueryPerformanceFrequency, qu'on va noter Fwin. Pour rappel, une fréquence est un nombre d'impulsions exprimé en Hertz (Hz) ou "par seconde" (s^-1) . On notera la fréquence du compteur CPU : Fcpu.

Donc, pour une pause d'exactement Texact secondes, on a donc les équations :

  • Texact =Twin / Fwin
  • Texact = Tcpu / Fcpu
  • => Twin / Fwin = Tcpu / Fcpu
  • Enfin : Fcpu = (Tcpu * Fwin ) / Twin

On notera que la durée de la pause Texact n'entre plus en compte dans le calcul ... en théorie. Mais en pratique, plus la pause est longue meilleure est la précision. J'ai retenu Texact = 500 ms, car c'est le meilleur compromis entre précision et durée de la pause (= blocage du programme).

Dans le code donné à la fin du programme, j'utilise encore une autre technique : j'attend une certaine durée en lisant sans arrêt le chronomètre Windows. Ceci permet d'avoir des pauses très courtes, tout en gardant une précision correcte.

Ce qui donne le code :

// Lit la fréquence du processeur
// Renvoie la fréquence en Hz dans 'frequence' si le code de retour est
// différent de 1. Renvoie 0 en cas d'erreur.
int LitFrequenceCpu (double* frequence)
{
  unsigned __int64 Fwin;
  unsigned __int64 Twin_avant, Twin_apres;
  double Tcpu_avant, Tcpu_apres;
  double Fcpu;

  // Lit la frequence du chronometre Windows
  if (!QueryPerformanceFrequency((LARGE_INTEGER*)&Fwin)) return 0;
  printf ("Frequence du compteur Windows = ");
  AfficheFrequence (Fwin);

  // Avant
  Tcpu_avant = RDTSC();
  QueryPerformanceCounter((LARGE_INTEGER*)&Twin_avant);

  // Pause de 500 ms
  Sleep(500);

  // Apres
  Tcpu_apres = RDTSC();
  QueryPerformanceCounter((LARGE_INTEGER*)&Twin_apres);

  // Calcule la fréquence en MHz
  Fcpu = (Tcpu_apres - Tcpu_avant);
  Fcpu *= Fwin;
  Fcpu /= (double)(Twin_apres - Twin_avant);
  *frequence = Fcpu;
  return 1;
}

Même remarque que pour le code Linux : J'ai choisi le format double, car il fonctionne avec tous les compilateurs et a une précision suffisante.

L'instruction RDTSC

Les processeurs compatibles Intel Pentium possède une instructeur permettant de lire le nombre de cycles processseurs depuis l'allumage de l'ordinateur. C'est donc un nombre non signé sur 64 bits que l'on va lire. Il nous faut donc une variable pouvant stocker un tel nombre. Plusieurs solutions s'offrent à nous :

  • Utiliser le format "unsigned long long" disponnible avec la norme ISO C99. Mais peu de compilateurs la supporte. Le seul que je connais est GCC (http://gcc.gnu.org).
  • Emuler un nombre 64 bits en stocker deux nombres de 32 bits (au format "unsigned long"). Ceci fonctionne la totalité des compilateurs C (ou bien ?).
  • Utiliser le format "double"qui a une taille et une précision suffisante.

L'instruction en question est "RDTSC" (Read Timestamp Counter), ou en code machine "db 0x0F, 0x31".

Ce qui nous donne le code suivant :

// Instruction RDTSC du processeur Pentium
double RDTSC(void)
{
#ifdef linux
  unsigned long long x;
  __asm__ volatile (".byte 0x0f, 0x31" : "=A"(x));
  return (double)x;
#else
  unsigned long a, b;
  double x;
  asm
  {
    db 0x0F,0x31
    mov [a],eax
    mov [b],edx
  }
  x = b;
  x *= 4294967296;
  x += a;
  return x;
#endif
}

Ce code fonctionne à la fois sous Linux et sous Windows (grâce à l'utilisation du "#ifdef linux").

Chronomètre ultra-précis

Maintenant que nous avons les deux outils nécessaires : fréquence du processeur et instruction RDTSC, on peut lire une durée très précisément. Pour cela, il faut :

  1. Lire la fréquence du processeur frequence (format double)
  2. Sauver le compteur RDTSC dans une variable avant (format double)
  3. Faire les calculs ...
  4. Sauver le compteur RDTSC dans une variable apres (format double)
  5. Calculer la différence de temps duree = (apres - avant) / frequence

Voilà pour l'algorithme, en pratique : voyez le code source donné plus bas :-)

Code complet en C :

Plutôt que de copier/coller inutilement, je vous propose de charger un exemple complet qui fonctionne simultanément sous Linux et sous Windows.

Télécharger le code C complet (6 Ko)
Attention, le code est sous licence GPL !

Ce code source fonctionne sous Linux avec GCC 2.95, Borland C++ Builder 5 (Windows) et Visual C++ 6 (Windows). Bien sûr, il doit être compatible avec d'autres compilateurs moyennant une petite correction du code.

Code en assembleur

Jean-Paul MICHEL m'a envoyé un code source en assembleur pour lire la fréquence du processeur en assembleur. Il l'offre gratos, trop sympa ! Par contre, je vous prierai de respecter le copyright !!!

Télécharger le code assembleur

Revenir sur la page de Haypo

Responsable bénévole de la rubrique Accueil : Nicolas Vallée - Contacter par EMail :
Vos questions techniques : forum d'entraide Accueil - Publiez vos articles, tutoriels et cours
et rejoignez-nous dans l'équipe de rédaction du club d'entraide des développeurs francophones
Nous contacter - Copyright © 2000-2008 www.developpez.com - Legal informations.