|
Site |
Cours VB.net |
|
|
|
Optimiser en vitesse. |
|
|
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.
Comment accélérer une application VB.net?
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.
Array.Sort(A)
est hyper plus rapide que:
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é.
I=Array.BinarySearch(Mois, "Février")
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.
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.
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 l'é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:
Dim a As Integer= P(6)
b=a*3
c=a+2
...
z=a*5
Est plus rapide que:
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.
Dim Mois() As String ={Janvier,Février,Mars,Avril,Mai,Juin,Juillet}
nomDuMois=Mois(i-1)
Est plus rapide que:
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).
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 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)
En VB2005 on peut utiliser les Collections génériques plus rapides car typées (Les éléments ne sont pas des Objets).
Eviter 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:
Dim A crée un 'Objet' A
Il est préférable d'utiliser:
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.
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).
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:
Utiliser des buffers entre 8 et 64K
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:
Select Case Variable
Case Valeur1
... Valeur1 revient souvent
Case Valeur2
...
...
Case Valeur25
... Valeur25 survient rarement
End Select
If..Then
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 (par en C#). B étant une opération booléenne quelconque.
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:
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:
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 Sinus Cosinus... 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.
For i=1 to 100000
R=i*P+ Log(2)
next i
utiliser plutôt le résultat calculé à la main:
For i=1 to 100000
R=i*P+ 0.693147
next i
- De même si on utilise un membre d'une classe:
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:
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:
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:
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.
Utiliser :With End With
With.. End With accélère le code:
With Form1.TextBox1
.BackColor= Red
.Text="BoBo"
.Visible= True
End With
est plus rapide que
Form1.TextBox1.BackColor= Red
Form1.TextBox1.Text="BoBo"
Form1.TextBox1.Visible= True
car Form1.TextBox1 est 'évalué' 1 fois au lieu de 3 fois.
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:
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:
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 ralentissent 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:
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:
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
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.
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:
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.
Usage de thread:
Il peut être judicieux d'utiliser des threads, pour accélèrer certaines applications.
Comment accélérer quand on utilise des 'String':
Utiliser & pour la concaténation de chaîne plutôt que +.
Utiliser:
s &= "mon" & "ami"
plutôt que:
s += "mon" + "ami"
Utiliser les StringBuilder.
Exemple d'une opération coûteuse en temps:
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
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 =.
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.
TextBox1.Text = TextBox1.Text + "Bonjour"
TextBox1.Text = TextBox1.Text + ""Monsieur"
faire:
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):
Utiliser les tableaux en mémoire plutôt que la lecture de fichier 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.
Ce qui n'influence pas la rapidité du code:
Les boucles For , Do , While ont toutes une vitesse identique.
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.
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.
|
|
|
|
|