Une colle ! Optimisation VBA

Résolu
us_30 Messages postés 2065 Date d'inscription lundi 11 avril 2005 Statut Membre Dernière intervention 14 mars 2016 - 28 janv. 2006 à 17:15
us_30 Messages postés 2065 Date d'inscription lundi 11 avril 2005 Statut Membre Dernière intervention 14 mars 2016 - 31 janv. 2006 à 01:05
Bonjour,

Dans mes petites recherches pour l'optimisation d’exécution en VBA, je viens de tomber sur une énigme... (J’avais déjà déposé un petit tuto sur VBF sur le sujet, qui se trouve maintenant sur mon modeste site…)


En effet, je me suis intéressé aux types de variables dans un calcul. L’idée était de classer en fonction de la vitesse d’exécution, les différents types de variables, suite à un calcul d’une expression… Naturellement, je pensais que le type Long était normalement le plus rapide que Double… Ceci est correctement vérifier pour l’exécution d’une boucle comme avec FOR TO NEXT… Et en effet, c’est la même chose, si dans un l’expression d’un calcul, toutes les variables sont du type LONG, par rapport à DOUBLE (quoique assez faible…).


MAIS là, où je reste le cul par terre ! (excusez moi de l’expression), c’est que suite à une erreur de manipulation, j’ai découvert qu’une combinaison (un peu savante) du type Long et Double, donne un calcul plus rapide !


J’ai testé de nombreuses fois le phénomène, sur 2 PC, sous Excel 97 et 2003… et même constat… JE suis vraiment stupéfait ! Qui en connaît une explication rationnelle ?


=


Le code des tests (un peu simplifié) donnent ceci :


Sub test()
Dim t As Long, t2 As Long, duree As Double, temps As Double
Dim a As Long, b As Long, c As Double, r As Double
'Dim a As Long, b As Long, c As Long, r As Long
'Dim a As Double, b As Double, c As Double, r As Double
'Dim a As Long, b As Long, c As Long, r As Double
b = 3
a = 3
c = 2
For t2 = 1 To 10
temps = Timer
For t = 1 To 10000000
r = a * b * c
Next t
duree = duree + Timer - temps
Next t2
MsgBox duree / 10
End Sub


=


QUELQUES résultats après classement :




r,

a,

B,

c,

temps,
----

double,

double,

Long,

long,

2,9,
----

double,

long,

Double,

double,

3,09,
----

long,

long,

Long,

long,

3,56,
----

double,

double,

Double,

double,

3,64,
----

double,

long,

Long,

long,

3,84,
----

long,

double,

Double,

double,

4,4


ET comme on peut le constater, les deux plus rapides sont un mélange de type ! ! Curieux, non ? !


J’ai aussi un peu regardé avec les 3 autres opérations élémentaires, et idem… JE ne sais pas si dans VB après compilation, cela donne les mêmes résultats, puisque ce qui m’intéresse c’est l’exécution sous Excel, donc en VBA (mode interprété, du fait)…


Evidemment comprendre comment cela se fait, pourra aidé à trouver le meilleur type pour les variables, en vue d’une optimisation fine…


Amicalement,
Us.


PS : On peut utiliser autre chose que TIMER mais cela ne change absolument rien en moyenne aux résultats… (Merci de ne pas rentrer dans ce débat totalement stérile et sans fondement...)

5 réponses

kalobit Messages postés 169 Date d'inscription mardi 15 juillet 2003 Statut Membre Dernière intervention 7 avril 2008 2
30 janv. 2006 à 11:51
Bha, c'est peut être que tout le monde colle.

Je vais essayer d'avancer une explication. Je précise bien que je pars sur des supposition. Toutefois, si quelqu'un arrive à prouver ce que j'avance, ça me ferait plaisir.

Tout d'abord :
Voici mes résultats triés en ordre croissant de rapidité


<COLGROUP>
<COL span=5 width=80>

----

R,
a,
b,
c,
Temps,
----

Long,
Long,
Long,
Long,
3,92434375,
----

Double,
Double,
Double,
Double,
4,46434375,
----

Double,
Long,
Double,
Double,
4,82946875,
----

Double,
Long,
Long,
Double,
5,458625,
----

Double,
Long,
Long,
Long,
5,972125,
----

Long,
Double,
Double,
Double,
6,3810625,
----

Long,
Long,
Double,
Double,
6,59,
----

Long,
Long,
Long,
Double,
7,807

Voici ce que j'en tire

Principe 1 :
Le long est plus rapide que le double. Forcément, 4 octets contre 8, ça ne se discute pas

Principe 2 :
Le traitement avec un seul type de variable est plus rapide que le même traitement avec plusieurs type de variable

Principe 3 :
Le résultat d'un traitement de plusieurs type de variable est plus rapide si on lui affecte le type de la variable la plus grande (en espace mémoire)
Il faut donc comprendre qu'un résultat Double de variables double et long est plus rapide qu'un résultat long de variables double et long

Voilà pour les principes qui pour moi semblent clairs. Je vais maintenant pousser le raisonnement un peu plus loin.

La conversion temporaire.

Pour effectuer le calcul, il faut que les variables soient du même type. On va donc convertir les variables dans le type le plus grand (pour ce qui nous interrese, ce sera toujours de long - 4octets - à Double - 8 octets)
Enfin pour donner le résultat, on va reconvertir toutes les variables dans le type du résultat.

Ce qui donne, dans le même ordre que mes résultats


<COLGROUP>
<COL span=15 width=25>
<COL width=59>

----

R,
a,
b,
c,
,
">->,
a,
b,
c,
,
">->,
a,
b,
c,
,
Nb Conv,
----

L,
L,
L,
L,
,
,
L,
L,
L,
,
,
L,
L,
L,
,
0,
----

D,
D,
D,
D,
,
,
D,
D,
D,
,
,
D,
D,
D,
,
0,
----

D,
L,
D,
D,
,
,
D,
D,
D,
,
,
D,
D,
D,
,
1,
----

D,
L,
L,
D,
,
,
D,
D,
D,
,
,
D,
D,
D,
,
2,
----

D,
L,
L,
L,
,
,
L,
L,
L,
,
,
D,
D,
D,
,
3,
----

L,
D,
D,
D,
,
,
D,
D,
D,
,
,
L,
L,
L,
,
3,
----

L,
L,
D,
D,
,
,
D,
D,
D,
,
,
L,
L,
L,
,
4,
----

L,
L,
L,
D,
,
,
D,
D,
D,
,
,
L,
L,
L,
,
5

J'ai noté en rouge les conversions de type.
Voici les explications pour les lignes 5 à 8
Ligne 5 :
Le type dominant du calcul est L, pas de conversion
Le type du résultat est D on converti 3 L en D
Total 3 Conversions.

Ligne 6
Le type dominant du calcul est D, pas de conversion ( déjà 3D)
Le type du résultat est L, on converti 3D en L
Total 3 conversion

Ici on note sur les lignes 5 et 6 le même nombre de conversion alors que 5 est plus rapide que 6.
L'explication que je donne est que la conversion L -> D est plus rapide. Et pour cause, on passe de 4 à 8 octets, ce qui semble plus simple que l'inverse.

Ligne 7 :
Le type dominant du calcul est D, on converti 1 L en D
Le type du résultat est L, on converti 3 D en L
Total 4 conversions

Ligne 8 :
Le type dominant du calcl est D, on converti 2L en D
Le type du résultat est L, on converti 3L en D
Total 5 conversions.

En comparant donc la rapidité du code avec d'une part les 3 principes énoncés et d'autre part mon théorème sur les conversions, j'en arrive à la conclusion suivante.

Pour optimiser la rapidité de son code, on favorisera dans ses calculs un seul type de données, si possible celui monopolisant le moins de mémoire possible. Si ce n'est pas possible, on attribuera à la variable résultat le type de donnée le plus fort par mis les variables du calcul.
Par exemple, avec trois variables de calcul (1 long et 2 double), le résultat en double sera plus rapide à calculer que le résultat en long. Idem si 2 long et 1 double.

J'attends vos réactions avec impatience.

[mailto:K@lobit K@lobit]
3
Gobillot Messages postés 3140 Date d'inscription vendredi 14 mai 2004 Statut Membre Dernière intervention 11 mars 2019 34
30 janv. 2006 à 13:29
se méfier des résultats ...

trop petites différences n'entraîne aucune conclusion valable

suivant que les tests sont faits dans un sens ou dans l'autre, les résultats peuvent être totalement inversés.

en VB6 compilé on non compilé, optimisé ou pas optimisé, les résultats seront encore différents.



la logique veut malgré tout que je suis d'accord avec kalobit, à quelque détails près ..:

R a b c

D L L D --> 1 conversion seulement

temp = a * b calcul en Long

temp = temp * c conversion ?

D L D D --> 1 conversion



qu'est ce qui prouve qu'il y a conversion ou pas ?

le calcul avec des entiers ne se fait-il pas en flottant, en utilisant la FPU qui toujours plus rapide ?


Daniel
3
kalobit Messages postés 169 Date d'inscription mardi 15 juillet 2003 Statut Membre Dernière intervention 7 avril 2008 2
30 janv. 2006 à 13:53
j'oubliais un petit détail,

J'ai utilisé la notion de "variable dominante" qui porte à confusion. Il fallait bien comprendre la variable qui utilise le plus de mémoire. Donc on converti le long en double et pas le double en long

Mais bon c'est clair que ce ne sont pas là des vérités verrifiées et approuvées.

Je tiens juste à faire une remarque très importante. où est l'interêt de place le résultat d'un produit d'un long et d'un double dans un long.

la logique veut que ce soit forcément un double car sinon on risque un dépassement de capacité.

Donc la règle découlant de tout ça c'est :

Toujours attribuer au résultat le type de la variable la plus grande!!!

Ici, on s'est arrêter à des variable de valeur 10*10^6. Mais si on va au delà, le long ne suit pas et se trouve contraint à utiliser des doubles.

Pour finir sur les convertions, je vous invite à tester ces deux routines :

la première converti les long en double et la deuxième fait l'inverse.
Je vous laisse interpréter les résultats. chez moi
0,3789063 L2D
0,6835938 D2L

Sub testerconvL2D()


Dim temps
Dim départ
Dim fin
Dim x As Long
Dim y As Double


départ = Timer


For x = 1 To 10000000
y = CDbl(x)
Next
fin = Timer
temps = fin - départ


Debug.Print temps


End Sub


Sub testerconvD2L()


Dim temps
Dim départ
Dim fin
Dim x As Long
Dim y As Double


départ = Timer


For y = 1 To 10000000
x = CLng(y)
Next
fin = Timer
temps = fin - départ


Debug.Print temps


End Sub

[mailto:K@lobit K@lobit] <- ne pas clicker ici !!!
3
us_30 Messages postés 2065 Date d'inscription lundi 11 avril 2005 Statut Membre Dernière intervention 14 mars 2016 10
29 janv. 2006 à 12:29
Je vois un peu tristement que ma question n'intéresse pas grand monde... si vous avez une réaction, n'hésiter pas pour autant...

Us.
0

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

Posez votre question
us_30 Messages postés 2065 Date d'inscription lundi 11 avril 2005 Statut Membre Dernière intervention 14 mars 2016 10
31 janv. 2006 à 01:05
Bonsoir à tous,

Je vous remercie tous les deux de vous pencher sur ma colle , et plus particulièrement Kalobit d'avoir pris autant de temps pour faire des tests...


JE suis d'accord avec Kalobit sur ses explications qu'il expose la première fois... c'est du moins, ce que je pensais avant... mais voilà mon problème c'est qu'en refaissant encore mes tests exposés, j'obtiens bien des meilleurs résultats avec : r en double, a en double, b en long et c en long ! Meilleur que si toutes les variables étaient en Long par exemple.

Donc, là je suis en contradiction manifeste avec les tests de Kalobit. En effet, ses tests donnent un classement qui sont bien ordonnées avec ce qu'on peut penser sur la manière dont sont traités les calculs... Donc, là je ne comprends pas déjà comment se fait-il qu'on ait pas (peu ou prou) le même classement. Je ne comprends pas cette différence d'autant que j'ai pris soin de faire de nombreux tests avec deux PC, deux versions d'Excel sur deux OS différents (WinME et XP), ce qui retir quasiment toute possibilité d'un fonctionnement spécifique à une machine... Ce que j'ai exposé devrait être pareil pour tout le monde. Donc, si j'essaye d'analyser pourquoi cette différence, la seule raison simple que je vois, c'est que Kalobit n'aurait peut-être pas fait ses tests à partir de VBA, mais peut-être avec VB6... L'autre soin particulier à mes tests, qui fait écho à la remarque Gobillot de faire attention à de trop petites différences, c'est justement d'augmenter le nombre de tests dans la boucle, pour observer que la différence augmente proportionnellement. En d'autre terme en augmentant de 10 fois le nombre de passage dans la boucle FOR t= ..., j'obtiens effectivement une durée 10 fois supérieure quel que soit le test considéré. Donc les différences extrêmes (par exemple) ne sont plus de 4,4-2,9=1,5 seconde mais bien de 15 secondes; valeur bien significative. Effectivement après compilation, le classement a de fortes chances d'être plus proche de ceux de Kalobit, mais je parle bien de VBA, et il n'y a pas de compilation possible dans ce cas...

Pour revenir au dernier post de Kalobit, j'ai testé les deux procédures. J'ai obtenu
2,085938 L2D
4,007813 D2L


Résultats en tout point comparables, puisqu'on observe que les rapports des deux sont presque les mêmes : 1,9 contre 1,8...
De plus, en retirant les fonctions de convertions CLng et CDbl, donc en "forçant" une convertion de type en interne, cela ne change absolument rien aux résultats... (on aura noté que y et x sont déclarés avec Dim en Double et en Long)... Preuve (ou quasi preuve) que le principe 3 (très clairs, au passage) est justifié.


Pour le principe 1, à savoir que "Le long est plus rapide que le double" est aussi vérifié dans les résultats que j'ai exposé, et semble général. Avec cette précaution en plus, à condition que toute l'expression soit tout en Long ou tout en Double.

Par contre le principe 2... " Le traitement avec un seul type de variable est plus rapide que le même traitement avec plusieurs type de variable. " n'est pas vérifié sous VBA , chez moi.

Bon, avant d'aller vraiment plus loin il est important de voir si mes tests sont confirmés sous VBA.


Car, j'ai poursuivis un peu cette étrange comportement que j'observe et que je me n'explique pas... il est vrai cette fois (et pour le moment) que sur 1 seul PC, mais mon constat pose encore une nouvelle belle colle ! ... et j'suis de nouveau tombé sur le cul, d'ailleurs...


De fils en aiguille, j'ai repris le test proposé, mais cette fois avec 5 variables pour voir comment la vitesse se comporte, en fonction des types... ce qui m'a permis de faire cette conclusion (provisoire) ; qu'il faut que dans l'expression que toutes les variables soient en Double sauf une en Long. ET surtout, l'emplacement de cette variable en Long doit être juste après l'égalité !! J'ai observé que la position avait une importance aussi sur la vitesse, et en rien négligeable !! J'avoue perdre mon latin dans l'histoire... j'ai juste qlq spéculations, mais le plus important c'est que les résultats soient confirmés par d'autres...


Voici le test où j'ai augmenté le nb de passage par 10 pour avoir une différence significative sans discussion possible :

=

Sub test()
Dim t As Long, duree As Double, temps As Double
Dim r As Double
'Dim a As Double, b As Double, c As Double, d As Double, e As Double '44,8699951171875
Dim a As Long, b As Double, c As Double, d As Double, e As Double '37,949912109375
'Dim a As Double, b As Long, c As Double, d As Double, e As Double '46,1300585937502
'Dim a As Double, b As Double, c As Long, d As Double, e As Double '47,4100976562499
'Dim a As Double, b As Double, c As Double, d As Long, e As Double '50,259970703125
'Dim a As Double, b As Double, c As Double, d As Double, e As Long '47,9499023437502
b = 2
a = 3
c = 5
d = 7
e = 11
temps = Timer
For t = 1 To 100000000
r = a * b * c * d * e
Next t
duree = duree + Timer - temps
Debug.Print duree
End Sub

=

J'ai mis les durées en seconde en Remarque dans le code. Chaque test à été effectué 3 fois de suite, et un peu au hasard en complément...

Si au lieu de déclarer en Long une variable précise avec Dim, on déplace la variable en Long dans l'expression de "r", on obtiens des résultats comparables... donc c'est bien la position dans l'expression qui est importante... la plus mauvaise place étant l'avant-dernière...

Voilà, encore un peu d'eau au moulin, mais j'arrive de moins en moins à comprendre la logique sous-jacente lorsque VBA exécute un code

Mystère... Mystère...

A+
Amicalement,
Us.
0
Rejoignez-nous