Juste pour un dimanche

Résolu
jmfmarques Messages postés 7666 Date d'inscription samedi 5 novembre 2005 Statut Membre Dernière intervention 22 août 2014 - 20 mai 2007 à 16:03
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019 - 20 mai 2007 à 21:59
Bon...
Je viens de le dire ailleurs, alors pourquoi pas ici également ...

J'y suis d'autant plus encouragé que, cette semaine encore, quelqu'un s'est "frotté" à ce genre de chose...

De quoi s'agit-il ?
De la Fonction Timer de VB et de l'utilisation de la fonction GetTickCount de l'API de Windows...
Je sais... je sais... (on va me parler une fois de plus de la faculté qu'a la 2ème de compter en millisecondes alors que la 1ère ne le peut pas (dixit l'aide en ligne mais non exact... et j'ai déjà eu l'occasion de le dire à propos de l'estimation de la longueur d'un click "long"...) ...

La preuve ?

Private Declare Function GetTickCount& Lib "kernel32" ()
Private Sub Command1_Click()
  Dim starting As Long, starting1 As Long
  starting = Timer * 1000
  starting1 = GetTickCount()
  For i = 1 To 1000000
    DoEvents
  Next
  MsgBox "with timer : " & CLng((Timer * 1000) - starting) & " milliseconds  " & _
  "and with Gettickcount = " & CLng(GetTickCount() - starting1) & " milliseconds"
End Sub

La différence de 1 milliseconde entre une méthode et l'autre (qu'il vous sera possible de constater dans certains cas) ne résulte que du fait du traitement de l'une avant l'autre !

Je ne veux pas faire un "snipet" pour si peu... juste le dire...


Bon dimanche.

18 réponses

BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
20 mai 2007 à 20:15
GetTickCount fonctionne comme le Timer, même résolution de synchro de l'horloge donc AUCUNE précision à la milliseconde. C'est d'ailleurs bien précisé dans MSDN.
La seule haute précision est QueryPerformanceCounter.

ciao...
BruNews, MVP VC++
3
cs_casy Messages postés 7741 Date d'inscription mercredi 1 septembre 2004 Statut Membre Dernière intervention 24 septembre 2014 40
20 mai 2007 à 16:59
Il faut tout de même preciser (chose qui n'est pas forcément très clair dans le msdn) c'est que la fonction Timer est basé sur l'horloge RTC, celle qui est implanté au niveau du bios et qui donne l'heure au niveau système.

Tout ce qui touche sous Windows de près ou de loin à une datation calendaire (comme ici Timer qui renvoie le nombre de secondes depuis minuit) est basé sur cette horloge, horloge qui a une résolution de 20ms (précisément18.2ms). Donc pour toutes ces fonctions tout résultat ou partie de résultat inférieur à 20ms doit etre pris avec extreme  prudence car pas forcement significatif.

A contrario GetTickCount, n'est pas basé sur l'horloge RTC, mais sur une des horloges, timers et autres compteurs systèmes qui eux sont en lien direct avec l'oscilateur qui cadence le processeur. Il en va ainsi une bien meilleure precision. D'ailleurs GetTickCount n'est rien d'autre qu'un compteur.

Donc pour tout ce qui se trouve en dessous de 20 ms, il est illusoire de faire confiance à la fonction Timer, même si en apparence ça semble fonctionner.

PS : J'ai eu l'occasion de me confronter à ce problème dans le cadre d'une expertise de code pour laquelle j'aimis pas loin d'un mois complet à cerner et comprendre le problème. D'autant plus qu'il y avait dans ce même code un second bug lié au (très) faux amis qui est le controle Timer du style "Pourquoi, pour un timer à 150ms avec un code relativement conséquent, environ 15ms d'execution, au bout de 1.5 sec, il n'y avait eu que 9 execution au lieu de 10". Il m'a fallu monter des carte d'entrée/sortie, et branché des osciloscope pour arriver à comprendre. Aucune doc n'existe sur le net pour ce problème.

---- Sevyc64  (alias Casy) ----<hr size="2" width="100%" /># LE PARTAGE EST NOTRE FORCE #
0
jmfmarques Messages postés 7666 Date d'inscription samedi 5 novembre 2005 Statut Membre Dernière intervention 22 août 2014 27
20 mai 2007 à 17:20
Bonjour Casy,

Et pourtant !
Essaye...
Si l'un n'est pas fiable, l'autre non plus alors

Private Declare Function GetTickCount& Lib "kernel32" ()




Private Sub Command1_Click()
 Dim j As Integer
  For j = 10000 To 0 Step -200
    testons j
  Next
End Sub


Private Sub testons(combien As Integer)
   Dim starting As Long, starting1 As Long
  starting = Timer * 1000
  starting1 = GetTickCount()
  For i = 1 To combien
    DoEvents
  Next
  Me.Print "with timer : " & CLng((Timer * 1000) - starting) & " milliseconds  " & _
  "and with Gettickcount = " & CLng(GetTickCount() - starting1) & " milliseconds"


End Sub


Private Sub Form_Activate()
  Me.Move 0, 0, Screen.Width, Screen.Height
End Sub
0
Gobillot Messages postés 3140 Date d'inscription vendredi 14 mai 2004 Statut Membre Dernière intervention 11 mars 2019 34
20 mai 2007 à 17:23
Salut,
hum ! testons donc et voyons les différences:
1) les deux sont parfaitement synchronisés,
    la différence affichée est toujours la même à +/- 1ms près
2) résolution maxi atteinte:
    par Timer --> 15,625 ms
    par Gettickcount --> 16 ms

Daniel
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
jmfmarques Messages postés 7666 Date d'inscription samedi 5 novembre 2005 Statut Membre Dernière intervention 22 août 2014 27
20 mai 2007 à 17:39
Hé oui ! (bonjour Daniel)

Mais on va tester autrement, pour en avoir le coeur net...
Avec Sleep.
On peut bien évidemment considérer que Sleep lui même n'est pas fiable, mais cette infiabilité n'a pas d'importance, dès lors que l'on va "sleeper" (hé ! hé!) pendant la même durée pour les deux mesures !
et là encore, ma foi, les résultats sont semblables ...

Allons-y donc :

Private Declare Function GetTickCount& Lib "kernel32" ()
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)


Private Sub Command1_Click()
 Dim j As Integer
  For j = 600 To 0 Step -10
    testons j
    DoEvents
  Next
End Sub


Private Sub testons(combien As Integer)
  Dim starting As Long, starting1 As Long
  starting = Timer * 1000
  starting1 = GetTickCount()
  Sleep combien  Me.Print "temps d'attente " & combien & " >> avec timer : " & CLng((Timer * 1000) - starting) & " milliseconds  " & _
  "et avec Gettickcount = " & CLng(GetTickCount() - starting1) & " milliseconds"
End Sub


Private Sub Form_Activate()
  Me.Move 0, 0, Screen.Width, Screen.Height
  Me.AutoRedraw = True
End Sub


 
0
jmfmarques Messages postés 7666 Date d'inscription samedi 5 novembre 2005 Statut Membre Dernière intervention 22 août 2014 27
20 mai 2007 à 18:05
Bon...
Plus de réactions ( bouh.... je vais pleurer, alors....)

On va clore le sujet, alors, mais pas sans avoir dit ce qui suit :

La fonction GetTickCount n'apporte (contrairement à ce que l'on a l'habitude de penser) qu'une faible précision .

Il en va hélàs de même avec  la fonction TimeGetTime de la Librairie Winmm  (bouh....)

La preuve ?

La voilà :

Private Declare Function GetTickCount& Lib "kernel32" ()
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function timeGetTime Lib "winmm.dll" () As Long


Private Sub Command1_Click()
 Dim j As Integer
  For j = 300 To 0 Step -5
    testons j
    DoEvents
  Next
End Sub


Private Sub testons(combien As Integer)
  Dim starting As Long, starting1 As Long
  starting = Timer * 1000
  starting1 = GetTickCount()
  starting2 = timeGetTime()
  Sleep combien  Me.Print "temps d'attente " & combien & " >> avec timer : " & CLng((Timer * 1000) - starting) & " ms  " & _
  "et avec Gettickcount = " & CLng(GetTickCount() - starting1) & " ms" & _
  "et avec TimeGetTime = " & CLng(timeGetTime() - starting2) & " ms"
End Sub


Private Sub Form_Activate()
  Me.Move 0, 0, Screen.Width, Screen.Height
  Me.AutoRedraw = True
End Sub

Il y a de quoi se la "mo**dre" 
0
Gobillot Messages postés 3140 Date d'inscription vendredi 14 mai 2004 Statut Membre Dernière intervention 11 mars 2019 34
20 mai 2007 à 18:21
pour ma part je suis convaincu,
je retrouve toujours la même chose avec différentes méthodes
    résolution 15,625ms avec Timer et 16ms avec GettickCount
comme Timer est en Single et GettickCount est en entier, la différence n'est pas parlante
donc je considère que les deux sont identiques avec avantage pour Timer d'être en natif et évite d'utiliser une Api extérieure.
merci jmf

Daniel
0
Gobillot Messages postés 3140 Date d'inscription vendredi 14 mai 2004 Statut Membre Dernière intervention 11 mars 2019 34
20 mai 2007 à 19:09
je rajoute que le contrôle Timer se déclenche au bout d'un temps multiple de la résolution (15,625ms chez moi)
le pire cas c'est interval = 16, déclenchement à 15,625 ou 31,25 du simple au double quoi !
si je met interval = 125 j'aurais souvent 125 (8 fois 15,625) et parfois 140,625 (9 fois 15,625)
pas très fiable donc mais rien à voir avec la fonction Timer

Daniel
0
cs_casy Messages postés 7741 Date d'inscription mercredi 1 septembre 2004 Statut Membre Dernière intervention 24 septembre 2014 40
20 mai 2007 à 19:27
Salut Daniel, suite à mon expertise je vais t'en dire plus sur le controle timer.

De ce que j'ai pu observer, lorsque on programme un Controle Timer pour (admettons, pour l'exemple) 125 ms, les déclenchements auront lieu tous les 125+temps d'execution de la routine Timer.
Donc avec une routine Timer assez costaud, par exemple 37ms, les déclenchements auront lieu toutes les 162ms (125+37).

C'est le premier effet K**Cool du timer
Mais si c'était si simple ça sera trop facile.

Deuxième effet que j'ai pu constaté ce que malgrés ce déclenchement toute les 162ms, de temps en temps (à priori assez régulièrement sans que j'ai réellement pu le quantifié) le timer va se resynchronisé sur un multiple de 125. donc régulièrement on pourra avoir un temps intermédiare de moins de 125 ms (peut-etre 50 ou 80 ms) au lieu des 162 ms déduites du premier point.

C'est pour cela que sur la longueur le Controle Timer semble relativement régulier et le phénomène passe totalement inapperçu. Mais lorsque, comme ça a été mon cas, d'ou l'expertise, au bout d'un certain temps donné tu dois avoir un nombre X de déclenchement et que tu te retrouve avec un nombre X-1 ou X-2 ça marche plus (du fait de la resynchronisation, l'écart n'est jamais bien grand).

Voila le double effet K**Cool de notre Faux Ami, le controle Timer

---- Sevyc64  (alias Casy) ----<hr size="2" width="100%" /># LE PARTAGE EST NOTRE FORCE #
0
Gobillot Messages postés 3140 Date d'inscription vendredi 14 mai 2004 Statut Membre Dernière intervention 11 mars 2019 34
20 mai 2007 à 19:43
c'est ce que voulais savoir, est-ce le temps d'exécution du contrôle timer joue même s'il est inférieur à l'interval ?
effectivement oui, si je rajoute un Sleep 10, je vois un nombre de 140,625 bien supérieur (au lieu de 125)
avec un Sleep 50, c'est une catastrophe,  j'obtiens 171,875 (soit 11 fois 15,625) et aussi 187,5 (12 fois 15,625)
j'ai jamais vu de temps inférieur à l'interval  (sauf pour les arrondis 16 au lieu de 15,625)
je ne crois pas qu'il y est de resynchronisation, ce qui perdu est perdu, essayes donc avec un Timer de 16, le temps moyen sera de 1,5 fois 15,625 (moyenne entre 1 fois et 2 fois)
attention ce temps 15,625 n'est pas le même suivant les OS:

In Windows 98 the 18.2 Hz DOS timer was used, with update interval of
about 54.9 ms; but the value transferred from the OS was rounded to a
multiple of 10, and so went up in steps of 50 or 60.

In Windows XP the update rate is 64 Hz, interval 15.625 ms; and that is
rounded only to integer, so rising in steps of 15 or 16.  But
applications can alter the update rate.

Daniel
0
Gobillot Messages postés 3140 Date d'inscription vendredi 14 mai 2004 Statut Membre Dernière intervention 11 mars 2019 34
20 mai 2007 à 20:09
ah ce timer qui donne pas des temps précis !
pour en être sûr je reprends mon préféré QueryPerf ...
sans Sleep (aucun jeu de mot) c'est moins grave ça tourne entre 124 et 125
avec Sleep 50 j'ai 187,50 plus ou moins des poussières
et une fois j'ai eu 203,...  (13 fois la résolution)
bon ça se confirme, il n'y a pas rattrapage.

' voilà la base de mon code pour tester
Private Sub Timer1_Timer()

    QueryPerformanceCounter count2
    List1.AddItem (count2 - count1) * 1000 / freq
    count1 = count2

    Sleep 50

'il faut bien s'arrêter
    nb = nb + 1    If nb >100 Then Timer1.Enabled False: Beep
End Sub

Daniel
0
jmfmarques Messages postés 7666 Date d'inscription samedi 5 novembre 2005 Statut Membre Dernière intervention 22 août 2014 27
20 mai 2007 à 20:55
Bonsoir Brunews,

Je suis tout-à-fait d'accord avec ce que tu exposes là.
Encore faut-il que la machine le "supporte"...
Pour ceux que ces choses intéressent de plus près (mais là également : à condition que la machine le "supporte"):

http://ccrp.mvps.org/index.html?controls/ccrptimer6.htm

(non dénué d'intérêt)
0
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
20 mai 2007 à 21:16
QueryPerformanceCounter est assuré de fonctionner depuis le Pentium, pas hier... (basé sur instruction RDTSC, RDMSR depuis kernel).

ciao...
BruNews, MVP VC++
0
cs_casy Messages postés 7741 Date d'inscription mercredi 1 septembre 2004 Statut Membre Dernière intervention 24 septembre 2014 40
20 mai 2007 à 21:25
QueryPerformanceCounter marchent sur certains celeron, pentium4, 5, amd, ... et pas d'autres, même des plus récents. JE pense que ça ne doit pas dépendre uniquement du processeur

J'ai voulue faire un test sur le portable tout neuf d'une connaissance, il y a quelque mois, avec QueryPerformanceCounter pour plus de precision, ben la fonction n'est pas implantée. Je ne saurais pas dire exactement sur quel proco, mais le pc etait tout neuf, portable pro moyen/haut de gamme d'il y a 6/8 mois environ.

---- Sevyc64  (alias Casy) ----<hr size="2" width="100%" /># LE PARTAGE EST NOTRE FORCE #
0
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
20 mai 2007 à 21:33
C'est le 1er cas que j'entends depuis 10 ans.
Serait intéressant de savoir quelle est cette daube de CPU.

ciao...
BruNews, MVP VC++
0
jmfmarques Messages postés 7666 Date d'inscription samedi 5 novembre 2005 Statut Membre Dernière intervention 22 août 2014 27
20 mai 2007 à 21:52
Hé bé !....


la présente discussion "juste pour un dimanche"...n'avait pas d'autre but que celui d'appeler l'attention sur certains aspects.


Mais aussi de se décontracter un peu.

Ne trahissons pas cet esprit-là, hein ?

Une satisfaction pour chacun d'entre nous : nous nous sommes à la fois divertis un peu et nous sommes, par la même occasion, rémémorés ensemble certains aspects de Windows et de VB.

Je remercie tous ceux qui ont participé à cette discussion. Aucune intervention n'a été ni inutile, ni futile.
Merci à tous.
Bonne nuit.
0
cs_casy Messages postés 7741 Date d'inscription mercredi 1 septembre 2004 Statut Membre Dernière intervention 24 septembre 2014 40
20 mai 2007 à 21:54
Perso ce n'est pas la première fois que je rencontre le cas, mais je pensais moi aussi que depuis quelques années, toutes les machines en étaient systématiquement équipées.
Je l'ai aussi rencontré sur des Pentium III, certes sur des machines d'assembleurs asiatiques. Plus étrange, je l'ai rencontré sur un Pentium IV avec Win NT4.0, la fonction n'était pas reconnue, laosr que le même PC avec Win2000, c'était OK et un pc strictement identique avec WinNT4.0 c'était OK (juste un suffixe différent dans le num de série de la carte mère, CM Dell, les 2 pc ont été achetés en lot).

C'est pour cela, que je pense que ça ne vient pas uniquement du proco.

---- Sevyc64  (alias Casy) ----<hr size="2" width="100%" /># LE PARTAGE EST NOTRE FORCE #
0
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
20 mai 2007 à 21:59
Alors hop, tout le monde à l'ASM, jamais vu RDTSC ne pas fonctionner.

Bonne nuit à tous.

ciao...
BruNews, MVP VC++
0
Rejoignez-nous