Cours VB.NET

Image non disponible


précédentsommairesuivant

XVIII. Optimisation en vitesse

Image non disponible

XVIII-A. Comparaison VB6 VB.Net

VB.NET est t-il rapide?

XVIII-A-1. Comment VB.NET 2003 est situé en comparaison avec les autres langages de programmation ?

Le site OsNews.com publie les résultats d'un petit benchmark comparant les performances d'exécution sous Windows de plusieurs langage de programmation.

Les langages .NET - et donc le code managé en général - n'ont pas à rougir devant Java, pas plus que face au langage C compilé grâce à GCC.

- Nine Language Performance Round-up: Benchmarking Math & File I/O [OsNews.com]

Article publier sur www.DotNet-fr.org

XVIII-A-2. Vitesse de VB6, VB.NET 2003, 2005, 2008, 2010

En mode Design, pour écrire un programme, l'IDE VB 2008 est beaucoup plus rapide que VB 2005.

En exécution par contre:

Exemple No1

Sur une même machine P4 2.4 G faisons tourner un même programme: 2 boucles imbriquées contenant une multiplication, l'addition à un sinus et l'affichage dans un label:

  • En VB6
 
Sélectionnez

Private Sub Command1_Click()
Dim i As Long
Dim j As Long
Dim k As Long
For i = 0 To 100
For j = 0 To 1000
Label1.Caption = Str(k * 2 + Sin(4)): Label1.Refresh
k = k + 1
Next
Next
End Sub

9 secondes dans l'IDE , 7 secondes avec un exécutable après compilation.

  • En Vb.Net 2003
 
Sélectionnez

Imports System.Math

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ 
 Handles Button1.Click
Dim i As Integer
Dim j As Integer
Dim k As Integer
For i = 0 To 100
For j = 0 To 1000
Label5.Text = (k * 2 + Sin(4)).ToString : Label5.Refresh()
k = k + 1
Next
Next
End Sub

35 secondes dans l'IDE, 25 secondes avec un exécutable après compilation.

En utilisant des 'Integer' ou des 'Long', il y a peu de différence.

  • En Vb.Net 2005 en Vb 2008Image non disponible

Même code:

55 secondes dans l'IDE, 45 secondes avec un exécutable après compilation.

Dur, dur!!!.
Pratiquement la même vitesse en Vb 2008

  • En Vb.Net 2008 et vb 2010Image non disponible

En faisant tourner le même programme sur une autre machine:
Vb 2008=47 s
Vb 2010=43 s
Vb 2010 parrait donc légèrement plus rapide que Vb 2008.

Exemple No2

Sur une même machine P4 2.4 G faisons tourner un même programme: On crée un tableau de 10000 String dans lequel on met des chiffres Puis on trie le tableau.

  • En VB6
 
Sélectionnez

Private Sub Command1_Click()

Dim i As Integer
Dim A(10000) As String
Dim j As Integer
Dim N As Integer
Dim Temp As String
N = 9999
'remplir le tableau
For i = 9999 To 0 Step -1
    A(i) = Str(9999-i)
Next i
'trier
For i = 0 To N - 1
    For j = 0 To N - i - 1

       If A(j) > A(j + 1) Then
            Temp = A(j): A(j) = A(j + 1): A(j + 1) = Temp
        End If
 Next j
Next i
End Sub

35 secondes

  • En Vb.Net 2003
 
Sélectionnez

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ 
 Handles Button1.Click
Dim i As Integer
Dim A(10000) As String
For i = 9999 To 0 Step -1
A(i) = (9999 - i).ToString
Next i
Array.Sort(A)
Label1.Text = "ok"
End Sub

< 1 seconde

  • En Vb.Net 2005 beta2 Image non disponible

Même code:

< 1 seconde

Moins d'une seconde avec VB.NET, 35 secondes en VB6.

La méthode 'Sort' est hyper plus rapide que la routine de tri.

(Pour être honnête, il faut dire que mon test n'est pas rigoureux car le tri VB.NET est probablement un tri 'rapide' alors qu'en VB6 la routine basic n'est pas optimisée, je ne compare donc pas les mêmes routines.)

Plusieurs remarques (merci Clement):

1. Le tableau employé n'est pas représentatif : il n'est pas constitué d'éléments à répartition aléatoire.

2. Ce ne sont pas les mêmes routines. Vous utilisez une version optimisée du bubble sort, le tri à bulle, de complexité O(n²). Dès lors, pour trier mille éléments, avec une complexité de comparaison de deux string en O(1), il faut approximativement un million d'opérations dans le pire cas.

3. Le Framework emploie un tri fusion, c'est-à-dire un tri de complexité O(n*log(n)), soit environ 10'000 opérations (le log est un log binaire en réalité). Il ne s'agit pas d'optimiser la routine de tri, mais de la changer.

4. Si on code effectivement un tri fusion en VB 6, alors la routine 'Sort' en VB.net n'est pas « hyper plus rapide » que la routine de tri, mais la routine de tri est fausse.

En conclusion:

La couche du Framework semble ralentir considérablement la vitesse du code.

Mais, en VB.net, il faut raisonner différemment et utiliser judicieusement les classes et les méthodes au lieu de taper de longues routines.

Cela fait que en VB.Net:

Le code est plus court et compact (moins de temps de développement)

Le code est plus rapide.

Vitesse comparée Windows Forms WPF

Thomas Lebrun dans son blog teste: le remplissage de 10 000 objets dans une ComboBox ListView Treeview et les trie en vb 2008?.

"D'une manière générale, les applications WPF sont plus performantes que les applications WindowsForms. Seul le contrôle ListView semble dérogé à ce constat mais la différence est sans doute suffisamment infime pour qu'elle soit ignorée."

Un bémol:la position d'un contrôle dans une grid un panel peut influencer fortement la vitesse s'il y a une gestion d'affichage différente.

Enfin l'usage immodéré d'effets visuels dans une interface ralentie et consomme du temps CPU.

XVIII-B. Chronométrer le code, compteur temps mémoire..

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)

XVIII-B-1. 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.

 
Sélectionnez

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()

 
Sélectionnez

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...

XVIII-B-2. Créer un compteur pour les temps très courts (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.

Le temps écoulé est en ms, sachant toutefois que le compteur de ticks (1tick=100 nanosecondes) n'est mis à jour que toutes les 16ms environ...(15,62468 ms) Now utilise la même horloge. En ce qui concerne le timer, tout intervalle inférieur à 15 donnera le même résultat, 15,6 ms d'attente. A noter également pour le timer que le délai d'attente ne commence pas au top précédent, mais à la fin de la procédure appelée par ce timer, ce qui peut augmenter sa période.

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%.

 
Sélectionnez

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

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

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?

XVIII-B-3. Créer un compteur pour les temps très courts (Framework 2, VB2005)

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

 
Sélectionnez

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

' Code à mesurer
MsgBox(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.Attention, pour mesurer le temps de fonctionnement d'une routine, les millisecondes c'est trop long!!

On a également la propriété ElapsedTicks pour compter les Ticks (1 Ticks: 100 nanosecondes).

 
Sélectionnez

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

'routine à mesurer

S.Stop()
Dim t1 As Decimal = S.ElapsedTicks
        MsgBox( t1.ToString)

Enfin si l'on veut compter en TimeSpan, utilisons la propriété Elapsed.

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

 
Sélectionnez

stopwatch.Stop     'arrête le compteur

stopwatch.Start    'fait redémarrer le compteur

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

stopwatch.Restart()  'redémmare le compteur (A partir de VB 2010)
				

XVIII-B-4. Compteur de performance

On généralise aux compteurs de performance qui peuvent calculer des temps mais aussi de la mémoire...

Un compteur de performance surveille le comportement d'un élément de performance mesurable, sur un ordinateur.
(Composants physiques tels que processeurs, disques et mémoire; objets système: processus et threads).

Il faut créer une instance de la classe PerformanceCounter, puis indiquer la catégorie avec laquelle le composant doit interagir, puis choisir un compteur de cette catégorie.
Par exemple, dans la catégorie Memory des compteurs système assurent le suivi des données en mémoire, telles que le nombre d'octets disponibles et le nombre d'octets mis en cache.
Les catégories les plus utilisées sont: Cache, Memory, Objects, PhysicalDisk, Process, Processor, Server, System et Thread.

Ici on va utiliser la catégorie 'Process' (processus, programme), le compteur 'octet' et on va surveiller le programme 'Explorer'.
A chaque fois qu'on exécute ce code, il donne la quantité de mémoire utilisée.

 
Sélectionnez

Dim PC As New PerformanceCounter()
PC.CategoryName = "Process"
PC.CounterName = "Private Bytes"
PC.InstanceName = "Explorer"
MessageBox.Show(PC.NextValue().ToString())

NextValue retourne le contenu du compteur.

Il est possible de compter du temps, mais la doc avec les noms des compteurs est très difficile à trouver!! Si vous avez une adresse!!

XVIII-C. Comment accélérer une application VB.net ?

L'optimisation est la pratique qui consiste généralement à réduire le temps d'exécution d'une fonction, l'espace occupé par les données et le programme. Elle ne doit intervenir qu'une fois que le programme fonctionne et répond aux spécifications. L'expérience montre qu'optimiser du code avant revient le plus souvent à une perte de temps et s'avère néfaste à la clarté du code et au bon fonctionnement du programme.

XVIII-C-1. Utilisation des nouvelles fonctionnalités

Il faut raisonner différemment et utiliser judicieusement les classes et les méthodes au lieu de taper de longues routines.

Exemple:

La méthode 'Sort' d'un tableau est hyper plus rapide que la routine de tri écrite en code.

 
Sélectionnez

Array.Sort(A)

est hyper plus rapide que:

 
Sélectionnez

For i = 0 To N - 1
    For j = 0 To N - i - 1

        If A(j) > A(j + 1) Then
            Temp = A(j): A(j) = A(j + 1): A(j + 1) = Temp
        End If

    Next j
Next i

La méthode BinarySearch de la Classe Array est hyper plus rapide que n'importe quelle routine écrite en code pour rechercher un élément dans un tableau trié.

 
Sélectionnez

I=Array.BinarySearch(Mois, "Février")

XVIII-C-2. Choix des variables

Les types de données les plus efficaces sont ceux qui emploient la largeur de données native de la plateforme d'exécution. Sur les plateformes courantes, la largeur de données est 32 bits, pour l'ordinateur et le logiciel d'exploitation.

Sur les ordinateurs actuels et en VB la largeur de donnée native est donc de 32 bits:

Pour les entiers les Integer sont donc les plus rapides car le processeur calcul en Integer . Viennent ensuite les Long, Short, et Byte.

Dans les nombres en virgule flottante, les Double sont les plus rapides car le processeur à virgule flottante calcul en Double, ensuite se sont les Single puis les Decimal.

Les Calculs en Decimal sont 10 fois plus lents que les calculs en Single.

Si c'est possible utiliser les entiers plutôt que les nombres en virgules flottantes car le travail sur les nombres entiers est beaucoup plus rapide.

Bon choix des unités:

Exemple: pour stocker les dimensions d'une image, on utilisera les pixels: l'image aura un nombre entier de pixels et on peut ainsi utiliser une variable Integer, alors que si on utilise les centimètres on devra travailler sur des fractionnaires donc utiliser par exemple des Singles ce qui est plus lent.

L'usage de constantes est plus rapide que l'usage de variable, car la valeur d'une constante est directement compilée dans le code.

Pour stocker une valeur, une variable est plus rapide qu'une propriété d'objet.

Les variables 'par valeur' peuvent être plus rapide que celle 'par référence'. Les premières sont stockées directement dans la pile, les secondes sur le 'tas':

Si vous utilisez une valeur entière, créer une variable Integer et non une variable Object.

Typer le plus possible les variables:
Fuyez le plus possible les variables objets: si vous affecté un Integer à une variable objet, le CLR soit faire des opérations de boxing, avec recopie de la valeur et enregistrement du type de la variable ce qui prend du temps; Et l'inverse pour UnBoxing!
Vous pouvez éliminer le boxing/unboxing par inadvertance par la mise en Option Strict On.

Une variable est à liaison tardive si elle est déclarée en tant que type objet et non d'un type de données explicite. Lorsque votre code accede à des membres d'une telle variable, le Common Language Runtime est obligé d'effectuer la vérification de type et de recherche de membres au moment de l'exécution.

XVIII-C-3. Tableau

Le CLR est optimisé pour les tableaux unidimensionnel. Employer le moins de dimension possible dans un tableau.

L'usage des tableaux de tableau 'A(9),(9)' est plus rapide que les tableaux multidimensionnels 'A(9,9)'.

Tableau ou Collections?

Pour rechercher un élément dans un ensemble d'élément à partir de son index , utilisez un tableau (l'accès à un élément d'index i est plus rapide dans un tableau que dans une collection)

L'accès a une variable simple est plus rapide que l'accès à un élément d'un tableau:

Si vous utilisez de nombreuse fois à la suite le même élément d'un tableau, le mettre dans une variable simple, elle sera plus rapide d'accès:

 
Sélectionnez

Dim a As Integer= P(6)

b=a*3

c=a+2

...

z=a*5

Est plus rapide que:

 
Sélectionnez

b=P(6)*3

c=P(6)+2

...

z=P(6)*5

L'usage d'un tableau est plus rapide qu'une multitude de SelectCase ou de If Then:

Exemple: obtenir le nom du mois en fonction de son numéro d'ordre.

 
Sélectionnez

Dim Mois() As String ={Janvier,Février,Mars,Avril,Mai,Juin,Juillet}

nomDuMois=Mois(i-1)

Est plus rapide que:

 
Sélectionnez

Select Case i

    Case 1

        nomDuMois="Janvier"

    Case 2

        nomDuMois="Février"

    ....

End Select.

Pour rechercher rapidement un élément dans un tableau:

Utiliser la méthode Binaryscearch plutôt que IndexOf.

Pour la méthode Binaryscearch , le tableau doit être trié. (Le trier avant la recherche).

On peut utiliser des méthodes génériques (VB 2005) pour travailler sur les tableaux. c'est beaucoup plus rapide.

Exemple recherche dans un tableau de short nommé monTab l'élément 2

index= Array.indexOf (Of Short)(monTab, 2) est hyper plus rapide que

index= Array.indexOf (monTab, 2) car la première version avec généric est directement optimisée pour les Short.

Il est est de même pour Binarysearch et Sort.

Cela est valable pour les types 'valeur' (peu d'intérêts pour les strings par exemple).

XVIII-C-4. Collections

Si on ne connaît pas le nombre d'éléments maximum et que l'on doit ajouter, enlever des éléments, il vaut mieux utiliser une collection (ArrayList) plutôt qu'un tableau avec des Dim Redim Preserve. Mais attention une collection comme ArrayList est composée d'objets, ce qui implique une exécution plus lente.

Pour rechercher un élément dans un ensemble d'éléments à partir d'un index, utilisez un tableau.

Pour rechercher un élément dans un ensemble d'éléments à partir d'une clé (KeyIndex) , utilisez une collection (l'accès à un élément ayant la clé X est plus rapide dans une collection que dans un tableau; dans une tableau il faut en plus écrire la routine de recherche).

En VB2005 on peut utiliser les Collections génériques plus rapides car typées (Les éléments ne sont pas des Objets).

XVIII-C-5. Éviter la déclaration de variables 'Objet' et les liaisons tardives, les variables non typées

Eviter de créer des variables Objet:

Pour créer une variable et y mettre une String:

 
Sélectionnez

Dim A  crée un 'Objet' A

Il est préférable d'utiliser:

 
Sélectionnez

Dim A As String

La gestion d'un objet est plus lente que la gestion d'une variable typée.

Il faut aussi éviter les liaisons tardives: Une liaison tardive consiste à utiliser une variable Objet. A l'exécution, donc tardivement, on lui assigne un type, une String ou un Objet ListBox par exemple. Dans ce cas, à l'exécution, VB doit analyser de quel type d'objet il s'agit et le traiter, alors que si la variable a été déclarée d'emblée comme une String ou une ListBox, VB a déjà prévu le code nécessaire en fonction du type de variable. Utilisez donc des variables typées.

Utilisez donc des variables le plus typées possible.

Si une variable doit être utilisée pour une assignation de Button, ListBox... plutôt que la déclarer en Objet, il est préférable de la déclarer en System.Windows.Forms.Control

Utilisez donc des variables ByVal plutôt que ByRef. Les types ByVal sont gérés sur la pile, les types ByRef sur 'le tas' c'est plus long.

De manière générale, si le compilateur sait quel type de variable il utilise, il fait des contrôles lors de la compilation; si il ne sait pas, il fait des contrôles lors de l'exécution et cela prend du temps à l'exécution.

XVIII-C-6. Utiliser les bonnes 'Options'

Option Strict On permet de convertir les variables de manière explicite et accélère le code. De plus on est poussé à utiliser le bon type de variable.

Si on affecte une valeur 'par référence' à un objet, le CLR doit créer un objet, transformer la valeur, la mettre dans l'objet et gérer le pointeur. (On nomme cela 'boxing'), c'est très long. L'inverse c'est du 'unboxing'.

Si on utilise Option Strict Off le boxing se fait automatiquement et systématiquement et c'est long.

Si on utilise Option Strict On, on a tendance (ce qui est bien) à moins utiliser des variables de type différent, ce qui diminue le nombre de boxing-unboxing; et cela oblige si on utilise des variables différentes à caster à l'aide d'instructions qui sont plus rapide.

Donc utiliser Option Strict On et choisir des variables du même type dans une routine afin de réduire au minimum les conversions.

Choisir les méthodes de conversion, quand elle sont nécessaire, les plus typées possible:

Pour les conversions en entier par exemple CInt est plus rapide que CType car CInt est dédié aux entiers.

Option Compare Binary accélère les comparaisons et les tris (la comparaison binaire consiste à comparer les codes unicode des chaînes de caractère).

XVIII-C-7. Pour les fichiers, utiliser System.IO

L'utilisation des System.IO classes accélère les opérations sur fichiers (en effet, les autres manières de lire ou d'écrire dans des fichiers comme les FileOpen font appel à System.IO: autant l'appeler directement!!)

Utiliser donc:

  • Path, Directory, et File
  • FileStream pour lire ou écrire
  • BinaryReader and BinaryWriter pour les fichiers binaires.
  • StreamReader and StreamWriter pour les fichiers texte.

Utiliser des buffers entre 8 et 64K

XVIII-C-8. If Then ou Select Case ?

Plutôt qu'un If Then et une longue série de ElseIf, il est préférable d'utiliser un SelectCase qui en Vb est plus rapide (20%)

Dans les Select Case mettre les 'case'fréquent et qui reviennent souvent en premier, ainsi il n'y a pas besoin de traiter tous les Case:

 
Sélectionnez

Select Case Variable

Case Valeur1

... Valeur1 revient souvent

Case Valeur2

...

...

Case Valeur25

... Valeur25 survient rarement

End Select

 

Plutôt qu'un If B= True Then.. il est préférable d'utiliser un If B Then .. cela va 2 fois plus vite en VB. B étant une opération booléenne quelconque. En effet , entre If et Then l'expression est de toutes façons évaluée pour savoir si elle est = True.

XVIII-C-9. Utiliser les bonnes 'Opérations'

Si possible:

Utiliser :"\"

Pour faire une vraie division on utilise l'opérateur '/'

Si on a seulement besoin du quotient d'une division (et pas du reste ou du résultat fractionnaire) on utilise '\', c'est beaucoup plus rapide.

Utiliser :"+="

A+= 2 est plus rapide que A= A+2

Utiliser :AndAlso et ElseOr

AndAlso et ElseOr sont plus rapide que And et Or.

(puisque la seconde expression n'est évaluée que si nécessaire)

Arrêter le test lorsqu'on connaît la réponse:

 
Sélectionnez

if x<3 And y>15 then

Les 2 expressions sont évaluées x<3 et x>15 puis le And est évalué alors que dès que x<3 on pourrait arrêter de tester.

Solution:

 
Sélectionnez

if x<3 then

    If y>15 then

..

Réduire les opérations gourmandes:

Remplacer une multiplication par une addition quand c'est possible.

Les fonctions trigométriques (Sinus Cosinus...), Log, Exp.. sont très gourmandes.

(Je me souviens d'un programme , en QuickBasic!! qui affichait de la 3D; plutôt que de calculer plein de sinus , on allait chercher les sinus stockés dans un tableau; cela entraînait un gain de temps phénoménal.)

Calculer des expressions à l'avance:

- Log(2) est très long à calculer surtout s'il est dans une boucle.

 
Sélectionnez

For i=1 to 100000

    R=i*P+ Log(2)

next i

utiliser plutôt le résultat calculé à la main:

 
Sélectionnez

For i=1 to 100000

    R=i*P+ 0.693147

next i

De même si on utilise un membre d'une classe:

 
Sélectionnez

For i=1 to 100000

    R=i*P+ Myobjet.MyPropriété

next i

mettre la valeur de la propriété dans une variable simple, c'est plus rapide:

 
Sélectionnez

Dim valeur As Integer = Myobjet.MyPropriété

For i=1 to 100000

    R=i*P+ valeur

next i

- L'accès à un élément d'un tableau est lent:

 
Sélectionnez

For i=1 to 100000

    R=i*P+ MyTableau (2 ,3 )

next i

mettre la valeur du tableau dans une variable simple, c'est plus rapide si on utilise cette valeur 10000 fois:

 
Sélectionnez

Dim valeur As Integer= MyTableau (2 ,3)

For i=1 to 100000

    R=i*P+ valeur

next i

Pour les conversions utilisez DirectCast plutôt que CType:

CType est moins rapide.

Utiliser les conversion typées plutôt que CType:

Faire d=Cdbl(i) plutôt que d= CType(i, Double)

Cdbl est fait pour convertir en Double alors de CType qui converti 'tout' en 'tout' doit analyser en quel type il faut convertir puis appeler les routines correspondantes.

XVIII-C-10. Utiliser : With End With

With.. End With accélère le code:

 
Sélectionnez

With Form1.TextBox1

    .BackColor= Red

    .Text="BoBo"

    .Visible= True

End With

est plus rapide que

 
Sélectionnez

Form1.TextBox1.BackColor= Red

Form1.TextBox1.Text="BoBo"

Form1.TextBox1.Visible= True

car Form1.TextBox1 est 'évalué' 1 fois au lieu de 3 fois.

XVIII-C-11. Optimiser les boucles

En mettre le moins possible dans les boucles:

Soit un tableau J(100,100) d'entiers:

Soit un calcul répété 100 000 fois sur un élément du tableau, par exemple:

 
Sélectionnez

For i=1 to 100000

    R=i*J(1,2)

next i

On va 100000 fois chercher un élément d'un tableau,c'est toujours le même!

Pour accélérer la routine (c'est plus rapide de récupérer la valeur d'une variable simple plutôt d'un élément de tableau), on utilise une variable intermédiaire P:

 
Sélectionnez

Dim P as Integer

P=J(1,2)

For i=1 to 100000

    R=i*P

next i

c'est plus rapide.

De la même manière si on utilise une propriété (toujours la même) dans une boucle, on peut stocker sa valeur dans une variable car l'accès à une variable simple est plus rapide que l'accès à une propriété.

Les opérations qui ne sont pas modifiées dans la boucle doivent donc être mises à l'extérieur.

Eviter les On Error dans des grandes boucles, qui ralentissent considérablement; par contre, contrairement à ce qu'on entend, le Try Catch Finally dans une très grande boucle ralentit très peu.

Dans une boucle tournant 1000000000 fois:

5 s sans gestion d'erreur.

6 s si la boucle contient Try Catch

2 mn si la boucle contient on error resume next!!

Fusionner plusieurs boucles si nécessaire:

Au lieu de faire 2 boucles:

 
Sélectionnez


For i=1 to 100000

    P(i)=i

Next i

For i=1 to 100000

    Q(i)=i

Next i

Faire:

For i=1 to 100000

    P(i)=i

    Q(i)=i

Next i

C'est possible quand les 2 boucles ont même valeur initiale et finale.

En cas de boucles imbriquées placer la boucle la plus grande à l'intérieur:

 
Sélectionnez

For i=1 to 100    '100 itérations

For j=1 to 10     '100 X 10 itérations

    ...

Next j

Next i

100+(100X10) = 1100 itérations de compteur

 

For j=1 to 10     '10 itérations

For i=1 to 100    '100 X 10 itérations

    ...

Next j

Next i

10+(100X10) = 1010 itérations de compteur

Attention: le nombre de boucle est le même (1000), par contre le nombre d'itération de compteur (à chaque fois qu'on passe par une instruction 'For') n'est pas le même.
Comptons les itérations:

 
Sélectionnez

 
 Dim iteration, boucle, i, j As Integer
 
 Private Sub Button1_Click
        For i = 1 To 100    '100 iterations
            iteration = itération + 1
            For j = 1 To 10     '100 X 10 iterations
                iteration = iteration + 1
                boucle = boucle + 1

            Next j

        Next i
        MsgBox(iteration)
        '100+(100X10) = 1100 iterations de compteur

        iteration = 0 : boucle = 0

        For j = 1 To 10     '10 iterations
            iteration = iteration + 1
            For i = 1 To 100    '100 X 10 iterations
                iteration = iteration + 1
                boucle = boucle + 1

            Next i

        Next j
        MsgBox(iteration)
        '10+(10X100) = 1010 iterations de compteur

    End Sub

En conclusion:

(1100-1010)/1100 gain 8% d'itérations de compteur en moins (le nombre de boucle restant égal)

Sortir avec exit for dès qu'on a trouvé ou qu'il n'y a plus rien a chercher.

Exemple: rechercher un élément dans un tableau avec une boucle.

Des que l'élément a été trouvé ou que le tableau ne contient plus rien, on quitte la boucle avec un Exit For.

XVIII-C-12. Appel de procédure

Si on appelle une petite procédure dans une grande boucle, on a parfois intérêts à mettre le contenu de la procédure directement dans la boucle. Cela évite les appels et retour. C'est plus rapide.

Si on a:

 
Sélectionnez

 For i=1 to 100000

        resultat= Calcul(i,j)

    Next i

     

    Functin Calcul (i As Integer, j As Integer)

        i=i+3

        Return i*j

    End Sub

    C'est plus rapide d'écrire:

    For i=1 to 100000

        i=i+3

        resultat= i*j

    Next i

     

Si la procédure est longue et complexe et surtout si on a besoin de l'utiliser à différents endroits dans le programme, il est préférable d'utiliser une procédure.

Ne multipliez pas les petites procédures courtes ce qui entraine dans une boucle un grand nombre d'appel, preférez l'appel d'une procédure unique:
Exemple: Plutôt que d'appeler Calcul1 puis Calcul2 puis Calcul3 puis Calcul4 il est préférable de créer une procédure unique nommée Calculs qui fait tous les calculs; le découpage excessif s'il n'est pas absolument nécessaire diminue les performances.

XVIII-C-13. Usage de threads

Il peut être judicieux d'utiliser des threads, pour accélèrer certaines applications.

XVIII-C-14. Comment accélérer quand on utilise des 'String' ?

Utiliser & pour la concaténation de chaîne plutôt que +.

Utiliser:

 
Sélectionnez

  s &= "mon" & "ami"

plutôt que:

 
Sélectionnez

   s += "mon" + "ami"

Utiliser les StringBuilder.

Exemple d'une opération coûteuse en temps:

 
Sélectionnez

 Dim s As String = "bonjour"
     

    s += "mon" + "ami"

En réalité le Framework va créer 3 chaînes en mémoire avec toutes les pertes en mémoire et en temps que cela implique.

Pour effectuer des opérations répétées sur les string, le framework dispose donc d'une classe spécialement conçue et optimisée pour ça : System.Text.StringBuilder.

Pour l'utiliser, rien de plus simple

 
Sélectionnez

 Dim sb As new System.Text.StringBuilder()

    sb.Append("bonjour")

    sb.Append("mon ami")

    Dim s As String

    s = sb.ToString()

La méthode ToString de la classe StringBuilder renvoi la chaîne qu'utilise en interne l'instance de StringBuilder.

Pour comparer 2 StringBuilder utiliser la méthode Equals plutôt que =.

XVIII-C-15. Comment accélérer l'affichage ?

Formater le plus vite possible le texte:

Pour mettre en forme des nombres et les afficher Format est puissant (Prise en charge de la culture..), mais si on peut utiliser ToString c'est plus rapide (ToString est aussi plus rapide que Cstr).

ChrW utilisé pour afficher un caractère(et AscW) sont plus rapide que Chr et Asc car ils travaillent directement sur les Unicodes.

Précharger les fenêtres et les données.

Quand une fenêtre en ouvre une autre, le temps de chargement est long, l'utilisateur attend!

Solution:

En début de programme pré charger les fenêtres en les rendant invisible. Lors de l'utilisation de ces fenêtres il suffira de les rendre visible, ce qui est plus rapide que de les charger.

Certaines données (liste..)doivent être chargées une fois pour toute, le faire en début de programme, lors de l'affichage de la fenêtre 'Splach' par exemple.

Afficher les modifications en une fois dans un TextBox:

A chaque fois que l'on fait une modification de propriété (couleur, taille..) ou de contenu (texte dans un TextBox) Vb met à jour chaque modification . Si on modifie tout et que l'on re-affiche tout, cela va plus vite.

Pour le cas du TextBox ne pas faire.

 
Sélectionnez

TextBox1.Text = TextBox1.Text + "Bonjour"

TextBox1.Text = TextBox1.Text + ""Monsieur"

faire:

 
Sélectionnez

Dim T as string

T = "Bonjour"

T &= "Monsieur"

TextBox1.Text = T

Le texte est affiché en une fois, en plus, cela ne 'clignote' pas.

Rendre l'affichage de l'objet inactif, faire toutes les modifications puis réactiver l'affichage.

Cas d'affichage dans une grid (MsFlexGrid).

Afficher en 2 fois dans une ListBox:

A l'inverse pour ne pas faire attendre un affichage très long, afficher le début (l'utilisateur voit apparaître quelque chose à lire) il est occupé un temps, ce qui permet d'afficher le reste.

Exemple : remplir une listBox avec un grand nombre d'éléments: en afficher 5 rapidement puis calculer et afficher les autres. L'utilisateur à l'impression de voir la ListBox se remplir immédiatement.

Pour les images utiliser le DoubleBuffer.

Pour faire patienter l'utilisateur lors d'une routine qui dure longtemps ?(et lui montrer que l'application n'est pas bloquée):

  • Transformer le curseur en sablier en début de routine, remettre un curseur normal en fin de routine.
  • Utiliser une ProgressBar (pour les chargements longs par exemple)

XVIII-C-16. Utiliser les tableaux en mémoire plutôt que la lecture de fichiers sur disque

On a un fichier de 300 noms, plutôt que de lire 300 enregistrements sur disque pour rechercher le bon, charger en mémoire le fichier dans un tableau( en début de programme); la recherche sera ensuite, dans le tableau en mémoire, extrêmement rapide.

XVIII-C-17. Ce qui n'influence pas la rapidité du code

Les boucles For, Do, While ont toutes une vitesse identique.

Quand on appelle une procédure, c'est aussi rapide si elle est dans le même module ou un autre module.

XVIII-C-18. Compilation DLL

Le chargement de dll est gourmant en temps; moins il y en a, plus c'est rapide.

Les dll ne sont chargées qu'une fois.

La compilation en mode Debug fourni un code plus lent.

XVIII-C-19. En conclusion

Une optimisation sur une ou deux instructions apporte un gain de temps négligeable.

L'optimisation dans les grandes boucles est perceptible.

Le travail en mémoire plutôt que sur disque accélère considérablement.

Les goulots d'étranglement sont les longues boucles et l'accès aux bases de données.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.