Site:  Cours VB.net  
7.4 Chronométrer le code.

On veut comparer 2 routines et savoir laquelle est la plus rapide.

On peut pour compter le temps écoulé:

Utiliser un Timer

Utiliser l'heure système

Utiliser la Classe Environment.Tickcount

Appeler QueryPerformanceCounter, une routine de Kernel32

Utiliser System.Diagnostics.Stopwatch (Framework 2 Vb 2005)

Pour chronométrer une évènement long.

Entendons par évènement long, plusieurs secondes ou minutes.

3 solutions:

- On utilise un Timer, (dans l'évènement Ticks qui survient toutes les secondes, une variable s'incrémente comptant les secondes).(4-5).

 

- On peut utiliser l'heure Système.

Dim Debut, Fin As DateTime

Dim Durée As TimeSpan

Debut=Now

...Routine...

Fin=Now

Durée=Fin-Debut

 

(Utiliser Durée.ToString pour afficher)

- On peut utiliser la variable Environment.tickcount()

Dim Debut, Fin As int32

Debut=Environment.tickcount()

...Routine...

Fin=Environment.tickcount()

Durée=Fin-Debut
 
Cela donne un temps écoulé en ms, sachant toutefois que le compteur de ticks (1tick=100 nanosecondes) n'est mis à jour que toutes les 16ms environ...

 

Créer un compteur pour les temps très court (Framework 1 , VB2003).

C'est le cas pour chronométrer des routines de durée bien inférieure à une seconde.

Cela semblait à première vue facile!!!

J'ai en premier lieu utilisé un Timer, (dans l'évènement Ticks un compteur de temps s'incrémente) mais les intervalles  de déclenchement semblent longs et aléatoires.

 

J'ai ensuite utilisé l'heure système:

Mais 'Durée' est toujours égal à 0 pour les routines rapides car il semble que Now ne retourne pas les millisecondes ou les Ticks.

 

J'ai trouvé la solution chez Microsoft:

Utilisation d'une routine de Kernel32 qui retourne la valeur d'un compteur (QueryPerformanceCounter).

De plus QueryPerformanceFrequency  retourne le nombre de fois que le compteur tourne par seconde.

 

Exemple: comparer 2 boucles, l'une contenant une affectation de variable tableau (b=a(2)) l'autre une affectation de variable simple (b=c), on gagne 33%.

 

Declare Function QueryPerformanceCounter Lib "Kernel32" (ByRef X As Long) As Short

Declare Function QueryPerformanceFrequency Lib "Kernel32" (ByRef X As Long) As Short

 

 

 

Private Sub ButtonGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonGo.Click

Dim debut As Long

Dim fin As Long

Dim i As Long

Dim a(5) As String

Dim b As String

Dim c As String

Dim d1 As Long

Dim d2 As Long

'**********première routine

QueryPerformanceCounter(debut)

For i = 0 To 10000

b = a(2)

Next

QueryPerformanceCounter(fin)

d1 = fin - debut

Label1.Text = d1.ToString

'**********seconde routine

QueryPerformanceCounter(debut)

c = a(2)

For i = 0 To 10000

b = c

Next

QueryPerformanceCounter(fin)

d2 = fin - debut

Label2.Text = d2.ToString

 

Label5.Text = "Gain 2eme routine:" & 100 - Int(d2 / d1 * 100).ToString

End Sub

 C'est cette routine qui est utilisée pour étudier l'optimisation du code

Elle n'est pas parfaite, car sujette à variation: les valeurs sont différentes d'un essai à l'autre en fonction des processus en cours !

Avec VB .Net (version antérieur au Build 1.1.4322.SP1)  le problème est catastrophique: 2 routines identiques ne donnent pas les mêmes temps (bug du compilateur VB?).
 
Il reste que le problème de la variabilité des performances est absolument inévitable puisque Windows est un système préemptif qui peut prendre le contrôle de l'ordinateur à tout moment.

C'est pourquoi la seule façon fiable de faire des tests consiste à avoir une approche expérimentale : on répète la mesure un certain nombre de fois et on fait une moyenne des essais individuels.

 

Pour des routines très très rapides, une autre façon de faire consiste à effectuer des tests sur de longues durées (en répétant des milliers de fois la routine rapide à l'aide d'une boucle)pour arriver à des temps de plusieurs dizaines de secondes, de manière à diluer les interventions de Windows dans le processus.

Il y  a peut-être d'autres solutions?

Créer un compteur pour les temps très court (Framework 2 , VB2005)

Le Framework 2.0 comporte une classe qui encapsule l'utilisation de l' API QueryPerformanceCounter: System.Diagnostics.Stopwatch.

Dim Stopwatch As System.Diagnostics.stopWatch = System.Diagnostics.Stopwatch.StartNew()

' Code à mesurer
Debug.WriteLine(stopWatch.ElapsedMilliseconds.ToString())

La première ligne crée une instance d'un Stopwatch et démarre le compteur.
La propriété ElapsedMilliseconds nous donne le temps mesuré en millisecondes.
On a également la propriété ElapsedTicks pour compter les Ticks (1 Ticks: 100 nanosecondes).
Enfin si l'on veut compter en  TimeSpan, utilisons la propriété Elapsed.

On peut arrêter et redémarrer le compteur plusieurs fois.

stopwatch.Stop     'arrête le compteur

stopwatch.Start    'fait redémarrer le compteur

stopwatch.Reset()  'remet le compteur à zéro