Défi: optimisation d'une fonction de convertion (Bin => Dec)

Défi: optimisation d'une fonction de convertion (Bin => Dec) - VB/VBA/VBS - Programmation

Marsh Posté le 04-01-2005 à 14:38:09    

Salut!
 
J'ai besoin dans un programme d'avoir une fonction qui me converti en nombre entier une chaine de caractères représentative d'un nombre binaire strictement positif compris entre 0 et 511 ex: "100110100" (codage sur 9 bits).
 
Cette fonction doit être appelée environ 9 000 000 de fois à la suite. Il me faut donc qu'elle soit le plus rapide possible. Je poste la fonction que je me suis programmé mais que je voudrais optimiser.  
Si vous avez des idée pour augmenter la rapidité du code je vous serai reconnaissant.
Merci!  
 
Loïc
 
 
 
<code>
Private Function BinToNum(sBinary As String) As Long
   Dim nValue As Long
   Dim nCpt As Long
   Dim nPow As Byte
   
   nValue = 0
   nCpt = 1
   While nCpt <= Len(sBinary)
       If Mid$(sBinary, nCpt, 1) = "1" Then
          nPow = Len(sBinary) - nCpt
          nValue = nValue + (2 ^ nPow)
       End If
       nCpt = nCpt + 1
   Wend
   
   BinToNum = nValue
End Function
 
 
</code>


Message édité par LCPROG le 04-01-2005 à 15:06:42
Reply

Marsh Posté le 04-01-2005 à 14:38:09   

Reply

Marsh Posté le 04-01-2005 à 14:49:48    

Déjà remplace Mid par Mid$

Reply

Marsh Posté le 04-01-2005 à 15:06:15    

effectivement! je gagne déjà 15% de temps
 
Merci bien.

Reply

Marsh Posté le 04-01-2005 à 15:08:26    

Ensuite on peut voir que tu fait appel plusieurs fois à :

Code :
  1. Len(sBinary)


Grosse perte de temps de recalculer la longueur de sBinary, surtout si elle change pas, donc met au début :

Code :
  1. Dim BinaryLen as Integer = Len(sBinary)


Et après remplace tout tes Len(sBinary) par BinaryLen

Reply

Marsh Posté le 04-01-2005 à 15:40:15    

Ok! J'ai étudié ça. Voici ma nouvelle fonction. je n'utilise plus du tout de len() car ma chaine d'entrée est toujours sur 9 caractères.
Si tu vois comment optimiser cette nouvelle méthode..
Je n'utilise que des longs pour les calculs, j'ai lu dans un tutorial sur l'optimisation que c'était préférable.
je gagne désormais a peu près 25 % de temps d'exécution!
 
 
Private Function Bin9BitsToNum(sBinary As String) As Long
   Dim nValue As Long
   Dim nCpt As Long
   Dim nPow As Long
   Dim nGet2Pow As Long
   
   nValue = 0
   nCpt = 9
   While nCpt > 0
       If Mid$(sBinary, nCpt, 1) = "1" Then
          nPow = 9 - nCpt
          nGet2Pow = 2 ^ nPow
          nValue = nValue + nGet2Pow
       End If
       nCpt = nCpt - 1
   Wend
   
   BinToNum = nValue
End Function


Message édité par LCPROG le 04-01-2005 à 15:40:43
Reply

Marsh Posté le 04-01-2005 à 15:46:31    

Tiens pour la comparaison avec "1" :

Code :
  1. If AscW(Mid$(sBinary, nCpt)) = 31 Then
  2. ...


Sachant que 31 est le code ASCII de "1"

Reply

Marsh Posté le 04-01-2005 à 15:49:39    

Aussi, tu pourrais précalculer les puissances, vu qu'elles sont connues et limitées. Met un tableau constant dans ton programme qui contient les puissances déjà calculées, afin que tu n'ai que ça à faire :

Code :
  1. nValue = PowerArray(nCpt)

Reply

Marsh Posté le 04-01-2005 à 15:58:08    

J'ai encore gagné 5 secondes! (on "bench" en était à 50 sec, j'en suis à 45 juste avec cette modif.
Voici le nouveau code. C'est super ton aide :), je pensais pas gagner tout ce temps. Je suis dans mes objectifs maintenant
 
 
Private Function Bin9BitsToNum(sBinary As String) As Long
   Dim nValue As Long
   Dim nCpt As Long
   Dim nPow As Byte
   Dim nGet2Pow As Long
   
   nValue = 0
   nCpt = 9
   nPow = 0
   While nCpt > 0
       If AscW(Mid$(sBinary, nCpt, 1)) = 49 Then
          nGet2Pow = 2 ^ nPow
          nValue = nValue + nGet2Pow
       End If
       nPow = nPow + 1
       nCpt = nCpt - 1
   Wend
   
   Bin9BitsToNum = nValue
End Function

Reply

Marsh Posté le 04-01-2005 à 16:00:25    

Essaye avec le tableau de puissances. Met une variable globale genre PowerArray = Array(1, 2, 4, 8, 16, 32, 64, 128, 256, 512). Comme ça suffit de faire un PowerArray(nCpt), et hop t'as la puissance cash, pas besoin de la recalculer à chaque fois que tu lances la fonction :)

Reply

Marsh Posté le 04-01-2005 à 16:12:48    

Vraiment pas bête!! Ca m'avait même pas effleuré l'esprit les constantes...  J'ai encore gagné 4 secondes, j'en suis à 41 secondes pour le bench. Je ne sais pas si on peut faire plus rapide maintenant.
 
Voici la partie du code de ma classe concernée
 
private nPowerArray(1 to 9) as long
 
Private Function Bin9BitsToNum(sBinary As String) As Long
   Dim nValue As Long
   Dim nCpt As Long
   
   nValue = 0
   nCpt = 9
 
   While nCpt > 0
       If AscW(Mid$(sBinary, nCpt, 1)) = 49 Then
          nValue = nValue + nPowerArray(nCpt)
       End If
       nCpt = nCpt - 1
   Wend
   
   Bin9BitsToNum = nValue
End Function
 
 
Private Sub Class_Initialize()
   nLevel = 1
   nPowerArray(1) = 256
   nPowerArray(2) = 128
   nPowerArray(3) = 64
   nPowerArray(4) = 32
   nPowerArray(5) = 16
   nPowerArray(6) = 8
   nPowerArray(7) = 4
   nPowerArray(8) = 2
   nPowerArray(9) = 1
End Sub
 
 
merci pour tout.
 
Loïc

Reply

Marsh Posté le 04-01-2005 à 16:12:48   

Reply

Marsh Posté le 04-01-2005 à 16:20:03    

Ce qu'il faudrait essayer, c'est de profiler la fonction pour voir quelle est la ligne qui prend le plus de temps :)

Reply

Marsh Posté le 04-01-2005 à 16:25:59    

C'est cette ligne qui bouffe le plus :

Code :
  1. If AscW(Mid$(sBinary, nCpt, 1)) = 49


A voir... :)

Reply

Marsh Posté le 04-01-2005 à 16:26:16    

Pour cette fonction ça va aller je pense :)
Maintenant faut je j'optimise le reste de mon code. Je ne pensais vraiment pas gagner autant de temps avec cette fonction. Tu m'as donné pleins d'idées avec tout ça.
Merci encore.
 

Reply

Marsh Posté le 04-01-2005 à 16:28:55    

Oui c'est ce que je me disais :) Peut être qu'en la décomposant en :
 
dim cChar as char
 
cChar  = Mid$(sBinary, nCpt, 1)  
If AscW(cChar) = 49
 
J'ai lu qu'en décomposant les calculs on pouvait gagner un peu de temps...
 
Aller j'essaie!

Reply

Marsh Posté le 04-01-2005 à 16:31:06    

Et avec ça :)
 

Code :
  1. res = 0
  2. If Mid$(sBin, 1, 1) = "1" Then
  3.    res = res + 256
  4. End If
  5. If Mid$(sBin, 2, 1) = "1" Then
  6.    res = res + 128
  7. End If
  8. If Mid$(sBin, 3, 1) = "1" Then
  9.    res = res + 64
  10. End If
  11. If Mid$(sBin, 4, 1) = "1" Then
  12.    res = res + 32
  13. End If
  14. If Mid$(sBin, 5, 1) = "1" Then
  15.    res = res + 16
  16. End If
  17. If Mid$(sBin, 6, 1) = "1" Then
  18.    res = res + 8
  19. End If
  20. If Mid$(sBin, 7, 1) = "1" Then
  21.    res = res + 4
  22. End If
  23. If Mid$(sBin, 8, 1) = "1" Then
  24.    res = res + 2
  25. End If
  26. If Mid$(sBin, 9, 1) = "1" Then
  27.    res = res + 1
  28. End If


c'est moche mais ça doit être très rapide

Reply

Marsh Posté le 04-01-2005 à 16:32:07    

Ouais, t'as déplié la boucle while :D C'est sûr plus de décrémentation, ni de condition pour le while, mais après bonjour la syntaxe ;) D'ailleurs ce genre d'optimisation est parfois réalisée par le compilateur...


Message édité par FlorentG le 04-01-2005 à 16:32:23
Reply

Marsh Posté le 04-01-2005 à 16:32:50    

j'essaie de suite... c vrai que c'est pas beau mais je m'en tape pour cette fonction...

Reply

Marsh Posté le 04-01-2005 à 16:33:01    

oui mais bon comme on ne sait pas trop comment fonctionne le compilo (ou plutot le pre-compilo) faut tout lui dire...

Reply

Marsh Posté le 04-01-2005 à 16:35:29    

Mais faudrait remplacer genre  

Code :
  1. If Mid$(sBin, 9, 1) = "1" Then
  2.    res = res + 1
  3. End If


Avec

Code :
  1. If AscW(Mid$(sBin, 9, 1)) = 49 Then
  2.   res = res + 1
  3. End If


J'ai pu voir que c'était plus rapide... à vérifier

Reply

Marsh Posté le 04-01-2005 à 16:36:28    

ça passe à 48 secondes avec ça, c'est pas plus rapide

Reply

Marsh Posté le 04-01-2005 à 16:37:08    

J'essaie avec le second code...

Reply

Marsh Posté le 04-01-2005 à 16:42:00    

j'ai des perfs identiques avec
 If AscW(Mid$(sBinary, 9, 1)) = 49 Then  
  nValue = nValue + 1  
End If
...
et la fonction sous sa dernière version. Ca doit se jouer au dixième de seconde près maintenant.

Reply

Marsh Posté le 04-01-2005 à 16:42:29    

une petite question c'est sous quoi (vba , vb.net??) et quelle version du language (ou d'office)

Reply

Marsh Posté le 04-01-2005 à 16:44:13    

Oui aussi, là j'ai tout fait dans le cas de VB normal, pas VB.NET

Reply

Marsh Posté le 04-01-2005 à 16:46:48    

c'est sous vb sp6 :)

Reply

Marsh Posté le 04-01-2005 à 16:53:58    

par contre en essayant sous excel 2000 (vba), avec la boucle dépliée, ça divise le temps d'execution par 2...

Reply

Marsh Posté le 04-01-2005 à 16:54:28    

Oui en VBA c'est directement interpreté, en VB normal y'a quand-même une compilation, donc ça doit jouer...

Reply

Marsh Posté le 04-01-2005 à 17:23:43    

Au lieu de travailler sur une chaîne, il serait peut-être plus judicieux de travailler sur un nombre, en la convertissant (CInt()).
 
Et aussi, utiliser un masque binaire, au lieu d'une égalité.


Message édité par charly007 le 04-01-2005 à 17:32:13
Reply

Marsh Posté le 04-01-2005 à 17:31:07    

Et un petit :

Code :
  1. While nCpt <= Len(sBinary)
  2.        nValue = 2*nValue
  3.        If Mid$(sBinary, nCpt, 1) = "1" Then
  4.           nValue = nValue + 1
  5.        End If
  6.        nCpt = nCpt + 1
  7.    Wend


à la place du while proposé, ça n'irait pas ?

Reply

Marsh Posté le 04-01-2005 à 17:31:55    

Bah je ne suis pas sur qu'en rajoutant une conversion en entier on y gagne car finalement on va faire string vers int(binaire) vers int au lieu de faire directement string vers int

Reply

Marsh Posté le 04-01-2005 à 17:48:18    

dreameddeath a écrit :

Bah je ne suis pas sur qu'en rajoutant une conversion en entier on y gagne car finalement on va faire string vers int(binaire) vers int au lieu de faire directement string vers int


Je propose :

Code :
  1. valeur = CInt(sBinary)
  2. If (valeur And 256) Then


Au lieu de :

Code :
  1. If AscW(Mid$(sBin, 9, 1)) = 49 Then


Message édité par charly007 le 04-01-2005 à 17:48:40
Reply

Marsh Posté le 04-01-2005 à 18:11:35    

Est-ce que le Cint va fonctionner ? J'me demande s'il va pas faire un erreur...

Reply

Marsh Posté le 04-01-2005 à 18:15:12    

FlorentG a écrit :

Est-ce que le Cint va fonctionner ? J'me demande s'il va pas faire un erreur...


J'ai testé.

Reply

Marsh Posté le 04-01-2005 à 18:18:48    

FlorentG a écrit :

Est-ce que le Cint va fonctionner ? J'me demande s'il va pas faire un erreur...


Utilise CLng, tu auras un dépassement de capacité avec CInt.


---------------
"I wonder if the internal negative pressure in self pumping toothpaste tubes is adjusted for different market altitudes." John Carmack
Reply

Marsh Posté le 04-01-2005 à 18:20:20    


 
Ok :jap:
 
Ben si ça marche, c'est sûr qu'il y ait moyen que ce soit plus rapide :D

Reply

Marsh Posté le 04-01-2005 à 18:25:02    

mareek a écrit :

Utilise CLng, tu auras un dépassement de capacité avec CInt.


CInt(expression)
 
Plage de valeurs de l'argument expression :
-2 147 483 648 à 2 147 483 647
 
Je pense que ça ira  ;) A moins qu'il en soit différemment dans sa version de VB.


Message édité par charly007 le 04-01-2005 à 18:27:54
Reply

Marsh Posté le 04-01-2005 à 18:47:38    

charly007 a écrit :

CInt(expression)
 
Plage de valeurs de l'argument expression :
-2 147 483 648 à 2 147 483 647
 
Je pense que ça ira  ;) A moins qu'il en soit différemment dans sa version de VB.


perdu

Citation :

CInt          Integer           -32,768 to 32,767; fractions are rounded.


http://msdn.microsoft.com/library/ [...] ersion.asp


---------------
"I wonder if the internal negative pressure in self pumping toothpaste tubes is adjusted for different market altitudes." John Carmack
Reply

Marsh Posté le 04-01-2005 à 18:50:34    

mareek a écrit :

perdu

Citation :

CInt          Integer           -32,768 to 32,767; fractions are rounded.


http://msdn.microsoft.com/library/ [...] ersion.asp


Citation :


Visual Basic for Applications Reference


Moi ce que j'ai donné c'est du VB.NET.
Et puis, c'est un détail minime. :D


Message édité par charly007 le 04-01-2005 à 18:52:03
Reply

Marsh Posté le 04-01-2005 à 19:02:18    

il a dit que c'était du VB6 ;)


---------------
"I wonder if the internal negative pressure in self pumping toothpaste tubes is adjusted for different market altitudes." John Carmack
Reply

Marsh Posté le 04-01-2005 à 19:03:33    

donc c'est pas du VBA comme le liens que tu donnes dans VB l'integer c'est du 32 bits (soit environ 4 milliard de valeurs possibles)

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed