Optimisation de PureBasic et de L’ASM

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
PAPIPP
Messages : 534
Inscription : sam. 23/févr./2008 17:58

Optimisation de PureBasic et de L’ASM

Message par PAPIPP »

Optimisation de PureBasic et de L’ASM

Bonjour à tous.
Je ne sais si cette étude doit être placée ici. Vous pouvez la déplacer.

Je cherche à améliorer un algorithme de tri stable et rapide, pour permettre des ordres de tri de toute table à 1 dimension de structure complexe sur plusieurs zones sans pour autant détruire le tri relatif précédemment réalisé, exemple tri par insertion.
Avec un module généralisé qui puisse accepter toute table à 1 dimension de structure aussi complexe que vous le désirez.
Comme les algos de tri sont connus depuis longtemps le tri par insertion se trouve être l’un des tris stables les plus rapides.
Il me reste, compte tenu de la complexité de la structure que je ne connais pas puisque l’utilisateur (programmeur) peut utiliser toute structure, de trouver un swap mémoire généralisé .pour les échanges pendant la commande de tri. (Il faut déplacer toute une entité d’indice x de la structure complexe vers un indice y<>x).

J’ai d’abord utilisé PB et ensuite ASM
En PB comme en ASM il existe 3 méthodes de base pour échanger 2 zones de mémoire élémentaire soit mem1 et mem2 2 zones élémentaires de type connu
1) méthode avec une mémoire supplémentaire.
memtemp=mem1
mem1=mem2
mem2=memtemp

2) méthode avec XOR ou ! comme opérateur
mem1 ! mem2 ;; dans mem1 ou exclusif de mem1 et mem2
mem2 ! mem1 ;; dans mem2 on retrouve mem1
mem1 ! mem2 ;; enfin dans mem1 on retrouve mem2

Code : Tout sélectionner

mem1.q=$789ABCDE
mem2.q=$FEDCBA98
Debug Hex(mem1)+" "+Hex(mem2)
mem1 ! mem2
; Debug Hex(mem1)+" "+Hex(mem2)
mem2 ! mem1
; Debug Hex(mem1)+" "+Hex(mem2)
mem1 ! mem2
Debug Hex(mem1)+" "+Hex(mem2)
Debug " c'est OK"
3) méthode swap la plus rapide comme les tests suivants le démontreront.
swap mem1, mem2

Voici le prg de comparaison en PB

Code : Tout sélectionner

 ProcedureDLL.q _nbcs()
; ; ;   L'instruction RDTSC est un mnémonique pour ReaD Time Stamp Counter.
; ; ;   L'instruction retourne dans le couple de registre EDX:EAX le nombre de ticks1 écoulés depuis la dernière remise à zéro du processeur
; ; ;   (Reset).
   !RDTSC
   ProcedureReturn
 EndProcedure

Macro _q_t_
  "
EndMacro
Macro _n(__n)
  _q_t_#__n#=_q_t_+Str(__n)+" "
EndMacro
Macro _s(__S)
  _q_t_#__S#=_q_t_+__S+" "
EndMacro
Macro _NL
  "N°L="+Str(#PB_Compiler_Line)+" ** "
EndMacro

maxi=100000
maxi=maxi & $FFFFFFFE ; ;; force  la variable maxi à être paire
;  Nombre pair pour voir l'échange entre mem1 et mem2 la boucle for démarrant à 0 réalise une itération de plus
;                   alors que la boucle loop xxx réalise le nombre exact d'itération
mem1.l=1234567890
mem2.l=987654321
mes1$=_n(mem1)+_n(mem2)+#LF$

debo.q=_nbcs()
For i=0 To maxi
Next
delta.q=_nbcs()-debo

deb.Q=_nbcs()
For i=0 To MAXI
  Swap mem1, mem2
Next
fin.q=_nbcs()
mes1$+_n(mem1)+_n(mem2)+#LF$
debm.q=_nbcs()
For i=0 To maxi
  memtemp.q=mem1
  mem1=mem2
  mem2=memtemp
Next
finm.q=_nbcs()
mes1$+" av xor "+_n(mem1)+_n(mem2)+#LF$
debx.q=_nbcs()
For i=0 To maxi
  mem1 ! mem2
  mem2 ! mem1
  mem1 ! mem2
Next
finx.q=_nbcs()
mes1$+" ap xor "+_n(mem1)+_n(mem2)+#LF$

mes1$+_n(mem1)+_n(mem2)+#LF$
mes1$+" NB cycles SWAP ="+_n(fin-deb-delta)+#LF$
mes1$+" Nb cycles MOV  ="+_n(finm-debm-delta)+#LF$
mes1$+" Nb cycles XOR  ="+_n(finx-debx-delta)+#LF$

MessageRequester("essaicycle machine",mes1$)
On peut remarquer que l’option swap de PB est la plus rapide

Mise en œuvre de ces méthodes avec une structure de table complexe.
Je présente ci-dessous 3 prg en PB
Swapcopy méthode de la mémoire supplémentaire la plus simple mais aussi la plus lente.
swapPB méthode avec swap en mode récursif
swapPBN méthode avec swap en mode itératif
et dans un premier temps un PRG asm avec les mêmes références que pour PB c'est-à-dire en prenant XCHG reg,[mem] comme méthode d’échange

La méthode XOR n’est pas présentée ici car plus lente que le swap (vous pouvez remplacer dans le PRG swappb Swap x,y par
X ! y
Y !x
X ! y

Regardez le résultat.

Code : Tout sélectionner

Macro _q_t_
  "
EndMacro
Macro _n(__n)
  _q_t_#__n#=_q_t_+Str(__n)+" "
EndMacro
Macro _s(__S)
  _q_t_#__S#=_q_t_+__S+" "
EndMacro
Macro _NL
"N°L=" + Str(#PB_Compiler_Line) + " ** "
EndMacro
Procedure SWAPCOPY(*ADR1,*ADR2,LONG)
  Static *mem
  If flag=0
    ;     *mem=AllocateMemory(Long,#PB_Memory_NoClear)
    *mem=AllocateMemory(long*4)  ; vous pouvez auglmenter cette valeur
    flag=1
  EndIf
  CopyMemory(*ADR1,*Mem,Long)
  CopyMemory(*ADR2,*ADR1,Long)
  CopyMemory(*Mem,*ADR2,Long)
EndProcedure

Structure Brack
  StructureUnion
    Va.a[0]
    Vw.b[0]
    Vi.i[0]
    Vl.l[0]
    Vq.q[0]
    Vs8.s{8}[0]
    vs.s{131072}; vous pouvez augmentez cette valeur
  EndStructureUnion
EndStructure
Procedure SWAPPB(*ADR1.brack,*ADR2.brack,LONG)
  Static flag=0
  Protected Restd.l,QDIV.l
  If flag=0
    CompilerIf #PB_Compiler_Unicode=1:;;;Egal 1 si l'exécutable est compilé en mode unicode, égal 0 sinon.
      CompilerError "Il faut passer en ASCII pas en UNICODE, désolé."
    CompilerEndIf
    flag=1
  EndIf
  restd=long
  Select LONG
    Case 0
      ProcedureReturn
    Case 1,2,3
      Qdiv=LONG
;       RESTD=LONG
      For i=0 To QDIV-1
        Swap *ADR1\Va[i],*adr2\Va[i]
        RESTD-1
      Next
      ProcedureReturn RESTD
    Case 4,5,6,7
;       Qdiv=LONG/4
;       RESTD=LONG
      Swap *ADR1\Vl[0],*ADR2\Vl[0]
      RESTD-4
      If restd>0
        SWAPPB(@*ADR1\Vl[1],@*ADR2\Vl[1],restd)
      Else
        ProcedureReturn restd
      EndIf
    Default
      Qdiv=LONG/8
;       RESTD=LONG
      For i=0 To QDIV-1
        Swap *ADR1\Vq[i],*ADR2\Vq[i]
        RESTD-8
      Next
      If restd>0
        SWAPPB(@*ADR1\Vq[i],@*ADR2\Vq[i],restd)
      Else
        ProcedureReturn restd
      EndIf
  EndSelect
EndProcedure

;
Procedure SWAPPBN(*ADR1.brack,*ADR2.brack,LONG)
  Static flag=0
  Protected Restd.l,QDIV.l
  If flag=0
    CompilerIf #PB_Compiler_Unicode=1:;;;Egal 1 si l'exécutable est compilé en mode unicode, égal 0 sinon.
    CompilerError "Il faut passer en ASCII pas en UNICODE, désolé."
    CompilerEndIf
    flag=1
  EndIf
  restd=LONG
  iq=0
  While restd>0
    Select restd
      Case 0
        ProcedureReturn restd
      Case 1,2,3
        ia=long-restd
        While restd>0
          Swap *ADR1\Va[ia],*ADR2\Va[ia]
          ia+1
          RESTD-1
        Wend
      Case 4,5,6,7
        il=iq*2
        Swap *ADR1\Vl[il],*ADR2\Vl[il]
        restd-4
      Default
        While restd>7
          Swap *ADR1\Vq[iq],*ADR2\Vq[iq]
          RESTD-8
          iq+1
        Wend
    EndSelect
  Wend
  ProcedureReturn restd
EndProcedure
Procedure SwapASMEX(*adr1,*adr2,long);; PRG PAPIPP avec option ! sur toutes les instructions
;    Debug _h(rreax)+_h(rrebx)+_h(rrecx)+_h(rredx)+_h(rresi)+_h(rredi)+_h(rresp)+_h(rrebp)
 Protected REBX,RESI,REDI,RECX
;   EnableASM
  !MOV esi,[p.p_adr1]
  !MOV edi,[p.p_adr2]
  !MOV ecx,[p.v_long]
  !SHR ecx,2
  !JE SwapASMEX_BclFin1
  !SwapASMEX_BclDep1:
  !MOV eax,[esi+ecx*4-4]
  !xchg eax,[edi+ecx*4-4]
  !MOV [esi+ecx*4-4],eax
  !DEC ecx
  !JNZ SwapASMEX_BclDep1
;   LOOP SwapASMEX_BclDep1  ; cette instruction ralentit un peu le prg
  !SwapASMEX_BclFin1:
  
  ; swap du reste de la division long/4
  !MOV ecx,[p.v_long]
  !ADD esi,ecx 
  !ADD edi,ecx
  !AND ecx,3
  !JE SwapASMEX_BclFin2

  !SUB esi,ecx 
  !SUB edi,ecx
  
  !SwapASMEX_BclDep2:
  !MOV al,[esi+ecx-1]
  ; !MOV dl,[edi+ecx-1]
;   !MOV [esi+ecx-1],dl
;   !MOV [edi+ecx-1],al

  !XCHG al,[edi+ecx-1];;;;;; Allonge le temps de 3 cycles machine pour chaque appel par rapport aux 4 instructions précédentes
  !MOV[esi+ecx-1],al  ;;;;;; 3instructions en commentaire
  
  !DEC ecx
  !JNZ SwapASMEX_BclDep2
; 
;   LOOP SwapASMEX_BclDep2; cette instruction ralentit un peu le prg
;   
  !SwapASMEX_BclFin2:
  
;   DisableASM
EndProcedure
Procedure SwapASMXO(*adr1,*adr2,long);; PRG PAPIPP avec option ! sur toutes les instructions
;    Debug _h(rreax)+_h(rrebx)+_h(rrecx)+_h(rredx)+_h(rresi)+_h(rredi)+_h(rresp)+_h(rrebp)
 Protected REBX,RESI,REDI,RECX
;   EnableASM
  !MOV esi,[p.p_adr1]
  !MOV edi,[p.p_adr2]
  !MOV ecx,[p.v_long]
  !SHR ecx,2
  !JE SwapASMXO_BclFin1
  !SwapASMXO_BclDep1:
  !MOV eax,[esi+ecx*4-4]
  !XOR eax,[edi+ecx*4-4]
  !XOR [esi+ecx*4-4],eax 
  !XOR [edi+ecx*4-4],eax
  !DEC ecx
  !JNZ SwapASMXO_BclDep1
;   LOOP SwapASMXO_BclDep1  ; cette instruction ralentit un peu le prg elle remplace DEC ... JNZ ...
  !SwapASMXO_BclFin1:
  
  ; swap du reste de la division long/4
  !MOV ecx,[p.v_long]
  !ADD esi,ecx 
  !ADD edi,ecx
  !AND ecx,3
  !JE SwapASMXO_BclFin2

  !SUB esi,ecx 
  !SUB edi,ecx
  
  !SwapASMXO_BclDep2:
  !MOV al,[esi+ecx-1]
  !MOV dl,[edi+ecx-1]
  !MOV [esi+ecx-1],dl
  !MOV [edi+ecx-1],al
  !DEC ecx
  !JNZ SwapASMXO_BclDep2
; 
;   LOOP SwapASMXO_BclDep2  ;;cette instruction ralentit un peu le prg elle remplace DEC ... JNZ ...
;   
  !SwapASMXO_BclFin2:
  
;   DisableASM
EndProcedure

Procedure SwapASM(*adr1,*adr2,long);; PRG PAPIPP avec option ! sur toutes les instructions
  ;    Debug _h(rreax)+_h(rrebx)+_h(rrecx)+_h(rredx)+_h(rresi)+_h(rredi)+_h(rresp)+_h(rrebp)
  Protected REBX,RESI,REDI,RECX
;   EnableASM
  !MOV esi,[p.p_adr1]
  !MOV edi,[p.p_adr2]
  !MOV ecx,[p.v_long]
  !mov ebx,ecx
  !SHR ecx,2
  !JE SwapASM_BclFin1
  
  !SwapASM_BclDep1:
  !MOV eax,[esi+ecx*4-4]
  !MOV edx,[edi+ecx*4-4]
  !MOV[esi+ecx*4-4],edx
  !MOV[edi+ecx*4-4],eax
  !DEC ecx
  !JNZ SwapASM_BclDep1
  !SwapASM_BclFin1:
  
  !MOV ecx,ebx
  !AND ecx,3  ;;;; swap du reste de la division long/4
  !JE SwapASM_BclFin2
  !ADD esi,ebx
  !ADD edi,ebx
  !SUB esi,ecx
  !SUB edi,ecx
  
  !SwapASM_BclDep2:
  !MOV al,[esi+ecx-1]
  !MOV dl,[edi+ecx-1]
  !MOV[esi+ecx-1],dl
  !MOV[edi+ecx-1],al
  !DEC ecx
  !JNZ SwapASM_BclDep2
  
  ;   LOOP SwapASM_BclDep2
  ;
  !SwapASM_BclFin2:
  
;   DisableASM
EndProcedure


Procedure SwapASMo(*adr1,*adr2,long);; PRG PAPIPP avec option ! sur toutes les instructions
  !MOV esi,[p.p_adr1]
  !MOV edi,[p.p_adr2]
  !MOV ecx,[p.v_long]
  !MOV ebx,ecx
  !SHR ecx,2
  !JE SwapASMo_BclFin1
  
  !SwapASMo_BclDep1:
  
  !mov eax,[esi]
  !mov edx,[edi]
  !mov [esi],edx
  !mov [edi],eax
  
  ;   	!XCHG eax,[edi]   ;;;;;; Allonge le temps de 3 cycles machine pour chaque appel par rapport aux 4 instructions précédentes
  ;   	!MOV[esi],eax
  !ADD esi,4
  !ADD edi,4
  ;   !loop SwapASMo_BclDep1  ;;; Allonge le temps par rapport aux deux instructions suivantes
  !DEC ecx
  !JNZ SwapASMo_BclDep1
  !SwapASMo_BclFin1:
  
  ;   !MOV ecx,ebx
  ;   !AND ecx,3
  !and ebx,3
  !JE SwapASMo_BclFin2
  !mov ecx,ebx
  !SwapASMo_BclDep2:
  
  !mov al,[esi]
  !mov dl,[edi]
  !mov [esi],dl
  !mov [edi],al
  
  ; 	!XCHG al,[edi];;;;;; Allonge le temps de 3 cycles machine pour chaque appel par rapport aux 4 instructions précédentes
  ; 	!MOV[esi],al
  !INC esi
  !INC edi
  ;   !loop SwapASMo_BclDep2;;; Allonge le temps par rapport aux deux instructions suivantes
  
  !DEC ecx
  !JNZ SwapASMo_BclDep2
  !SwapASMo_BclFin2:
EndProcedure

A1$="DuPont Jean Jacques Durand Jean Pierre Michel Martin______------_____-"
A2$="1234567890123456789012345678901234567890123456789012345678901234567890"
Long=Len(A1$)
*adr1=@A1$
*adr2=@A2$
MAXI=1000000

For longst=Len(A1$) To 8 Step -7
  A1$=Left(A1$,longst)
    A2$=Left(A2$,longst)
    Long=Len(A1$)
  ;;;; ou ces 2 instructions
;     long=longst
;     *adr1+1
  ;
  ;
;;;; ****** Décommentez les lignes suivantes pour vérifier le bon fonctionnement des différents prg de swap  
;   Debug "SWAPCOPY(*ADR1,*ADR2,LONG)"+_n(long)+_n(long%8);+#LF$
;   Debug _nl+_s(A2$)+"///"+_s(A1$)+_n(*adr1)+_n(*adr2)+_n(long)+_n(Len(A1$));+#LF$
;   SWAPCOPY(*ADR1,*ADR2,LONG)
;   Debug _NL+_s(A2$)+"///"+_s(A1$);+#LF$
;   SWAPCOPY(*ADR1,*ADR2,LONG)
;   
;     Debug "SWAPPB(*ADR1,*ADR2,LONG)"+_n(long)+_n(long%8);+#LF$
;   Debug _nl+_s(A2$)+"///"+_s(A1$)+_n(*adr1)+_n(*adr2)+_n(long)+_n(Len(A1$));+#LF$
;   SWAPPB(*ADR1,*ADR2,LONG)
;   Debug _NL+_s(A2$)+"///"+_s(A1$);+#LF$
;   SWAPPB(*ADR1,*ADR2,LONG)
;    
;   Debug "SWAPPBN(*ADR1,*ADR2,LONG)"+_n(long)+_n(long%8);+#LF$
;   Debug _nl+_s(A2$)+"///"+_s(A1$)+_n(*adr1)+_n(*adr2)+_n(long)+_n(Len(A1$));+#LF$
;   SWAPPBN(*ADR1,*ADR2,LONG)
;   Debug _NL+_s(A2$)+"///"+_s(A1$);+#LF$
;   SWAPPBN(*ADR1,*ADR2,LONG)
;  
;     Debug "SWAPASMEX(*ADR1,*ADR2,LONG)"+_n(long)+_n(long%8);+#LF$
;   Debug _nl+_s(A2$)+"///"+_s(A1$)+_n(*adr1)+_n(*adr2)+_n(long)+_n(Len(A1$));+#LF$
;   SWAPASMEX(*ADR1,*ADR2,LONG)
;   Debug _NL+_s(A2$)+"///"+_s(A1$);+#LF$
;   SWAPASMEX(*ADR1,*ADR2,LONG)
; 
;    Debug "SWAPASMXO(*ADR1,*ADR2,LONG)"+_n(long)+_n(long%8);+#LF$
;   Debug _nl+_s(A2$)+"///"+_s(A1$)+_n(*adr1)+_n(*adr2)+_n(long)+_n(Len(A1$));+#LF$
;   SWAPASMXO(*ADR1,*ADR2,LONG)
;   Debug _NL+_s(A2$)+"///"+_s(A1$);+#LF$
;   SWAPASMXO(*ADR1,*ADR2,LONG)
; 
;     Debug "SWAPASM(*ADR1,*ADR2,LONG)"+_n(long)+_n(long%8);+#LF$
;   Debug _nl+_s(A2$)+"///"+_s(A1$)+_n(*adr1)+_n(*adr2)+_n(long)+_n(Len(A1$));+#LF$
;   SWAPASM(*ADR1,*ADR2,LONG)
;   Debug _NL+_s(A2$)+"///"+_s(A1$);+#LF$
;   SWAPASM(*ADR1,*ADR2,LONG)
; 
;       Debug "SWAPASMO(*ADR1,*ADR2,LONG)"+_n(long)+_n(long%8);+#LF$
;   Debug _nl+_s(A2$)+"///"+_s(A1$)+_n(*adr1)+_n(*adr2)+_n(long)+_n(Len(A1$));+#LF$
;   SWAPASMO(*ADR1,*ADR2,LONG)
;   Debug _NL+_s(A2$)+"///"+_s(A1$);+#LF$
;   SWAPASMO(*ADR1,*ADR2,LONG)
; 
;   
;   Debug "*****************************************************************************************************************"
  :  
  ;;;; ***** Vous pouvez commentez les lignes suivantes en conservant la dernière ligne NEXT pour le contrôle des lignes précédentes

  debut$=""
  debut$+#LF$+#LF$+"***** SWAPCOPY(*ADR1,*ADR2,LONG)"+_n(long)+_n(Len(A1$))+_n(long%8)+#LF$
debut$+_s(A1$)+"///"+_s(A2$)+_n(*adr1)+_n(*adr2)+#LF$
time_d.q=ElapsedMilliseconds()
For i=0 To maxi
  SWAPCOPY(*ADR1,*ADR2,LONG)
Next 
debut$+_s(A1$)+"///"+_s(A2$)+#LF$+" temps en ms="+Str(ElapsedMilliseconds()-time_d)+" pour "+Str(i)+" itérations"+" longueur="+Str(long)
SWAPCOPY(*ADR1,*ADR2,LONG)
 
  
  debut$+#LF$+#LF$+"***** SWAPPB(*ADR1,*ADR2,LONG)"+_n(long)+_n(Len(A1$))+_n(long%8)+#LF$
debut$+_s(A1$)+"///"+_s(A2$)+_n(*adr1)+_n(*adr2)+#LF$
time_d.q=ElapsedMilliseconds()
For i=0 To maxi
  SWAPPB(*ADR1,*ADR2,LONG)
Next 
debut$+_s(A1$)+"///"+_s(A2$)+#LF$+" temps en ms="+Str(ElapsedMilliseconds()-time_d)+" pour "+Str(i)+" itérations"+" longueur="+Str(long)
SWAPPB(*ADR1,*ADR2,LONG)


debut$+#LF$+#LF$+"***** SWAPPBN(*ADR1,*ADR2,LONG)"+_n(long)+_n(Len(A1$))+_n(long%8)+#LF$
debut$+_s(A1$)+"///"+_s(A2$)+_n(*adr1)+_n(*adr2)+#LF$
time_d.q=ElapsedMilliseconds()
For i=0 To maxi
  SWAPPBN(*ADR1,*ADR2,LONG)
Next 
  debut$+_s(A1$)+"///"+_s(A2$)+#LF$+" temps en ms="+Str(ElapsedMilliseconds()-time_d)+" pour "+Str(i)+" itérations"+" longueur="+Str(long)
SWAPPBN(*ADR1,*ADR2,LONG)

debut$+#LF$+#LF$+"***** SWAPASMEX(*ADR1,*ADR2,LONG)"+_n(long)+_n(Len(A1$))+_n(long%8)+#LF$
debut$+_s(A1$)+"///"+_s(A2$)+_n(*adr1)+_n(*adr2)+#LF$
time_d.q=ElapsedMilliseconds()
For i=0 To maxi
  SWAPASMEX(*ADR1,*ADR2,LONG)
Next 
  debut$+_s(A1$)+"///"+_s(A2$)+#LF$+" temps en ms="+Str(ElapsedMilliseconds()-time_d)+" pour "+Str(i)+" itérations"+" longueur="+Str(long)
  SWAPASMEX(*ADR1,*ADR2,LONG)
  
;;;;;************************************************************************************
;;;  **** Commentez la ligne suivante pour comparer tous les prg ASM ********************
;;;;;************************************************************************************

Goto fin_premier_essai

debut$+#LF$+#LF$+"***** SWAPASMXO(*ADR1,*ADR2,LONG)"+_n(long)+_n(Len(A1$))+_n(long%8)+#LF$
debut$+_s(A1$)+"///"+_s(A2$)+_n(*adr1)+_n(*adr2)+#LF$
time_d.q=ElapsedMilliseconds()
For i=0 To maxi
  SWAPASMXO(*ADR1,*ADR2,LONG)
Next 
  debut$+_s(A1$)+"///"+_s(A2$)+#LF$+" temps en ms="+Str(ElapsedMilliseconds()-time_d)+" pour "+Str(i)+" itérations"+" longueur="+Str(long)
SWAPASMXO(*ADR1,*ADR2,LONG)

debut$+#LF$+#LF$+"***** SWAPASM(*ADR1,*ADR2,LONG)"+_n(long)+_n(Len(A1$))+_n(long%8)+#LF$
debut$+_s(A1$)+"///"+_s(A2$)+_n(*adr1)+_n(*adr2)+#LF$
time_d.q=ElapsedMilliseconds()
For i=0 To maxi
  SWAPASM(*ADR1,*ADR2,LONG)
Next 
  debut$+_s(A1$)+"///"+_s(A2$)+#LF$+" temps en ms="+Str(ElapsedMilliseconds()-time_d)+" pour "+Str(i)+" itérations"+" longueur="+Str(long)
SWAPASM(*ADR1,*ADR2,LONG)
  
  debut$+#LF$+#LF$+"***** SWAPASMO(*ADR1,*ADR2,LONG)"+_n(long)+_n(Len(A1$))+_n(long%8)+#LF$
debut$+_s(A1$)+"///"+_s(A2$)+_n(*adr1)+_n(*adr2)+#LF$
time_d.q=ElapsedMilliseconds()
For i=0 To maxi
  SWAPASMO(*ADR1,*ADR2,LONG)
Next 
  debut$+_s(A1$)+"///"+_s(A2$)+#LF$+" temps en ms="+Str(ElapsedMilliseconds()-time_d)+" pour "+Str(i)+" itérations"+" longueur="+Str(long)
SWAPASMO(*ADR1,*ADR2,LONG)

fin_premier_essai:  
MessageRequester("Essai 4 ou 7 programmes en SWAP",debut$)
; 
Next

Surprenant le prg ASM est un peu plus rapide que Swapcopy mais 3 à 4 fois plus lent que swapPB ou swapPBN

J’ai donc réalisé un autre test sur les commandes ASM

Code : Tout sélectionner

 ProcedureDLL.q _nbcs()
; ;   L'instruction RDTSC est un mnémonique pour ReaD Time Stamp Counter.
; ;   L'instruction retourne dans le couple de registre EDX:EAX le nombre de ticks1 écoulés depuis la dernière remise à zéro du processeur
; ;   (Reset).
   !RDTSC
   ProcedureReturn
 EndProcedure
Macro _q_t_
  "
EndMacro
Macro _n(__n)
  _q_t_#__n#=_q_t_+Str(__n)+" "
EndMacro
Macro _s(__S)
  _q_t_#__S#=_q_t_+__S+" "
EndMacro
Macro _NL
  "N°L="+Str(#PB_Compiler_Line)+" ** "
EndMacro



maxi=1000
maxi=maxi | $00000001
;  Nombre impair pour voir l'échange entre mem1 et mem2 la boucle loop xxx réalise le nombre exact d'itération
;                   alors que for démarrant à 0 réalise une itération de plus
mem1.l=1234567890
mem2.l=987654321
mes1$=_n(mem1)+_n(mem2)+#LF$
debo.q=_nbcs()
EnableASM
MOV ecx,[v_maxi]
!ldebo:
LOOP ldebo
; DisableASM
delta.q=_nbcs()-debo

deb.Q=_nbcs()
; EnableASM
MOV ecx,[v_maxi]
!ldebxch:
MOV ebx,[v_mem1]
XCHG ebx,[v_mem2]
MOV[v_mem1],ebx
LOOP ldebxch
; DisableASM
fin.q=_nbcs()
mes1$+_n(mem1)+_n(mem2)+#LF$

debm.q=_nbcs()
; EnableASM
MOV ecx,[v_maxi]
!ldebmov:
MOV ebx,[v_mem1]
MOV edi,[v_mem2]
MOV[v_mem1],edi
MOV[v_mem2],ebx
LOOP ldebmov
; DisableASM
finm.q=_nbcs()
mes1$+" av xor "+_n(mem1)+_n(mem2)+#LF$

debx.q=_nbcs()
;   EnableASM
MOV ecx,[v_maxi]
!ldebxor:
MOV ebx,[v_mem1]
XOR ebx,[v_mem2]
XOR[v_mem1],ebx
XOR[v_mem2],ebx
LOOP ldebxor
DisableASM
finx.q=_nbcs()
mes1$+" ap xor "+_n(mem1)+_n(mem2)+#LF$

mes1$+_n(mem1)+_n(mem2)+#LF$
mes1$+" NB cycles XCHG    = "+_n(fin-deb-delta)+#LF$
mes1$+" Nb cycles MOV  ="+_n(finm-debm-delta)+#LF$
mes1$+" Nb cycles XOR   ="+_n(finx-debx-delta)+#LF$

MessageRequester("essaicycle machine",mes1$)
Et ici au surprise c’est l’option mov qui est la plus rapide

J’ai modifier le PRG ASM avec l’option MOV et l’option XOR
Voici donc 3 autres prg ASm optimisés que vous pouvez tester en commentant la ligne goto xxxx

A+
Il est fort peu probable que les mêmes causes ne produisent pas les mêmes effets.(Einstein)
Et en logique positive cela donne.
Il est très fortement probable que les mêmes causes produisent les mêmes effets.
PAPIPP
Messages : 534
Inscription : sam. 23/févr./2008 17:58

Re: Optimisation de PureBasic et de L’ASM

Message par PAPIPP »

Bonjour Dobro
et merci pour la remarque.
C'est bien vrai pour PureBasic
mais ce type d'opération n'est pas pris en compte pour un échange à cause des risques de débordements dans l'addition
cela fonctionne parfaitement avec PB
Toutefois cela m'a permis de remarquer une bizarrerie dans l'affectation d'une variable.
voir ci dessous.

Code : Tout sélectionner

mem1.L=$7FFFFFFF
mem2.L=$7FFFFFFE
Debug Hex(mem1)+" "+Hex(mem2)
mem1 ! mem2
; Debug Hex(mem1)+" "+Hex(mem2)
mem2 ! mem1
; Debug Hex(mem1)+" "+Hex(mem2)
mem1 ! mem2
Debug Hex(mem1)+" "+Hex(mem2)
Debug " c'est OK"

mem1q.q=$7FFFFFFFFFFFFFFF
mem2q.q=$7FFFFFFFFFFFFFFE
Debug Hex(mem1)+" "+Hex(mem2)
mem1q ! mem2q
; Debug Hex(mem1)+" "+Hex(mem2)
mem2q ! mem1q
; Debug Hex(mem1)+" "+Hex(mem2)
mem1q ! mem2q
Debug Hex(mem1q)+" "+Hex(mem2q)
Debug " c'est OK"

;; et avec + et - 
mem1Q=mem1q+mem2q 
mem2q=mem1q-mem2q 
mem1q=mem1q-mem2q

Debug Hex(mem1q)+" "+Hex(mem2q)
Debug " c'est OK malgré le débordement une particularité de PB (risque de détecter une erreur de débordement pour d'autre langage plus strict)"


Debug "**************************"

mem3.q=mem1+mem2
Debug Hex(mem1)+" "+Hex(mem2)+"  "+Hex(mem3)+" "+Str(mem1)+" "+Str(mem2) +" "+Str(mem3)
mem1=mem1+mem2
Debug Hex(mem1)+" "+Hex(mem2)+"  "+Hex(mem3)+" "+Str(mem1)+" "+Str(mem2)+" "+Str(mem3)

mem2=mem1-mem2
Debug Hex(mem1)+" "+Hex(mem2)+"  "+Hex(mem3)+" "+Str(mem1)+" "+Str(mem2)+" "+Str(mem3)

mem1=mem1-mem2
Debug Hex(mem1)+" "+Hex(mem2)+"  "+Hex(mem3)+" "+Str(mem1)+" "+Str(mem2)+" "+Str(mem3)


Debug "  c'est surprenant car une addition dépassant le seuil de la capacité est acceptée alors qu'une affectation directe ne l'est pas ???"
mem0.l=mem1+mem2 
memx.l=($7FFFFFFF+$7FFFFFFF)
memq1.q=$7FFFFFFF
memq2.q=$7FFFFFFF
memx=memq1+memQ2
memxq.q=memq1+memQ2
Debug Hex(memq1)+" "+Hex(memq2)+" "+Hex(memxq)+" "+Str(memq1)+" "+Str(memq2)+" "+Str(memxq)+" "+Str(memx)
memq.q=$7FFFFFFF1
mem.l=memq
Debug Str(memq)+" " +Str(mem)
;; si vous de-commentez la ligne suivante vous aurez une erreur de compilation IDE or l'opération semble identique à la précédente  mem.l=memq
; mem.l=$7FFFFFFF1
; mais impossible d'affecter directement la même valeur c'est l'IDE de pureBasic qui contrôle avant compilation !!!!!
		 
Il est fort peu probable que les mêmes causes ne produisent pas les mêmes effets.(Einstein)
Et en logique positive cela donne.
Il est très fortement probable que les mêmes causes produisent les mêmes effets.
Mesa
Messages : 1126
Inscription : mer. 14/sept./2011 16:59

Re: Optimisation de PureBasic et de L’ASM

Message par Mesa »

Chez moi, xchg est toujours plus rapide que les deux autres qui ont la même vitesse.

Le fichiers asm n'a rien de spécial, pb utilise un simple ADC !
;
; PureBasic 5.40 LTS Beta 8 (Windows - x86) generated code
;
; (c) 2015 Fantaisie Software
;
; The header must remain intact for Re-Assembly
;
; :System
; kernel32.lib
; :Import
;
format MS COFF
;
;
extrn _ExitProcess@4
extrn _GetModuleHandleA@4
extrn _HeapCreate@12
extrn _HeapDestroy@4
extrn _memset
public _PB_Instance
public _PB_ExecutableType
public _PB_OpenGLSubsystem
public _PB_MemoryBase
public PB_Instance
public PB_MemoryBase
public _PB_EndFunctions

macro pb_public symbol
{
public _#symbol
public symbol
_#symbol:
symbol:
}

macro pb_align value { rb (value-1) - ($-_PB_DataSection + value-1) mod value }
macro pb_bssalign value { rb (value-1) - ($-_PB_BSSSection + value-1) mod value }

public PureBasicStart
;
section '.code' code readable executable align 4096
;
;
PureBasicStart:
;
PUSH dword I_BSSEnd-I_BSSStart
PUSH dword 0
PUSH dword I_BSSStart
CALL _memset
ADD esp,12
PUSH dword 0
CALL _GetModuleHandleA@4
MOV [_PB_Instance],eax
PUSH dword 0
PUSH dword 4096
PUSH dword 0
CALL _HeapCreate@12
MOV [PB_MemoryBase],eax
; mem1.L=$7FFFFFFF
MOV dword [v_mem1],2147483647
; mem2.L=$7FFFFFFE
MOV dword [v_mem2],2147483646
; Debug Hex(mem1)+" "+Hex(mem2)
; mem1 ! mem2
MOV ebx,dword [v_mem1]
XOR ebx,dword [v_mem2]
MOV dword [v_mem1],ebx
;
; mem2 ! mem1
MOV ebx,dword [v_mem2]
XOR ebx,dword [v_mem1]
MOV dword [v_mem2],ebx
;
; mem1 ! mem2
MOV ebx,dword [v_mem1]
XOR ebx,dword [v_mem2]
MOV dword [v_mem1],ebx
; Debug Hex(mem1)+" "+Hex(mem2)
; Debug " c'est OK"
;
; mem1q.q=$7FFFFFFFFFFFFFFF
MOV dword [v_mem1q],-1
MOV dword [v_mem1q+4],2147483647
; mem2q.q=$7FFFFFFFFFFFFFFE
MOV dword [v_mem2q],-2
MOV dword [v_mem2q+4],2147483647
; Debug Hex(mem1)+" "+Hex(mem2)
; mem1q ! mem2q
PUSH dword [v_mem1q+4]
PUSH dword [v_mem1q]
LEA eax,[v_mem2q]
MOV edx,[eax]
XOR [esp],edx
MOV edx,[eax+4]
XOR [esp+4],edx
POP dword [v_mem1q]
POP dword [v_mem1q+4]
;
; mem2q ! mem1q
PUSH dword [v_mem2q+4]
PUSH dword [v_mem2q]
LEA eax,[v_mem1q]
MOV edx,[eax]
XOR [esp],edx
MOV edx,[eax+4]
XOR [esp+4],edx
POP dword [v_mem2q]
POP dword [v_mem2q+4]
;
; mem1q ! mem2q
PUSH dword [v_mem1q+4]
PUSH dword [v_mem1q]
LEA eax,[v_mem2q]
MOV edx,[eax]
XOR [esp],edx
MOV edx,[eax+4]
XOR [esp+4],edx
POP dword [v_mem1q]
POP dword [v_mem1q+4]
; Debug Hex(mem1q)+" "+Hex(mem2q)
; Debug " c'est OK"
;
;
; mem1Q=mem1q+mem2q
PUSH dword [v_mem1q+4]
PUSH dword [v_mem1q]
LEA eax,[v_mem2q]
MOV edx,[eax]
ADD [esp],edx
MOV edx,[eax+4]
ADC [esp+4],edx
POP dword [v_mem1q]
POP dword [v_mem1q+4]
; mem2q=mem1q-mem2q
PUSH dword [v_mem1q+4]
PUSH dword [v_mem1q]
LEA eax,[v_mem2q]
MOV edx,[eax]
SUB [esp],edx
MOV edx,[eax+4]
SBB [esp+4],edx
POP dword [v_mem2q]
POP dword [v_mem2q+4]
; mem1q=mem1q-mem2q
PUSH dword [v_mem1q+4]
PUSH dword [v_mem1q]
LEA eax,[v_mem2q]
MOV edx,[eax]
SUB [esp],edx
MOV edx,[eax+4]
SBB [esp+4],edx
POP dword [v_mem1q]
POP dword [v_mem1q+4]
;
; Debug Hex(mem1q)+" "+Hex(mem2q)
; Debug " c'est OK malgré le débordement une particularité de PB (risque de détecter une erreur de débordement pour d'autre langage plus strict)"
;
;
; Debug "**************************"
;
; mem3.q=mem1+mem2
MOV eax,dword [v_mem1]
CDQ
PUSH edx
PUSH eax
MOV eax,dword [v_mem2]
CDQ
ADD [esp],eax
ADC [esp+4],edx
POP dword [v_mem3]
POP dword [v_mem3+4]
; Debug Hex(mem1)+" "+Hex(mem2)+" "+Hex(mem3)+" "+Str(mem1)+" "+Str(mem2) +" "+Str(mem3)
; mem1=mem1+mem2
MOV ebx,dword [v_mem1]
ADD ebx,dword [v_mem2]
MOV dword [v_mem1],ebx
; Debug Hex(mem1)+" "+Hex(mem2)+" "+Hex(mem3)+" "+Str(mem1)+" "+Str(mem2)+" "+Str(mem3)
;
; mem2=mem1-mem2
MOV ebx,dword [v_mem1]
SUB ebx,dword [v_mem2]
MOV dword [v_mem2],ebx
; Debug Hex(mem1)+" "+Hex(mem2)+" "+Hex(mem3)+" "+Str(mem1)+" "+Str(mem2)+" "+Str(mem3)
;
; mem1=mem1-mem2
MOV ebx,dword [v_mem1]
SUB ebx,dword [v_mem2]
MOV dword [v_mem1],ebx
; Debug Hex(mem1)+" "+Hex(mem2)+" "+Hex(mem3)+" "+Str(mem1)+" "+Str(mem2)+" "+Str(mem3)
;
;
; Debug " c'est surprenant car une addition dépassant le seuil de la capacité est acceptée alors qu'une affectation directe ne l'est pas ???"
; mem0.l=mem1+mem2
MOV ebx,dword [v_mem1]
ADD ebx,dword [v_mem2]
MOV dword [v_mem0],ebx
; memx.l=($7FFFFFFF+$7FFFFFFF)
MOV dword [v_memx],4294967294
; memq1.q=$7FFFFFFF
MOV dword [v_memq1],2147483647
MOV dword [v_memq1+4],0
; memq2.q=$7FFFFFFF
MOV dword [v_memq2],2147483647
MOV dword [v_memq2+4],0
; memx=memq1+memQ2
PUSH dword [v_memq1+4]
PUSH dword [v_memq1]
LEA eax,[v_memq2]
MOV edx,[eax]
ADD [esp],edx
MOV edx,[eax+4]
ADC [esp+4],edx
POP eax
POP edx
MOV dword [v_memx],eax
; memxq.q=memq1+memQ2
PUSH dword [v_memq1+4]
PUSH dword [v_memq1]
LEA eax,[v_memq2]
MOV edx,[eax]
ADD [esp],edx
MOV edx,[eax+4]
ADC [esp+4],edx
POP dword [v_memxq]
POP dword [v_memxq+4]
; Debug Hex(memq1)+" "+Hex(memq2)+" "+Hex(memxq)+" "+Str(memq1)+" "+Str(memq2)+" "+Str(memxq)+" "+Str(memx)
; memq.q=$7FFFFFFF1
MOV dword [v_memq],-15
MOV dword [v_memq+4],7
; mem.l=memq
MOV eax,dword [v_memq]
MOV dword [v_mem],eax
; Debug Str(memq)+" " +Str(mem)
;
;
;
;
;
_PB_EOP_NoValue:
PUSH dword 0
_PB_EOP:
CALL _PB_EndFunctions
PUSH dword [PB_MemoryBase]
CALL _HeapDestroy@4
CALL _ExitProcess@4
_PB_EndFunctions:
RET
;
;
section '.data' data readable writeable
;
_PB_DataSection:
_PB_OpenGLSubsystem: db 0
pb_public PB_DEBUGGER_LineNumber
dd -1
pb_public PB_DEBUGGER_IncludedFiles
dd 0
pb_public PB_DEBUGGER_FileName
db 0
pb_public PB_Compiler_Unicode
dd 0
pb_public PB_Compiler_Thread
dd 0
pb_public PB_Compiler_Purifier
dd 0
pb_public PB_Compiler_Debugger
dd 0
_PB_ExecutableType: dd 0
align 4
align 4
align 4
s_s:
dd 0
dd -1
s_swiftbtndata:
dd 16
dd -1
align 4
;
section '.bss' readable writeable
_PB_BSSSection:
align 4
;
I_BSSStart:
_PB_MemoryBase:
PB_MemoryBase: rd 1
_PB_Instance:
PB_Instance: rd 1
;
align 4
v_memxq rq 1
v_memq1 rq 1
v_memq2 rq 1
v_mem3 rq 1
v_memq rq 1
v_mem1q rq 1
v_mem2q rq 1
v_mem rd 1
PB_DataPointer rd 1
v_mem0 rd 1
v_mem1 rd 1
v_mem2 rd 1
v_memx rd 1
align 4
align 4
align 4
align 4
I_BSSEnd:
section '.data' data readable writeable
SYS_EndDataSection:
M.
PAPIPP
Messages : 534
Inscription : sam. 23/févr./2008 17:58

Re: Optimisation de PureBasic et de L’ASM

Message par PAPIPP »

Bonjour Mesa
Le prg que tu nous montres est le généré ASM de la discussion que j’ai eu avec Spock (Dobro).
Qui n’a rien à voir avec la vitesse des 3 modes de swap en ASM.

Cette discussion porte sur la possibilité à partir de 2 données A et B de réaliser A+B et ensuite de faire B=A+B-A et B=A+B-B ce qui permet d’affecter B=A+B-B => donc A et A=A+B-A => donc B.
La petite bizarrerie que j’ai remarquée est l’impossibilité d’affecter directement une valeur > à la capacité le la variable.
Alors que cette valeur passée par une autre variable de capacité supérieure ne donne aucune erreur.

Exemple.

Code : Tout sélectionner

; va.l=$80000000+$80000000  ;;;; =====> erreur à la compilation
v1q.q=$80000000
v2q.q=$80000000
VA=V1Q+V2Q  ;;;;;  =====> pas d'erreur !!!!

Pour comparaison des vitesses
Je suis sous XP x86 en 32 bits
mem1=1234567890 mem2=987654321
mem1=987654321 mem2=1234567890
av xor mem1=1234567890 mem2=987654321
ap xor mem1=987654321 mem2=1234567890
mem1=987654321 mem2=1234567890
NB cycles XCHG = fin-deb-delta=111944
Nb cycles MOV =finm-debm-delta=3024
Nb cycles XOR =finx-debx-delta=4968
Le programme testé pour la vitesse des instructions ASM est le suivant

Code : Tout sélectionner

 ProcedureDLL.q _nbcs()
;   ; ;   L'instruction RDTSC est un mnémonique pour ReaD Time Stamp Counter.
;   ; ;   L'instruction retourne dans le couple de registre EDX:EAX le nombre de ticks1 écoulés depuis la dernière remise à zéro du processeur
;   ; ;   (Reset).
   !RDTSC
   ProcedureReturn
 EndProcedure
 Macro _q_t_
 	"
 EndMacro
 Macro _n(__n)
 	_q_t_#__n#=_q_t_+Str(__n)+" "
 EndMacro
 Macro _s(__S)
 	_q_t_#__S#=_q_t_+__S+" "
 EndMacro
 Macro _NL
 	"N°L="+Str(#PB_Compiler_Line)+" ** "
 EndMacro



maxi=1000
maxi=maxi | $00000001
;  Nombre impair pour voir l'échange entre mem1 et mem2 la boucle loop xxx réalise le nombre exact d'itération
;                   alors que for démarrant à 0 réalise une itération de plus
mem1.l=1234567890
mem2.l=987654321
mes1$=_n(mem1)+_n(mem2)+#LF$
debo.q=_nbcs()
EnableASM
MOV ecx,[v_maxi]
!ldebo:
LOOP ldebo
; DisableASM
delta.q=_nbcs()-debo

deb.Q=_nbcs()
; EnableASM
MOV ecx,[v_maxi]
!ldebxch:
MOV ebx,[v_mem1]
XCHG ebx,[v_mem2]
MOV[v_mem1],ebx
LOOP ldebxch
; DisableASM
fin.q=_nbcs()
mes1$+_n(mem1)+_n(mem2)+#LF$

debm.q=_nbcs()
; EnableASM
MOV ecx,[v_maxi]
!ldebmov:
MOV ebx,[v_mem1]
MOV edi,[v_mem2]
MOV[v_mem1],edi
MOV[v_mem2],ebx
LOOP ldebmov
; DisableASM
finm.q=_nbcs()
mes1$+" av xor "+_n(mem1)+_n(mem2)+#LF$

debx.q=_nbcs()
;   EnableASM
MOV ecx,[v_maxi]
!ldebxor:
MOV ebx,[v_mem1]
XOR ebx,[v_mem2]
XOR[v_mem1],ebx
XOR[v_mem2],ebx
LOOP ldebxor
DisableASM
finx.q=_nbcs()
mes1$+" ap xor "+_n(mem1)+_n(mem2)+#LF$

mes1$+_n(mem1)+_n(mem2)+#LF$
mes1$+" NB cycles XCHG    = "+_n(fin-deb-delta)+#LF$
mes1$+" Nb cycles MOV  ="+_n(finm-debm-delta)+#LF$
mes1$+" Nb cycles XOR   ="+_n(finx-debx-delta)+#LF$

MessageRequester("essaicycle machine",mes1$)
OpenConsole()
PrintN(mes1$)
Input()
CloseConsole()
A+
Il est fort peu probable que les mêmes causes ne produisent pas les mêmes effets.(Einstein)
Et en logique positive cela donne.
Il est très fortement probable que les mêmes causes produisent les mêmes effets.
Mesa
Messages : 1126
Inscription : mer. 14/sept./2011 16:59

Re: Optimisation de PureBasic et de L’ASM

Message par Mesa »

J'avais bien compris. :wink:

1) Pour la vitesse des échanges mémoires, sur mon xp32 et petite config, moi, j'ai:
NB cycles XCHG = fin-deb-delta=248944
Nb cycles MOV =finm-debm-delta=347492
Nb cycles XOR =finx-debx-delta=290460
2) Et pour le débordement, ce que je voulais dire en fait, c'est que je pense que tu as compris comme moi que PB doit faire des tests lors de l'allocation mémoire (création des variables) mais une fois fait, il n'y a plus de test et c'est au programmeur de faire attention à ce que le résultat d'une opération rentre dans la variable. Autrement dit, si la retenu de ADC (SBx) tombe (il y a débordement) sans être utilisée, aucune erreur de compilation n'est levée.

Reste à savoir si c'est souhaitable.
Tu peux peut-être demander plus d'info à Fred.

(Évidemment, tout cela n'a rien à voir avec les arrondis :roll: )

Code : Tout sélectionner

; va.l=$80000000+$80000000  ;;;; =====> erreur à la compilation
Erreur, Oui car tu veux allouer "trop" de mémoire

Code : Tout sélectionner

v1q.q=$80000000
v2q.q=$80000000
VA=V1Q+V2Q  ;;;;;  =====> pas d'erreur !!!!
Ici l'allocation est bonne et puis VA est rempli avec "0" car le débordement n'est pas pris en compte et sans lever d'erreur

[Edition] Si c'est vraiment critique, il te faudra tester le bit CF du registre EFLAGS, c'est le bit 0
Je pensais pouvoir utiliser TEST qui permet de tester un bit sans le modifier (après avoir récupérer le contenu du registre) mais en fait tout est expliqué ici (c'est très simple en fait)
https://fr.wikibooks.org/wiki/Programma ... /Les_flags
Accès au registre des indicateurs

Les indicateurs sont utilisés par les instructions de saut conditionnels.

Il est possible d'accéder au registre des indicateurs de deux manières :

En utilisant la pile :
Le registre des indicateurs est copié sur la pile (empilé) par l'instruction PUSHF,
Il est lu depuis la pile (dépilé) par l'instruction POPF.

En passant par le registre AH :
L'instruction LAHF charge les 8 bits de poids faible du registre des indicateurs vers le registre AH,
L'instruction SAHF copie le contenu du registre AH dans les 8 bits de poids faible du registre des indicateurs.
Normalement le multitâche ne devrait pas interférer mais bon...
A vérifier.

M.
PAPIPP
Messages : 534
Inscription : sam. 23/févr./2008 17:58

Re: Optimisation de PureBasic et de L’ASM

Message par PAPIPP »

Bonjour Mesa
Merci pour ta réponse.
Les vitesses que tu donnes sont réalisées avec l'option debug ce qui introduit des temps supérieurs et parasites

Voici à titre de comparaison ce que j'ai obtenu avec l'option debug.
mem1=1234567890 mem2=987654321
mem1=987654321 mem2=1234567890
av xor mem1=1234567890 mem2=987654321
ap xor mem1=987654321 mem2=1234567890
mem1=987654321 mem2=1234567890
NB cycles XCHG = fin-deb-delta=484008
Nb cycles MOV =finm-debm-delta=566584
Nb cycles XOR =finx-debx-delta=566560
Et sans debug qui est une option normale d'exploitation.

Code : Tout sélectionner

mem1=1234567890 mem2=987654321
mem1=987654321 mem2=1234567890
 av xor mem1=1234567890 mem2=987654321
 ap xor mem1=987654321 mem2=1234567890
mem1=987654321 mem2=1234567890
 NB cycles XCHG    = fin-deb-delta=111760
 Nb cycles MOV  =finm-debm-delta=2984
 Nb cycles XOR   =finx-debx-delta=4920


A+
Il est fort peu probable que les mêmes causes ne produisent pas les mêmes effets.(Einstein)
Et en logique positive cela donne.
Il est très fortement probable que les mêmes causes produisent les mêmes effets.
Mesa
Messages : 1126
Inscription : mer. 14/sept./2011 16:59

Re: Optimisation de PureBasic et de L’ASM

Message par Mesa »

Oui en effet
Sans debogueur
NB cycles XCHG = fin-deb-delta=24956
Nb cycles MOV =finm-debm-delta=2992
Nb cycles XOR =finx-debx-delta=4948
J'ai quelques souvenirs sur xchg qui n'était jamais utilisé par les programmeur en asm car ses temps de latence entre un registre et la mémoire était trop important, jusqu'à 18 cycles, je crois et de toute façon cette instruction a toujours été plus lente que mov.


M.
PAPIPP
Messages : 534
Inscription : sam. 23/févr./2008 17:58

Re: Optimisation de PureBasic et de L’ASM

Message par PAPIPP »

Merci pour ta réponse

Intel optimise les instructions qui sont le plus souvent utilisées MOV XOR etc.
Mais pour des raisons économiques (coûts de recherche et de prix de vente trop élevés)
Ils n’ont pas optimisé toutes les instructions et entre autre c’est le cas de l’instruction XCHG.
Je pense qu’AMD fait de même pour ses processeurs

XCHG REG,mem => 5 cycles
MOV REG,mem => 1 cycle

A+
Il est fort peu probable que les mêmes causes ne produisent pas les mêmes effets.(Einstein)
Et en logique positive cela donne.
Il est très fortement probable que les mêmes causes produisent les mêmes effets.
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: Optimisation de PureBasic et de L’ASM

Message par Ollivier »

Je me suis permis de publier un banc de mesure pour permettre telle ou telle vérification.

Cela permet de comprendre avec force test pourquoi, par exemple rajouter des instructions de saut inutiles vont accélérer la routine, pourquoi un SiZero est plus rapide ou plus lent qu'un SiNonZero, etc...

C'est évidemment très loin d'être un code parfait. (On peut tout mettre en Global par exemple) Mais ce n'est pas le but. Pour faire des mesures efficaces, il est nécessaire d'ajouter une foule de directives.
PAPIPP
Messages : 534
Inscription : sam. 23/févr./2008 17:58

Re: Optimisation de PureBasic et de L’ASM

Message par PAPIPP »

Bonjour Olivier
C'est OK
On peut par exemple pour mesurer la tare en nombre approché de cycles.
Exécutez ce PRG sans debug

Code : Tout sélectionner

 Macro _q_t_
   "
 EndMacro
 Macro _n(__n)
   _q_t_#__n#=_q_t_+Str(__n)+" "
 EndMacro
 Macro _s(__S)
   _q_t_#__S#=_q_t_+__S+" "
 EndMacro
 Macro _NL
   "N°L="+Str(#PB_Compiler_Line)+" ** "
 EndMacro
 Macro _d (__D,__nv=8)
 _q_t_#__D#=_q_t_+StrD(__D,__nv)+" "
 EndMacro

Define DEB.Q,FIN.Q,TAR.q
maxi=100000
!mov ecx,[v_maxi]
!RDTSC
!MOV dword [v_DEB], EAX
!MOV dword [v_DEB+4],edx
; !MOV dword [v_TAR], EAX  ; instructions doublées pour mesurer la tare (mais valeur peu importante avec 100000 itérations ou plus )
; !MOV dword [v_TAR+4],edx
!debut0:
!loop debut0
!RDTSC
!MOV dword [v_TAR], EAX
!MOV dword [v_TAR+4],edx
TAR=TAR-DEB
NBCYCLES_APP.D=TAR/maxi
mes$=_n(tar)+_n(DEB)+_d(NBCYCLES_APP)
MessageRequester("essai",mes$)
Cependant, si c'est pour calculer le nombre de cycles d'une série d'instructions avec précision, ce n'est pas si simple avec les processeurs modernes, car il y a le problème de l'accessibilité de la mémoire (en cache [lequel?] ou pas ?), le pipeline des instructions, les interruptions qui changent le temps d'exécution d'un test à l'autre, le multitâche qui fait de même..
C'est pourquoi je fais une boucle de tests de 10000 à 10000000 itérations pour avoir un résultat proportionnel aux nombre de cycles.

Quant à l'instruction RDTSC, elle m'a beaucoup servi. C'est formidable pour optimiser un process.

A+
Il est fort peu probable que les mêmes causes ne produisent pas les mêmes effets.(Einstein)
Et en logique positive cela donne.
Il est très fortement probable que les mêmes causes produisent les mêmes effets.
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: Optimisation de PureBasic et de L’ASM

Message par Ollivier »

Je ne sais pas, pour ma part. Je ne peux tester le code dans l'immédiat.

C'est juste un truc propre pour faire des mesures dans tous les sens.

Il me semble qu'un " loop " est lent. C'est une vieille conclusion que j'avais tiré il y a un peu plus de 20 ans. Donc, peut-être que ça a changé. Un " dec + jnz + jump " sera plus rapide... Cela dépend effectivement de plein de choses. Si le registre de comptage est utilisé dans un calcul ou non, cela dépend du nombre d'imbrications aussi. " loop " est dans un "gap" fonctionnel: entre un calcul à répéter d'un côté et les instructions de chaîne de l'autre...

Pour XCHG, c'est évident que c'est lent. Si tu prends 4 lieux symboliques:

1) Le compartiment fourchette du tiroir à couverts
2) Le compartiment cuillère du tiroir à couverts
3) Le rayon couverts de chez Intermarché
4) Le rayon couverts de chez Edouard Leclerc

Il est sûr que prendre une cuillère de sa main gauche et une fourchette de sa main droite, puis croiser les bras pour interchanger ladite fourchette et ladite cuillère, c'est rapide et XCHG est fait pour (1 cycle).

Maintenant, aller échanger sa fourchette avec une autre à Intermarché ça met du temps. XCHG n'est pas fait pour...

Maintenant, aller interchanger une fourchette de chez Inter avec une fourchette de chez Leclerc, ça implique le service Qualité de chaque magasin.

Aussi, ça fait à peu près 5 ans que sur le forum anglais, des membres ont expliqué à Fred qu'il y a de l'électroménager à la maison (SSE) et qu'avec Papa et la bonne qui invite le 45ème régiment (multi-tâche) chacun avec un sac de 40 Kg (cache), on peut en remuer des couverts...

Je crois qu'il met ce qu'il peut en natif. Après, tu trouveras toujours une appli spécifique où tu peux y aller pied dedans et être plus rapide, puisque tu peux déterminer spécialement tes besoins ni plus, ni moins.

Evidemment, ce que je dis peut être remis en cause...
PAPIPP
Messages : 534
Inscription : sam. 23/févr./2008 17:58

Re: Optimisation de PureBasic et de L’ASM

Message par PAPIPP »

A Olivier

Je sais que dec jnz xxx est plus rapide que LOOP
Mais par paresse je préfère utiliser LOOP qui n’a qu’ une seule instruction.
D’ailleurs les résultats sont assez identiques si l’on prend soin de déterminer une tare pour chaque boucle (dec jnz et loop).

Le code suivant doit être exécuté sans debug

Code : Tout sélectionner

 Macro _q_t_
   "
 EndMacro
 Macro _n(__n)
   _q_t_#__n#=_q_t_+Str(__n)+" "
 EndMacro
 Macro _s(__S)
   _q_t_#__S#=_q_t_+__S+" "
 EndMacro
 Macro _NL
   "N°L="+Str(#PB_Compiler_Line)+" ** "
 EndMacro
 Macro _d (__D,__nv=8)
 _q_t_#__D#=_q_t_+StrD(__D,__nv)+" "
 EndMacro
Define DEB.Q,FIN.Q,TAR.q,TARL.q
maxi=10000000
maxi2=maxi
mes$=""
Macro DEBUT
  !mov ecx,[v_maxi]
  !RDTSC
  !MOV dword [v_DEB], EAX
  !MOV dword [v_DEB+4],edx
EndMacro
Macro FINM
  ; LOOP debut1
  !RDTSC
  !MOV dword [v_FIN], EAX
  !MOV dword [v_FIN+4],edx
EndMacro
DebuT
!debut:
! dec ecx
!jnz debut
FINM
TAR=Fin
TAR=TAR-DEB
NBCYCLES_APP.D=TAR/maxi
mes$+"avec dec jnz "+_n(tar)+_n(DEB)+_d(NBCYCLES_APP)+#LF$
;******************** fin  de la tare  dec TAR ****************

DebuT
!debut0:
!loop debut0
FINM
TARL=Fin
TARL=TARL-DEB
NBCYCLES_APP.D=TARL/maxi
mes$+"avec Loop "+_n(tarL)+_n(DEB)+_d(NBCYCLES_APP)+#LF$
;******************** fin de la tare loop TARL ***************

DEBUT
!debut1:
!mov ebx,[v_maxi]    ; instruction à tester
!XCHG EBX,[v_maxi2]  ; instruction à tester
!mov [v_maxi],EBX    ; instruction à tester
! dec ecx
!jnz debut1
FINM
NBCYCLES_APP.D=(FIN-(DEB+TAR))/maxi
mes$+"Instruction XCHG et dec "+_n(FIN-(DEB+TAR))+_d(NBCYCLES_APP)+#LF$

DEBUT
!debut2:
!mov ebx,[v_maxi]    ; instruction à tester
!XCHG EBX,[v_maxi2]  ; instruction à tester
!mov [v_maxi],EBX    ; instruction à tester
!loop debut2
FINM
NBCYCLES_APP.D=(FIN-(DEB+TARL))/maxi
mes$+"Instruction XCHG et LOOP "+_n(FIN-(DEB+TARL))+_d(NBCYCLES_APP)+#LF$

DEBUT
!debut3:

!mov esi,[v_maxi]   ; instruction à tester
!mov EBX,[v_maxi2]  ; instruction à tester
!mov [v_maxi],ebx   ; instruction à tester
!mov [v_maxi2],esi  ; instruction à tester
! dec ecx
!jnz debut3
FINM
NBCYCLES_APP.D=(FIN-(DEB+TAR))/maxi
mes$+"Instruction MOV et dec "+_n(FIN-(DEB+TAR))+_d(NBCYCLES_APP)+#LF$

DEBUT
!debut4:
!mov esi,[v_maxi]   ; instruction à tester
!mov EBX,[v_maxi2]  ; instruction à tester
!mov [v_maxi],ebx   ; instruction à tester
!mov [v_maxi2],esi  ; instruction à tester
!loop debut4
FINM
NBCYCLES_APP.D=(FIN-(DEB+TARL))/maxi
mes$+"Instruction MOV et LOOP "+_n(FIN-(DEB+TARL))+_d(NBCYCLES_APP)+#LF$

MessageRequester("essai",mes$)
A+
Il est fort peu probable que les mêmes causes ne produisent pas les mêmes effets.(Einstein)
Et en logique positive cela donne.
Il est très fortement probable que les mêmes causes produisent les mêmes effets.
Répondre