Timer, sleep, chronométrage, vitesse du processeur, a la microseconde près !

Soyez le premier à donner votre avis sur cette source.

Vue 19 647 fois - Téléchargée 1 409 fois

Description

Tu souhaiterais:

Pouvoir évaluer le vitesse d'un processeur au MHz près ?

Savoir le nombre de microsecondes entre deux instants et donc laquel de tes 2 possibilités de code est la plus rapide ?

Pouvoir utiliser un Sleep précis à la microseconde ?

Executer un bout de code précisement 100000 fois par secondes quel que soit le processeur, et même si la vitesse de ce code peut varier d'une fois sur l'autre ?

Et que si le processeur soit incapable de le faire, tu sois au courant ?

Bah tu rêves pas: voilà une solution, et ultra simple d'emplois !

Ce source ne t'apprendras sûrement rien sur VB6, rien sur rien aussi: Il est juste là pour te fournir des routines de gestion du temps bien au dela des capacité de VB6.

Bon, la précision a la microseconde n'est pas toujours assurée: Il peut bien sûr arriver que Windows change de Trhead.

Mais on ne vas quand même pas empecher Windows de s'occuper de lui et des autres !

Si tu n'aimes pas lire, tu peux directement aller voire le code (Bien sûr, il y en plus dans le zip que ce que je donne ci-dessous).

Si tu veux plus d'infos:

Si tu as déjà essayer le contrôle Timer, tu sais que ses performances sont très loins de la milliseconde.

De même, l'API Sleep, bien que plus précise reste très peu performante dés que l'on s'approche des milisecondes.

Mes routines utilisent donc deux autres méthodes, infiniment plus précises:

1 Un compteur fourni par l'API Windows, dont la fréquence est indépendante de celle du processeur.
Ce compteur 64 bits est accessible via QueryPerformanceCounter et QueryPerformanceFrequency pour sa fréquence.

2 Le registre TSC, qui s'incrémente à chaque front d'horloge du processeur.

Mais la fréquence de certains processeurs (au moins les centrino) est variable.

En conséquence je n'utilise pas le registre TSC pour les routines de timer et les intervalles de temps.

On peut faire la plupart de mes routines en VB6, seulement, à ma connaissance VB6 ne prend pas en charge les entiers 64 bits.

Les dlls sont donc en Delphi7 qui prend en charge les entiers 64 bits, ainsi que l'assemblage en ligne, ce qui permet d'utiliser l'instruction assembleur RDTSC.

Le seul problème qu'il me reste est que je ne parviens toujours pas à bloquer au sens propre un trhead avec précision.

Conséquement, je les fais boucler sur un sleep(0) qui demande à Windows de s'occuper des autres trhead.

Mon timer et mon sleep ne libereront les ressources seulement pour des fréquence strictement inférieures à 200 Hz, et si le processeur est suffisement rapide.

En guise de libération, j'utilise Sleep(1) ou supérieur, c'est pour ça que je peux pas libérer à des fréquences supérieures.

Source / Exemple :


Public Declare Function GetCpuSpeed Lib "EX_Time.dll" Alias "EXtmr_GetCpuSpeed" (nBuffer As Long, ByVal lpEX As String) As Long
Public Declare Function GetCpuMaxSpeed Lib "EX_Time.dll" Alias "EXtmr_GetCpuMaxSpeed" (nBuffer As Long, ByVal lpEX As String) As Long
Public Declare Function Sleep Lib "EX_Time.dll" Alias "EXtmr_Sleep" (ByVal lpEX As String, Optional ByVal nTime As Long = 0) As Long
Public Declare Function InitDelta Lib "EX_Time.dll" Alias "EXtmr_InitDelta" (ByVal lpEX As String) As Long
Public Declare Function GetDelta Lib "EX_Time.dll" Alias "EXtmr_GetDelta" (nBuffer As Long, ByVal lpEX As String) As Long
Public Declare Function InitTimer Lib "EX_Time.dll" Alias "EXtmr_InitTimer" (ByVal lpEX As String, Optional ByVal nInterval As Long = 0) As Long
Public Declare Function Timer Lib "EX_Time.dll" Alias "EXtmr_Timer" (nBuffer As Long, ByVal lpEX As String) As Long
Public Declare Function InitDeltaClock Lib "EX_Time.dll" Alias "EXtmr_InitDeltaClock" (ByVal lpEX As String) As Long
Public Declare Function GetDeltaClock Lib "EX_Time.dll" Alias "EXtmr_GetDeltaClock" (nBuffer As Long, ByVal lpEX As String) As Long

Conclusion :


La fonction de calcul de la vitesse maximale du processeur est tirée d'un livre de Pierre Maurette.

GetCpuSpeed:
Renvoie la fréquence actuelle du processeur, en MHz.
Le calcul est rapide et la CPU ne monte pas à 100%.
On peut ainsi connaitre la fréquence actuelle d'un centrino (Elle varie beaucoup).
Sur un processeur à fréquence fixe, c'est cette fréquence qui est renvoyée.

GetCpuMaxSpeed:
Renvoie la fréquence (maximale) du processeur, en MHz.
Le calcul n'est pas instantané, et la CPU monte forcément à 100% de ses capacités.

Sleep:
Même fonction que sleep, mais en qui prend un argument en microsecondes.
Pour des intervalles supérieures à 5000 microsecondes la libération des ressources est réel.
Au dessous, la CPU mont à 100% sur un sleep(O), ce qui permet aux autres appplication de consommer pas mal de ressources.

InitDelta:
Initialise l'origine des temps.

GetDelta:
Renvoie le temps écoulé depuis l'origine des temps, en microsecondes.
Il est indispensable que l'origine des temps soit redéfinie toutes les demi-heures.
En effet: 2^32/2 microsecondes= 35 minutes.

InitTimer:
Initialise le timer.

Timer:
Il permet d'executer du code à une certaine fréquence.
Placé la fin du code à executé, elle calcul un temps envoyer au Sleep pour que le code dure toujours le même temps.
Elle renvoie le temps d'attente envoyé au sleep, négatif si le timer est en retard.

InitDeltaClock:
Initialise l'origine des fronts.

GetDeltaClock:
Renvoie le nombre de fronts de l'horloge du processeur ayant eu lieu depuis l'origine des fronts.
Renvoie 0 si il y a eu plus de 2^32/2 fronts (Sur un 3GHz, cela prend moins de 0,716 secondes).

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

cs_rt15
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
10
Suite à une question par MP de zapanyeti (En gros, un Sleep un peu précis en full VB6, mais des problèmes de dépassement de capacité) ->
Son code utilisait QueryPerformance* en leurs passant des LARGE_INTEGER et en travaillant sur les 32 bits de poids faible.
Une astuce (Je la connais de Renfield) consiste plutôt à utiliser le type Currency qui est sur 64 bits (Avec 4 digits derrière la virgule, mais ça n'a pas d'importance dans ce cas vu les opération effectuées).

Voici donc un code inspiré du sien qui nécessite une form avec un bouton btnWait.
Les attentes sont affichées dans la fenêtre d'exécution. Le résultat est assez précis.
==================================================
Option Explicit

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long

Private curFreq As Currency
Private stopLoop As Boolean

Public Sub WaitMicroSeconde(Duree As Long)
Dim curStart As Currency
Dim curEnd As Currency
Dim WaitTop As Currency

QueryPerformanceCounter curStart
WaitTop = (Duree * curFreq) / 1000000 + curStart
Do
QueryPerformanceCounter curEnd
Loop Until curEnd >= WaitTop
End Sub

Private Sub btnWait_Click()
Dim i As Integer
Dim curStart As Currency
Dim curEnd As Currency

btnWait.Enabled = False

For i = 0 To 200
If stopLoop Then Exit Sub
QueryPerformanceCounter curStart
WaitMicroSeconde 2000
QueryPerformanceCounter curEnd
Debug.Print "Waited " & (curEnd - curStart) / curFreq * 1000000 & " micro secondes"
Sleep 1
DoEvents
Next i

btnWait.Enabled = True

End Sub

Private Sub Form_Load()
QueryPerformanceFrequency curFreq
stopLoop = False
End Sub

Private Sub Form_Unload(Cancel As Integer)
stopLoop = True
End Sub
==================================================

Ce code devrait être franchement moins sujet aux dépassement de capacité quoiqu'ils peuvent encore se produire notamment lors du calcul de WaitTop. L'échec en cas de dépassement de capacité est désactivable dans les propriétés du projet, onglet "compilation", bouton "Optimisations avancées".
butcherofsiberia
Messages postés
1
Date d'inscription
vendredi 9 février 2007
Statut
Membre
Dernière intervention
9 février 2007

Y'a pas mieux que ca :

LARGE_INTEGER t1, t2
LARGE_INTEGER param;
QueryPerformanceCounter(&t1);
QueryPerformanceCounter(&t2);
QueryPerformanceFrequency( ¶m );
double ts = ((double)(t2.QuadPart-t1.QuadPart))/((double)(param.QuadPart)); //le temps en seconde
double ts = (((double)(t2.QuadPart-t1.QuadPart))/((double)(param.QuadPart)))*1000.0; //le temps en ms

vous obtiendrez qqch du genre : ts = 0.00039096927800171260 ms (c'est le tps d'execution du code que je vous donne) !!!

Le nanoseconde, c'est deja trop long !!! (ca dechire !!!)

bos.
cs_rt15
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
10
Bonjour,

Désolé je connais très mal le C++.

Déjà, la routine que tu souhaites utiliser ne s'appel pas Sleep (Elle est renommer dans la déclaration en VB6).

Son nom est EXtmr_Sleep.

Donc je dirais :

inp32 = (inpfuncPtr) GetProcAddress(hLib, "EXtmr_Sleep");

Quoi qu'il arrive, en C++, tu devrait pouvoir faire l'équivalent de ma dll en quelques dizaines de lignes, en te servant des API QueryPerformanceCounter et QueryPerformanceFrequency.
bwoufy86
Messages postés
4
Date d'inscription
vendredi 3 janvier 2003
Statut
Membre
Dernière intervention
31 juillet 2006

super cette dll, j'ai hate de l'essayer pour un de mes projet.
Mais le hic c'est que je compte programmer en C++ Builder et j'arrive a charger la dll mais pas a appeler Sleep.
Il me dit acces violation.

quelqu'un pourrai peut etre m'aider ?
je donne mon morceau de code au cas ou !


typedef long _stdcall (*inpfuncPtr) (String lpEx, long nTime = 0);




void TForm1::gererAttente()
{
HINSTANCE hLib;
inpfuncPtr inp32;
short x;
int i;
long z;

/* Load the library */
hLib = LoadLibrary("EX_Time.dll");

if (hLib == NULL) {
Application->MessageBox("LoadLibrary Failed", "Look", MB_OK);
}
else
{
inp32 = (inpfuncPtr) GetProcAddress(hLib, "Sleep");

z = (inp32)(" ",100000);
FreeLibrary(hLib);
}

}

Il me dit ACCESS Violation at adress 0000000.....

Merci d avance !
cs_rt15
Messages postés
3874
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
10
A priori, c'est fait...

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.