/******************************************************************************
* Programme d'exemple permettant de :
* - Lire la fréquence du processeur
* - Accéder à l'instruction RDTSC
* - Chronométrer très précisément une durée.
*
* Ce code est prévu pour fonctionner sous Linux ou sous Windows.
*
* ATTENTION: Ce programme ne fonctionne que sur des processeurs compatibles
* Intel Pentium ou supérieur (à cause de l'instruction RDTSC).
*
* CE CODE EST SOUS LICENCE GPL : http://www.fsf.org/licenses/gpl.html
*
* Historique :
* - 8 septembre 2003 : Correction pour Visual C++, ce naze ne sait pas
* convertir des uint64 en double, j'ai fait un ptit hack tout naze ...
* - 5 septembre 2003 : Portage pour Visual C++
* - 29 mars 2003 : Création, code pour Linux sous GCC,
* et Borland C++ Builder sous Windows
*
* Par Haypo (victor.stinner@haypocalc.com) - http://www.haypocalc.com/
* Original disponible: http://www.haypocalc.com/perso/prog/frequence_cpu.c
*
* Modifications par beridoxy: 27 octobre
* Modification pour rendre compilable par le compilateur Windows GCC qui utilise
* des instructions assembleurs AT&T.
*****************************************************************************/
/* A FAIRE INDIQUER VOTRE PLATE FORME ET TYPE DE COMPILATEUR !*/
/*Win32 detection fréquence*/
#define _Windows
/*Win32 AT&T ASM Instructions */
#define GCCWindows
/*Linux detection fréquence*/
//#define linux
#include <stdio.h>
#ifdef linux
# include <unistd.h>
# include <string.h>
# include <stdlib.h>
# define NOMFICH_CPUINFO "/proc/cpuinfo"
#elif defined(_Windows)
# include <windows.h>
#endif
//---------------------------------------------------------------------------
#if defined(_Windows)
// Définit les types uint32 et uint64 sous Windows
typedef unsigned __int32 uint32;
typedef unsigned __int64 uint64;
// Définit le convertion uint64 vers double
double uint64_to_double (const uint64 x)
{
#if defined(_MSC_VER)
// Petit hack pour convertir du 64 bits en double
// Ce hack dépend de l'endian (ici ça marche sur Intel x86)
typedef uint32 uint64_32[2];
uint64_32 *hack = (uint64_32 *)&x;
double dbl;
dbl = (*hack)[1];
dbl *= 4294967296;
dbl += (*hack)[0];
return dbl;
#else
return (double)x;
#endif
}
#endif
//---------------------------------------------------------------------------
// Instruction RDTSC du processeur Pentium
double RDTSC(void)
{
/*#ifdef linux
unsigned long long x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A"(x));
return (double)x;
#endif
//Windows et pas GCC.
#ifndef GCCWindows
unsigned long a, b;
double x;
#ifdef _MSC_VER
// Code pour Visual C++
__asm
{
RDTSC
#else
// Code pour Borland et autres
asm
{
db 0x0F,0x31 // instruction RDTSC
#endif
mov [a],eax
mov [b],edx
}
x = b;
x *= 4294967296;
x += a;
return x;
#endif
*/
#ifdef GCCWindows
//Pour Windows avec GCC les instruction ASM RDTSC sont les meme.
unsigned long long x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A"(x));
return (double)x;
#endif
}
//---------------------------------------------------------------------------
// Affichage d'une fréquence en utilisant le suffixe adapté (GHz, MHz, KHz, Hz)
void AfficheFrequence (double frequence)
{
if (1e9<frequence)
printf ("%.1f GHz\n", frequence/1e9);
else if (1e6<frequence)
printf ("%.1f MHz\n", frequence/1e6);
else if (1e3<frequence)
printf ("%.1f KHz\n", frequence/1e3);
else
printf ("%.1f Hz\n", frequence);
}
//---------------------------------------------------------------------------
// 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)
{
#ifdef linux
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;
#else
uint64 Fwin;
uint64 Twin_avant, Twin_apres;
double Tcpu_avant, Tcpu_apres;
double Fcpu;
// Lit la frequence du chronomêtre Windows
if (!QueryPerformanceFrequency((LARGE_INTEGER*)&Fwin)) return 0;
printf ("Frequence du compteur Windows = ");
AfficheFrequence (uint64_to_double(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 *= uint64_to_double(Fwin);
Fcpu /= uint64_to_double(Twin_apres - Twin_avant);
*frequence = Fcpu;
return 1;
#endif
}
//---------------------------------------------------------------------------
// Fonction de calcul : fait une pause d'une seconde :-)
void Calcul(void)
{
int i;
printf ("Pause d'une seconde ");
for (i=0; i<10; i++)
{
#ifdef linux
usleep (100);
#elif defined(_Windows)
Sleep (100);
#endif
printf (".");
}
printf (" ");
}
//---------------------------------------------------------------------------
// Fonction principale
/*
int main()
{
double frequence;
double avant, apres, duree;
if (LitFrequenceCpu(&frequence)) {
printf ("Frequence du processeur = ");
AfficheFrequence (frequence);
// Mesure du temps de calcul
printf ("Calcul : ");
avant = RDTSC();
Calcul();
apres = RDTSC();
duree = (apres-avant)/frequence;
printf ("OK\n");
// Affiche de la dur?e de calcul
printf ("Duree du calcul : %.1f ms\n", duree*1000);
} else {
printf ("ERREUR : Impossible de lire la frequence du processeur !\n");
}
getchar();
return 0;
}
*/
//---------------------------------------------------------------------------