Site : |
Cours VB.net | ![]() |
| Optimiser en vitesse. |
VB.NET est t-il rapide?
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.
Voici un aperçu des résultats chiffrés (valeurs les plus faibles = les
meilleures performances) :
int long double trig I/O TOTAL Visual C++ 9.6 18.8 6.4 3.5 10.5 48.8 Visual C# 9.7 23.9 17.7 4.1 9.9 65.3 gcc C 9.8 28.8 9.5 14.9 10.0 73.0 Visual Basic 9.8 23.7 17.7 4.1 30.7 85.9 Visual J# 9.6 23.9 17.5 4.2 35.1 90.4 Java 1.3.1 14.5 29.6 19.0 22.1 12.3 97.6 Java 1.4.2 9.3 20.2 6.5 57.1 10.1 103.1 Python/Psyco 29.7 615.4 100.4 13.1 10.5 769.1 Python 322.4 891.9 405.7 47.1 11.9 1679.0
- Nine Language Performance Round-up: Benchmarking Math & File I/O [OsNews.com]
Article publier sur http://www.dotnet-fr.org/
VB.NET(2003 ou 2005) est-il plus rapide que VB6?
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
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.
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 1000Label5.Text = (k * 2 + Sin(4)).ToString : Label5.Refresh()
k = k + 1
Next Next End Sub35 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 beta2 ![]()
Même code:
55 secondes dans l'IDE, 45 secondes avec un exécutable après compilation.
Dur, dur!!!.
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.
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
Private
Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim i As IntegerDim 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 ![]()
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.)
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.
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
Choix des variables:
Sur les ordinateurs actuels et en VB la largeur de donnée native est 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.
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'.
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)'.
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.
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 (ListArray) plutôt qu'un tableau avec des Dim Redim Preserve. Mais attention une collection est composée d'objet, ce qui implique une exécution plus lente.
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)
Eviter la déclaration de variables 'Objet' et les liaisons tardives:
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
Utiliser les bonnes 'Option':
Option Strict On permet de convertir les variables de manière explicite et accélère le code.
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 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.
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
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 x>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 x>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
Pour les conversions utilisez DirectCast plutôt que CType:
CType est moins rapide.
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.
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émoires 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:
Pour mettre en forme des nombres et les afficher Format est puissant, 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(Celle qui contient une belle image et qui s'ouvre en premier)
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 et affiche 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.
Cas d'affichage dans une 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 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.
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.