/****************************************************************************** * 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; } */ //---------------------------------------------------------------------------