SIMD_MMX_SSE

Pour discuter de l'assembleur
Avatar de l’utilisateur
Ganagyre
Messages : 67
Inscription : jeu. 09/nov./2006 13:41
Localisation : PACA

SIMD_MMX_SSE

Message par Ganagyre »

Bonjour.

Je teste les fonctionnalités SIMD, les bases pour débuter les additions de vecteurs/tableau avec des entiers. (vecteur/tableau étant des éléments contigue de meme longueur / aligné).

Code : Tout sélectionner

;
; ADDITION_SIMD_MMX_SSE_
; 1=> test Vecteur//Tableau
; long.l = 7   // = 8*8 = 64 bits
; long.l =  15 //= 16*8 = 128 bits
long.l = 7 
;
Dim veca.b(long)  ;// Source 1 
Dim vecb.b(long)  ;// Source 2 
Dim vecc.b(long)  ;// Destination
;
a.l = @veca()
b.l = @vecb()
c.l = @vecc()
; Remplir Vecteur
;
For i = 0 To long
  ;
  veca(i) = i + 1
  vecb(i) = i + 10
  vecc(i) = 0
  Debug"======================"
  Debug "Adresse   "+Str(i)
  Debug "Vecteur A contient = "+Str(veca(i)) 
  Debug "Vecteur B contient = "+Str(vecb(i)) 
  Debug "Vecteur C contient = "+Str(vecc(i)) 
  ;
Next i
;
Debug"_______________________"
Debug"_______________________"
; 
;***** adition tableau_vecteur 
!mov      eax,     [v_a]  ;// source Vecteur a en memoire eax = 64 bits = 8 valeur 
!movq     xmm0,    [eax]  ;// copie en memoire xmm0/128_bits contenu eax/64_bits
;
!mov      ebx,     [v_b]  ;// source Vecteur b en memoire ebx = 64 bits = 8 valeur  
!movq     xmm1,    [ebx]  ;// copie en memoire xmm1/128_bits contenu ebx/64_bits
;
!mov      ecx,     [v_c]  ;// destination Vecteur c en memoire ecx = 64 bits = 8 valeur 
!movq     xmm2,    [ecx]  ;// copie en memoire xmm2/128_bits contenu ecx/64_bits
;
!paddq    xmm2,    xmm0   ;// adition regitres xmm2 et xmm0 / vecc + veca 
!paddq    xmm2,    xmm1   ;// adition regitres xmm2 et xmm1 / vecc + vecb
;                         
!movq    [ecx],    xmm2  ; // resultat dans destination = vecc
;
;*******
;
For i = 0 To long
  Debug"________SSE_ADITION_RESULTAT_________"
  Debug "Adresse   "+Str(i)
  Debug "Vecteur A contient = "+Str(veca(i)) 
  Debug "Vecteur B contient = "+Str(vecb(i)) 
  Debug "Vecteur C contient = "+Str(vecc(i)) 
Next i
;
Debug""
les petits détails :

_Est'il possible de mettre directement dans le registre xmm0 un vecteur/tableau sans passer par l'intermédaire d'un registre EAX , idem si on augmente la taille des vecteurs/tableau avec comme limite les 128 bits xmm0, donc la variable long = 15 ?

style : !mov... xmm0, [v_a]

_2 vecteurs/tableau de 64bits peuvent se tenir cote à cote dans les 128 bits d'un seul registre xmm0 , la aussi est'il possible de les aligner cote à cote ?

style : !mov.. xmm0, [v_a + v_b ] ( 64_64)

:wink:
Mesa
Messages : 1093
Inscription : mer. 14/sept./2011 16:59

Re: SIMD_MMX_SSE

Message par Mesa »

Je ne crois pas qu'il faille utiliser les registres xmm directement car les ingénieurs d'Intel ont utilisés les registres ST du FPU (opérations sur les nombres à virgule flottante). A l'époque du pentium MMX (pentium 1), les transistors coûtaient cher. On ne peut donc pas utiliser simultanément les opérations MMX et celles sur les nombres à virgule flottante en assembleur.

Registres mm0 à mm7 font 64 bits mais un peu plus tard a été créés les registres Xmm qui font 128 bits.
http://fr.wikipedia.org/wiki/Streaming_SIMD_Extensions

Dans ton code, le registre eax est un registre 32 bits et non 64 bits, il faut utiliser le registre rax (64 bits) mais ce registre ne fonctionnera pas sur un windows 32 bits...
Il faut donc utiliser des instructions ad-hoc:PADDB/PADDW/PADDD,...
On peut utiliser les reg eax,... pour les adresses qui, elles, sont en 32b sur un OS32 et en 64b sur un OS 64b.


A savoir avec Purebasic, d'après la doc:
"-Les erreurs dans une section asm ne sont pas reportées par PureBasic mais par FAsm. Vérifiez votre code si une telle erreur survient.
- Les registres volatiles sont: eax, edx et ecx. Tous les autres doivent être préservés."
Donc ne pas utiliser ebx sans le sauvegarder auparavant.(dans ton code, tu utilises ebx.)

Instructions:
PADDB/PADDW/PADDD.
Elles permettent d'additionner deux séries, de 8 bytes (PADDByte), de 4 words (PADDWord), ou de 2 doubleword (PADDDoubleword).

Tu peux t'inspirer des codes delphi ici
http://esibert.developpez.com/delphi/mmx/
http://esibert.developpez.com/delphi/mmx2/

Pour compacter des données dans Xmm, voir ici
http://www.gladir.com/LEXIQUE/ASM/movaps.htm

Tiens nous au courant si tu parviens à quelque chose, ça m’intéresse.

Mesa.
Avatar de l’utilisateur
Ganagyre
Messages : 67
Inscription : jeu. 09/nov./2006 13:41
Localisation : PACA

Re: SIMD_MMX_SSE

Message par Ganagyre »

Bonjour.

Merci Mesa pour les explications et les docs.
(http://www.gladir.com/LEXIQUE/ASM/)
Il y de quoi se documenter/perfectionner.

Ma plus grosse gourde, la taille des registres , EAX,EBX,ECX,EDX,EDI,ESI,EBP,EIP,ESP sont effectivement en 32 bits sur un OS32.
64 Bits pour les MM0-7 // 128 pour les XMM0-7 sur les OS32.

Me reste à améliorer le remplissage/manipulation des Registres .
Vecteur => Reg32 => Reg MM // Reg XMM // calcul => Reg vers Vecteur.

Je posterai le résultat.
:wink:
PAPIPP
Messages : 534
Inscription : sam. 23/févr./2008 17:58

Re: SIMD_MMX_SSE

Message par PAPIPP »

Pour alléger le prg comme il est toujours nécessaire d’avoir un registre
Voici une solution particulière pour ce type de données

Code : Tout sélectionner

;JE teste LES fonctionnalités SIMD,LES bases pour débuter LES additions de vecteurs/tableau
;avec des entiers. (vecteur/tableau étant des éléments contigue de meme longueur/aligné).
;
; ADDITION_SIMD_MMX_SSE_
; 1=> test Vecteur//Tableau
; long.l = 7   // = 8*8 = 64 bits
; long.l =  15 //= 16*8 = 128 bits
long.l=7
;
Dim veca.b(long)  ;// Source 1
Dim vecb.b(long)  ;// Source 2
Dim vecc.b(long)  ;// Destination

; Define.l r1, r2, r3,r10,r12,r13
a.l=@veca()
b.l=@vecb()
c.l=@vecc()


Debug b-a
Debug c-b
Debug c-a
; Remplir Vecteur
;
For i=0 To long
	;
	veca(i)=i+1
	vecb(i)=i+10
	vecc(i)=0
	Debug"======================"
	Debug "Adresse   "+Str(i)
	Debug "Vecteur A contient = "+Str(veca(i))
	Debug "Vecteur B contient = "+Str(vecb(i))
	Debug "Vecteur C contient = "+Str(vecc(i))
	;
Next i
;
Debug"_______________________"
Debug"_______________________"
;***** adition tableau_vecteur
!mov ecx, [v_a]  ;// source Vecteur a en memoire eax = 64 bits = 8 valeur
!movq xmm0, [ecx]  ;// copie en memoire xmm0/128_bits contenu eax/64_bits

!movq xmm1,[ecx+40]
;
!movq xmm2,[ecx+80]
;
!paddq xmm2, xmm0   ;// adition regitres xmm2 et xmm0 / vecc + veca
!paddq xmm2, xmm1   ;// adition regitres xmm2 et xmm1 / vecc + vecb

!movq [ecx+80],xmm2  ; résultat dans vecc ecx+80

;*******
; Debug "r1="+Str(r1)+" r2="+Str(r2)
For i=0 To long
	Debug"________SSE_ADITION_RESULTAT_________"
	Debug "Adresse   "+Str(i)
	Debug "Vecteur A contient = "+Str(veca(i))
	Debug "Vecteur B contient = "+Str(vecb(i))
	Debug "Vecteur C contient = "+Str(vecc(i))

Next i
;
Debug""

Toutefois il est très intéressante d'utiliser ces instructions.
A titre d'exemple voici un programme résumant les performances entre une instruction PB un prg ASM simple et un Prg avec instructions SSE..
L'instruction Pb est 'Resultat = CountString(Chaine$, ChaineATrouver$)'

Code : Tout sélectionner

Procedure CountChars(*B.Ascii,L,Char.a)
  ;posted by Trond
	!mov ecx, [p.v_L]
	!mov dl, [p.v_Char]
	!mov eax, [p.p_B]
	
	!push edi
	!push ebp
	!mov ebp, eax
	
	!xor edi, edi
	!lp:
  !xor eax, eax
  !cmp byte [ecx+ebp-1], dl
  !sete al
  !add edi, eax
  !add ecx, -1
  !jnz lp
	!mov eax, edi
	
	!pop ebp
	!pop edi
	ProcedureReturn
EndProcedure

Procedure CountCharAsmSSE2_x86(*B.Ascii,Length,Char.a)
  ;Windows, 32-Bit
  ;based on posts by Road Runner. Excellent!
  ;without prefetch
  ;"old" loop, sorry
  ;not full tested!
  ; Le nom de @ @ signifie étiquette anonyme,vous pouvez avoir défini un grand nombre d'entre eux dans la source.
  ;Symbole @ b(ou l'équivalent @ r)le plus proche des références du label précédent anonyme
  ;,symbole @ f références plus proche de l'étiquette suivante anonyme.
  ;Ces symboles spéciaux sont insensibles à la casse.
  
	!mov ecx,[p.v_Length]
	!movzx edx,byte[p.v_Char]
	!push esi
	!mov esi,[p.p_B+4]
	!push edi
	!push ebx
  
	;Duplicate Char (Byte to Dqword)
	!mov dh,dl
	!mov eax,edx
	!shl edx,10h
	!mov dx,ax
	!movd xmm0,edx
	!shufps xmm0,xmm0,0        ;xmm0=16x Char
  
	;Check Length
	!xor eax,eax               ;eax=0, General-Counter
	!mov edx,ecx               ;ecx=Length
	!shr ecx,4                 ;div.16
	!or ecx,ecx                ;ecx=Number of 16-Byte-Chunks
	!jz Rest                   ;Length<16, edx=Length
  
	!mov ebx,esi               ;Test for 16-Byte-Alignment
	!and ebx,0fh
	!jz IsAli16                ;is 16-Byte-Alignment
	!dec ecx                   ;Chunk-1
	!mov edi,10h
	!sub edi,ebx               ;Alignment-Diff
  
	!sub edx,edi               ;Length-edi
	!movd ebx,xmm0             ;Char
	!Rest0:
	!cmp byte[esi],bl
	!jne @f
	!inc eax
	!@@:
	!inc esi
	!dec edi
	!jnz Rest0
  
	!IsAli16:                  ;Alignment16
  
	!or ecx,ecx                ;ecx=Number of 16-Byte-Chunks
	!jz Rest                   ;Length<16 or Chunk set to Zero, edx=Length
  
	!mov ebx,ecx
	!shl ebx,4                 ;mul.16
	!sub edx,ebx               ;edx=Rest-Bytes without 16-Byte-Chunks
  
	!movdqa xmm1,xmm0          ;Save Char
	!pxor xmm2,xmm2            ;Counter in Chunk-Loop
	
	!mov edi,ecx
	!cmp ecx,0ffh
	!jbe @f
	!mov edi,0ffh
	!@@:
	!pcmpeqb xmm0,[esi]
	!paddb xmm2,xmm0
	!movdqa xmm0,xmm1          ;Restore Char
	!add esi,16
	!dec edi
	!jnz @b                    ; jump si Non Zero au précédent label anonyme @@
	
	!pxor xmm3,xmm3            ;xmm3=0
	!psubb xmm3,xmm2           ;neg -> pos
  
	!pxor xmm4,xmm4            ;xmm4=0
	!psadbw xmm4,xmm3          ;add the 8 upper and 8 lower Bytes, Results in Bits 0-15 and Bits 64-79
	!movdqa xmm3,xmm4          ;take a copy
	!psrldq xmm4,8             ;shift right 8 Bytes
	!paddw xmm3,xmm4           ;add the 2 8 byte sums to give a 16 byte sum
	!movd ebx,xmm3
	!add eax,ebx               ;add to General-Counter
  
	!sub ecx,0ffh
	!jna @f                    ;zero or less, no Chunks more Jump si Non Egal au label anonyme @@ suivant
	!pxor xmm2,xmm2
	!mov edi,0ffh
	!cmp ecx,0ffh
	!ja @b
	!mov edi,ecx
	!jmp @b
	!@@:
  
	!movdqa xmm0,xmm1          ;Restore Char in xmm0
  
	!Rest:
	!or edx,edx                ;edx=Rest-Bytes
	!jz Ende
  
	!movd ecx,xmm0             ;Char
	!Rest1:
	!cmp byte[esi],cl
	!jne @f
	!inc eax
	!@@:
	!inc esi
	!dec edx
	!jnz Rest1
	!Ende:
  
	!pop ebx
	!pop edi
	!pop esi
	
	ProcedureReturn
EndProcedure

B.s
 B="abcdefabcdefaaaaaffffbdddffff1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
; b="1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
B=ReplaceString(Space(1000)," ",B) ;thousand copies of B
L=Len(B)

#Tries=100000

time=ElapsedMilliseconds()
For U=0 To #Tries
	NumHelle=CountCharAsmSSE2_x86(@B,L,'a')
Next
TimeHelle=ElapsedMilliseconds()-time
Helle$="NumHelle :  "+Str(NumHelle)+"       Time : "+Str(TimeHelle)

time=ElapsedMilliseconds()
For U=0 To #Tries
	NumTrond=CountChars(@B,L,'a')
Next

TimeTrond=ElapsedMilliseconds()-time
time=ElapsedMilliseconds()

For U=0 To #Tries
	NumPB=CountString(B,"a")

Next
TIMEPB=ElapsedMilliseconds()-time
PB$="NumPB : "+Str(NumPB)+"       Time : "+Str(TimePB)

Trond$="NumTrond : "+Str(NumTrond)+"       Time : "+Str(TimeTrond)

MessageRequester("Results","Long="+Str(l)+#CRLF$+Helle$+#CRLF$+Trond$+#CRLF$+PB$)

Les 2 Prg ASM sont de Trond pour le plus simple ASM et de Road Runner avec instructions SSE
Les résultats sont sans appel.
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.
Avatar de l’utilisateur
Ganagyre
Messages : 67
Inscription : jeu. 09/nov./2006 13:41
Localisation : PACA

Re: SIMD_MMX_SSE

Message par Ganagyre »

Bonjour.


Merci PAPIPP, pour ces exemples, de quoi me permettre d'étudier au mieux les possibilitées de ces registres de base et MMX/SSE.
Ce qui m'étonne avec les registres de base EAX EBX ECX ..., c'est que normalement il font bien 32 bits de long, mais en utilisation dans mes tests on dirait qu'ils supportent le double ?
C'est le détail troublant sur lequel je cherche la ou les erreurs.

Reprise de mon code précédent en addition de vecteur *.word .
Addition sur les registres MMX

Code : Tout sélectionner

               
; ADDITION_SIMD_MMX_
; TEST avec Vecteur VARIABLES .W  
; WORD = 2 OCTET = 16 BITS = 2 NORMALEMENT DANS REGISTRE BASE 32 = 16 * 2
;
taille.l = 3 
;
Dim veca.w(taille)  ;// Source 1 Word = 2 octet = 16 bits
Dim vecb.w(taille)  ;// Source 2 Word = 2 octet = 16 bits
Dim vecc.w(taille)  ;// Compteur Word = 2 octet = 16 bits
;
Debug" Nombre elements Vecteur = "+ Str(ArraySize(veca())+1) ;
Debug" Taille Octet Vecteur    = "+Str((taille +1) * 2)+" Octets " 
Debug" Taille Bits  Vecteur    = "+Str((ArraySize(veca())+1)*2*8)+"  Bits "
;
a.l = @veca()
b.l = @vecb()
c.l = @vecc()
;
; Remplir Vecteur
;
For i = 0 To taille
  ;
  veca(i) = i + 1
  vecb(i) = i + 1000
  vecc(i) = 0
  Debug"======================"
  Debug "Adresse   "+Str(i)
  Debug "Vecteur A contient = "+Str(veca(i)) 
  Debug "Vecteur B contient = "+Str(vecb(i)) 
  Debug "Vecteur C contient = "+Str(vecc(i)) 
  ;
Next i
;
Debug"_______________________"
Debug"_______________________"
; 
;***** adition MMX  
!mov      eax,    [v_a]  
!movq     mm0,    [eax]  
;
!mov      ebx,    [v_b]   
!movq     mm1,    [ebx]  
;
!mov      ecx,    [v_c]  
!movq     mm2,    [ecx]  
;
!paddq    mm2,    mm0    
!paddq    mm2,    mm1   
;                         
!movq    [ecx],   mm2 
;
;*******
;
For i = 0 To taille
  ;
  Debug"______________________________________"
  Debug"________MMX_ADITION_C = A + B_________"
  Debug "Adresse   "+Str(i)
  Debug "Vecteur A contient = "+Str(veca(i)) 
  Debug "Vecteur B contient = "+Str(vecb(i)) 
  Debug "Vecteur C = A + B  = "+Str(vecc(i))
  ;
Next i
;
Debug""
;
Un registre EAX de 32 bits(4 octet) ne devrait copier dans l'exemple, que 4 octet/32 bits dans les registres MM0, soir 2 valeur WORD, les 2 premieres valeurs veca(0) et veca(1) ?
Portant la 4 valeurs passent (taille = 3) !

Bon je vais me pencher sur les exemples de PAPIPP.

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

Re: SIMD_MMX_SSE

Message par PAPIPP »

Bonjour Ganagyre

Voici à titre d'exemple quelques modes d'adressages en ASM

Code : Tout sélectionner

;Testez partiellement le différentes instructions d'adressage
; ADDITION_SIMD_MMX_SSE_
; 1=> test Vecteur//Tableau
; long.l = 7   // = 8*8 = 64 bits
; long.l =  15 //= 16*8 = 128 bits
long.l=7
;
Dim veca.b(long) ; vecteur A
Dim vecb.b(long) ; vecteur B

Define.l r0,r1,r2,r3,r4,r5,r6,r7,r8,r9
a.l=@veca()
b.l=@vecb()
; Remplir Vecteur
;
For i=0 To long
	veca(i)=i+1
	Debug"======================"
	Debug "Adresse   "+Str(i)
	Debug "Vecteur A = "+Str(veca(i))+" Vecteur B = "+Str(vecb(i))
	;
Next i
;
Debug"_______________________"
Debug"_______________________"
;***** tests des différents adressages
!mov eax,456           ; (1) charge 456 immédiatement dans eax 
!mov [v_r0],eax        ; charge valeur de eax dans r0 pour analyse dans debug
!mov eax,dword ptr v_a ;(2) charge la valeur v_a dans eax dans lequel  v_a est l'adresse du vecteur veca()
!mov [v_r1],eax        ; charge valeur de eax dans r1 pour analyse dans debug
!mov ecx, [v_a]        ; (3) instruction identique à (2) charge la valeur de v_a
!mov [v_r2],ecx        ; charge valeur de ecx dans r2 pour analyse dans debug
 MOV ebx,a             ;(4) instruction identique à (2) et à (3) 
   ; remarquez l'absence de ! devant mov car   la variable a n'est pas connue dans l'environnement ASM
   ; !mov ebx,a provoque une erreure ASM
; !MOV ebx,a

!mov [v_r3],ebx        ; charge valeur de ebx dans r3 pour analyse dans debug

!movq xmm0, [eax]      ; Charge la valeur pointée par l'adresse de eax   dans xmm0 sur 64bits 
     ; ce n'est pas le registre eax qui est chargé mais la valeur de 64 bits pointée par le registre 
MOV ebx,b              ; charge l'adresse de vecteur B dans ebx
!mov [v_r4],ebx        ; charge la valeur de ebx dans r4 pour analyse debug
!movq [ebx],xmm0       ; copie le registre de 64 bits xmm0 dans le vecteur B pointé par ebx              
;*******

; Debug _n(a)+_n(r0)+_n(r1)+_n(r2)+_n(r3)+_n(r4)
Debug "a="+Str(a)+" r0="+Str(r0)+" r1="+Str(r1)+" r2="+Str(r2)+" r3="+Str(r3)+" r4="+Str(r4)+" b="+Str(b)
For i=0 To long

	Debug "Adresse   "+Str(i)
	Debug "Vecteur A = "+Str(veca(i))+" Vecteur B = "+Str(vecb(i))
	
Next i
;
Debug""


Comme exemple d'adressage :

http://www.csee.umbc.edu/courses/underg ... _extra.pdf
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