Cette source classique permet de récupérer une valeur approchée de la fréquence de son processeur. Comme je n'en ai pas vu pour C#/.NET, je l'ai adapté en C#. Cette implémentation alloue de la mémoire non managée pour y écrire un bout de code en assembleur, puis l'exécute en créant un thread dont le point d'entrée est l'adresse de la mémoire contenant le code asm.
Source / Exemple :
public struct CpuFreqParams
{
//addresse de QueryPerformanceCounter
public IntPtr lpQPCounterAddress;
//temps à attendre pendant le test
public long testSpan;
//nombre de cycles passés pendant le test
public long nCycles;
}
//code asm utilisé. un peu plus long que la plupart de codes de ce genre
//à cause de l'addition/soustraction d'entiers 64 bits (avec add/adc et sub/sbb)
//et de la structure utilisée pour les paramètres, pour être lancé comme thread
__declspec(naked) void __stdcall GetCPUFrequency( LPVOID lpParams )
{
//Pile locale:
//
//UINT64 endCycle (ebp-68 et ebp-64)
//UINT64 startCycle (ebp-60 et ebp-56)
//UINT64 tickCurrent (ebp-52 et ebp-48)
//UINT64 tickEnd (ebp-44 et ebp-40)
//UINT64 tickStart (ebp-36 et ebp-32)
//DWORD lpQPCounterAddress (ebp-28)
//UINT64 testSpan (ebp-20 et ebp-16)
//UINT64 nCycles (ebp-12 et ebp-8)
__asm
{
push ebp
mov ebp, esp
;alloue la pile locale
sub esp, 64
;sauvegarde les registres et les flags
push eax
push ecx
push edx
push esi
push edi
pushfd
;vérifie que lpParams n'est pas null
cmp dword ptr [ebp+8], 0
je clear
;copie les paramètres dans la pile
mov esi, dword ptr [ebp+8]
lea edi, dword ptr [ebp-28]
mov ecx, 24
cld
rep movsb
;vérifie que lpQPCounterAddress n'est pas null
cmp dword ptr [ebp-28], 0
je clear
;obtient le nombre de ticks avec QueryPerfCounter
lea ecx, dword ptr [ebp-36]
push ecx
call dword ptr [ebp-28]
;ajoute testSpan au nombre ticks pour avoir l'"heure" en ticks à attendre
mov ecx, dword ptr [ebp-36]
add ecx, dword ptr [ebp-20]
mov dword ptr [ebp-44], ecx
mov ecx, dword ptr [ebp-32]
adc ecx, dword ptr [ebp-16]
mov dword ptr [ebp-40], ecx
;obtient le nombre de cycles du processeur
rdtsc
mov dword ptr [ebp-60], eax
mov dword ptr [ebp-56], edx
;boucle d'attente
L1:
;obtient le nombre de ticks avec QueryPerfCounter
lea ecx, dword ptr [ebp-52]
push ecx
call dword ptr [ebp-28]
;regarde si on doit sortir de la boucle
lea esi, dword ptr [ebp-44]
lea edi, dword ptr [ebp-52]
mov ecx, 2
cld
rep cmpsd
jae L1
;obtient le nombre final de cycles du processeur
rdtsc
mov dword ptr [ebp-68], eax
mov dword ptr [ebp-64], edx
;obtient le nombre de cycles passés pendant le test
mov ecx, dword ptr [ebp-68]
sub ecx, dword ptr [ebp-60]
mov dword ptr [ebp-12], ecx
mov ecx, dword ptr [ebp-64]
sbb ecx, dword ptr [ebp-56]
mov dword ptr [ebp-8], ecx
;copie la pile vers les paramètres
lea esi, dword ptr [ebp-28]
mov edi, dword ptr [ebp+8]
mov ecx, 24
cld
rep movsb
clear:
popfd
pop edi
pop esi
pop edx
pop ecx
pop eax
add esp, 64
mov esp, ebp
pop ebp
ret
}
}
Conclusion :
Ce code devrait marcher pour tous les processeurs à partir du Pentium, et sur toutes les versions de Windows à partir de 2000 (à cause de l'appel à VirtualProtect). Le test effectué est sur un AMD 2400+ (2.0 Ghz) sous Windows XP. Pour les systèmes multiprocesseurs/multicore/HyperThreading, le test peut échouer s'il est trop long (ex: 1 sec), une durée de 10 ms ou moins devrait réussir dans la plupart des cas.
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.