AVX2 est un mode d'utilisation des processeurs x64 qui permet de faire du calcul numérique parallélisé. L'idée est de permettre d'injecter des données entrant dans des calculs en utilisant un jeu de registres élargi, à l'instar de ce que permet mmx, mais le mode AVX2 utilise une largeur de registres de 256 bits (registres ymm.). Ceci permet de faire passer autant de mots de différents formats que possibles, soit dans le cas d'une arithmétique 64 bits, 4 données en parallèle.
Travailler avec AVX2 nécessite de bien comprendre l'idée de parallélisme : on fait passer plusieurs données à la fois. Cela implique également de "négocier" de longues heures avec l'environnement interne d'un processeur x64 et de jongler avec tous les éléments utiles.
Pour envoyer des données au processeur, il faut les faire passer de la mémoire aux registres, et indiquer les instructions à effectuer, puis récupérer les résultats dans les registres pour les repasser en mémoire afin de les utiliser.
PureBasic ne dispose pas d'instructions spécifiques pour gérer AVX2, mais j'ai mis une petite méthode didactique en place pour y comprendre quelque chose et parvenir à faire réagir la bête à mon commandement. Je n'ai sans doute pas fait le tour du sujet, loin de là, c'est très poilu. Mais si cela peut susciter de l'intérêt tant mieux.
Il faut préciser qu'aucun langage de programmation ne dispose d'un vrai truc spécifique pour faire du parallélisme, quand on y regarde de près, le parallélisme en C ne vaut pas mieux, c'est imbitable malgré la disponibilité de bibliothèques idoines.
La présentation qui est montrée ici ne propose pas une large variété d'exemples, mais c'est un bout qui peut inspirer celles et ceux que ça intéressera pour aller plus loin. Dans une approche plus aboutie, on pourrait en déduire une bibliothèque assez riche, soit à base de macros, soit sous forme de procédures (ou les deux d'ailleurs).
Une petite mise en garde ... si t'aime pas l'assembleur, faut regarder ailleurs
Code : Tout sélectionner
Structure NOTHING
EndStructure
Define.NOTHING
Structure PACKED4D ; structure permettant de précharger / lire des Doubles
d0.d ; le format est 4 x 64 = 256 bits.
d1.d ; Ce format correspond à un registre ymm
d2.d
d3.d
EndStructure
Structure PACKED4Q ; structure permettant de précharger / lire des Quads
q0.q ; format 4 x 64 = 256 bits compatible registres ymm.
q1.q
q2.q
q3.q
EndStructure
Structure PACKED4x64 ; Structure fusionnée pour adresser soit en Double soit en Quad (Integer)
StructureUnion
P4D.PACKED4D
P4Q.PACKED4Q
EndStructureUnion
EndStructure
Vector1.PACKED4x64
Vector2.PACKED4x64
Results.PACKED4x64
res.d
Debug "Somme horizontale d'un vecteur AVX2"
Vector1\P4D\d0 = Random(10, 1)
Vector1\P4D\d1 = Random(10, 1)
Vector1\P4D\d2 = Random(10, 1)
Vector1\P4D\d3 = Random(10, 1)
! lea r9, qword [v_Vector1]
! vmovupd ymm0, yword [r9]
! vhaddpd ymm2, ymm0, ymm0 ; r0 = ymm0\0 + ymm0\1, r1 = ymm0\0 + ymm0\1, r2 = ymm0\2 + ymm0\3, r3 = ymm0\2 + ymm0\3
! lea r9, qword [v_Results]
! vmovupd yword [r9], ymm2
! lea r9, qword [v_Results] ; r0 + r2 = ymm0\0 + ymm0\1 + ymm0\2 + ymm0\3
! fld qword [r9]
! fadd qword [r9+24]
! fstp qword [v_res]
Debug StrD(Vector1\P4D\d0, 1) + #TAB$ + StrD(Vector1\P4D\d1, 1) + #TAB$ + StrD(Vector1\P4D\d2, 1) + #TAB$ + StrD(Vector1\P4D\d3, 1) + #TAB$ + StrD(res, 1)
Debug "Comparaison de deux vecteurs AVX2"
Vector1\P4D\d0 = Random(10, 1)
Vector1\P4D\d1 = Random(10, 1)
Vector1\P4D\d2 = Random(10, 1)
Vector1\P4D\d3 = Random(10, 1)
Vector2\P4D\d0 = Random(10, 1)
Vector2\P4D\d1 = Random(10, 1)
Vector2\P4D\d2 = Random(10, 1)
Vector2\P4D\d3 = Random(10, 1)
! lea r9, qword [v_Vector1]
! vmovupd ymm0, yword [r9]
! lea r9, qword [v_Vector2]
! vmovupd ymm1, yword [r9]
! vcmpeqpd ymm2, ymm1, ymm0 ; eq. vcmppd ymm2, ymm1, ymm0, $0
! vmovupd yword [v_Results], ymm2
Debug #TAB$ + StrD(Vector1\P4D\d0, 1) + #TAB$ + StrD(Vector1\P4D\d1, 1) + #TAB$ + StrD(Vector1\P4D\d2, 1) + #TAB$ + StrD(Vector1\P4D\d3, 1) + #CRLF$ +
#TAB$ + StrD(Vector2\P4D\d0, 1) + #TAB$ + StrD(Vector2\P4D\d1, 1) + #TAB$ + StrD(Vector2\P4D\d2, 1) + #TAB$ + StrD(Vector2\P4D\d3, 1) + #CRLF$ +
"eq" + #TAB$ + Str(Results\P4Q\q0) + #TAB$ + Str(Results\P4Q\q1) + #TAB$ + Str(Results\P4Q\q2) + #TAB$ + Str(Results\P4Q\q3)
! vcmpltpd ymm2, ymm1, ymm0 ; eq. vcmppd ymm2, ymm1, ymm0, $0
! vmovupd yword [v_Results], ymm2
Debug "lt" + #TAB$ + Str(Results\P4Q\q0) + #TAB$ + Str(Results\P4Q\q1) + #TAB$ + Str(Results\P4Q\q2) + #TAB$ + Str(Results\P4Q\q3)
Debug "Distance entre deux points placés dans un vecteur AVX2"
Vector1\P4D\d0 = Random(10, 1) ; x1
Vector1\P4D\d1 = Random(10, 1) ; x2 la distance est sqr((x1 - x2)² + (y1 - y2)²)
Vector1\P4D\d2 = Random(10, 1) ; y1
Vector1\P4D\d3 = Random(10, 1) ; y2
! lea r9, qword [v_Vector1]
! vmovupd ymm0, yword [r9]
! vhsubpd ymm1, ymm0, ymm0 ; r0 = ymm0\0 - ymm0\1, r1 = ymm0\0 - ymm0\1, r2 = ymm0\2 - ymm0\3, r3 = ymm0\2 - ymm0\3
! vmulpd ymm2, ymm1, ymm1
! lea r9, qword [v_Results]
! vmovupd yword [r9], ymm2
! lea r9, qword [v_Results] ; r0 + r2 = (ymm0\0 - ymm0\1)² + (ymm0\2 - ymm0\3)²
! fld qword [r9]
! fadd qword [r9+24]
! fsqrt
! fstp qword [v_res]
Debug StrD(Vector1\P4D\d0, 1) + #TAB$ + StrD(Vector1\P4D\d1, 1) + #TAB$ + StrD(Vector1\P4D\d2, 1) + #TAB$ + StrD(Vector1\P4D\d3, 1) + #TAB$ + StrD(res, 1) + #TAB$ + StrD(res * res, 1)
Debug StrD(Results\P4D\d0, 1) + #TAB$ + StrD(Results\P4D\d1, 1) + #TAB$ + StrD(Results\P4D\d2, 1) + #TAB$ + StrD(Results\P4D\d3, 1)
CallDebugger
End