[ASM/Intrinsics] Code asm généré

Code asm généré [ASM/Intrinsics] - ASM - Programmation

Marsh Posté le 19-11-2004 à 04:36:55    

Bonjour tout le monde,
 
Je m'intéresse en ce moment, plus par curiosité que par réel besoin, à l'utilisation des instrinsics pour avoir un accès "aisé" aux capacités SIMD des processeurs. Ayant un AthlonXP, j'ai fais qqs essais afin d'exploiter les instructions SSE. J'ai analysé le code asm généré par le compilateur, et cela soulève plusieurs interrogation. (J'ai compilé en Os parce que si je compile en O3, le compilo court circuite les instructions de la boucle et ne fait le "calcul" qu'une seule fois) :
 
Voici le bout de code et sa "variante" qui vont nous intéresser :
 
Version de base

Code :
  1. /*
  2. * Compilation:
  3. *  
  4. *  asm > gcc -std=c99 -Os -msse -S -c sse.c
  5. *  exe > gcc -std=c99 -Os -msse -c sse.c -o sse     
  6. *
  7. */
  8. #include <stdio.h>
  9. #include <xmmintrin.h>
  10. float a[4] __attribute__((aligned(16))) = { 2.f, 3.f, 4.f, 5.f } ;
  11. float b[4] __attribute__((aligned(16))) = { 5.f, 6.f, 7.f, 8.f } ;
  12. float c[4] __attribute__((aligned(16))) ;
  13. int main(int argc, char* argv[])
  14. {
  15.     for ( int i = 0 ; i < 100000 ; ++i ) {
  16.         for ( int j = 0 ; j < 100000 ; ++j ) {
  17.             _mm_store_ps(c,
  18.             _mm_add_ps( _mm_load_ps(a) ,_mm_load_ps(b)))  ;
  19.         }
  20.     }
  21.     printf("%f %f %f %f\n", c[0], c[1], c[2], c[3]) ;
  22. }


Version de base - les lignes de code assembleur qui m'intéressent

Code :
  1. .L20:
  2.     movaps a, %xmm0
  3.     decl %eax
  4.     movaps b, %xmm1
  5.     addps %xmm1, %xmm0
  6.     movaps %xmm0, c
  7.     jns .L20


Version bis

Code :
  1. /*
  2. * Compilation:
  3. *  
  4. *  asm > gcc -std=c99 -Os -msse -S -c ssebis.c
  5. *  exe > gcc -std=c99 -Os -msse -c ssebis.c -o ssebis       
  6. *
  7. */
  8. #include <stdio.h>
  9. #include <xmmintrin.h>
  10. union sse4 {
  11.     __m128 m ;
  12.     float values[4] ;
  13. } a,b,c ;
  14. int main(int argc, char* argv[])
  15. {
  16.     a.values[0] = 2.f ; a.values[1] = 3.f ; a.values[2] = 4.f ; a.values[3] = 5.f ;
  17.     b.values[0] = 5.f ; b.values[1] = 6.f ; b.values[2] = 7.f ; b.values[3] = 8.f ;
  18.     for ( int i = 0 ; i < 100000 ; ++i ) {
  19.         for ( int j = 0 ; j < 100000 ; ++j ) {
  20.             c.m = _mm_add_ps(a.m, b.m) ;
  21.         }
  22.     }
  23.     printf("%f %f %f %f\n", c[0], c[1], c[2], c[3]) ;
  24. }


Version bis - les lignes de code assembleur qui m'intéressent

Code :
  1. .L17:
  2.     movaps a, %xmm0
  3.     decl %eax
  4.     addps b, %xmm0
  5.     movaps %xmm0, c
  6.     jns .L17


 
Les performances pour les deux versions sont les mêmes en -Os, de même en -O3 en introduisant du travail dans la boucle (en changeant la valeur du vecteur a par exemple). (environ 10 secondes de "calculs" ).
 
N'ayant aucune expérience en assembleur, je me demande comment fonctionne l'instruction addps. D'après ce que j'ai compris (c'est là ou ca coince :p), elle prend en paramètre 2 registres sse afin de mettre le résultat dans un des deux (ici, xmm0, syntaxe AT/T). Or ici, b est un emplacement mémoire arbitraire dans cette version bis. Comme le programme fonctionne, que dois-je en conclure ? Il y a un movaps "caché" [:ddr555] ?  Y'a t'il quelque chose à savoir ou quelque chose qui ne va pas dans mon analyse ?
 
Question subsidiaire : Je n'ai pas l'impression que l'on puisse décider du registre sse à utiliser via les intrinsics, vous confirmez ?
 
Merci d'avance pour vos réponses ! @++
 
Edit : Il est dangereux de poster ce genre de sujet à 4h30 du matin [:banzai]. Je verrais bien ce que ca donne demain !


Message édité par Evadream -jbd- le 19-11-2004 à 05:17:57
Reply

Marsh Posté le 19-11-2004 à 04:36:55   

Reply

Marsh Posté le 19-11-2004 à 08:15:46    

non, rares sont les instructions prenant exclusivement deux registres. La plupart acceptent une operande en mémoire (ce que l'on apelle le load&execute)

Citation :


 
Question subsidiaire : Je n'ai pas l'impression que l'on puisse décider du registre sse à utiliser via les intrinsics, vous confirmez ?


 
yes sir !


Message édité par chrisbk le 19-11-2004 à 08:16:29
Reply

Marsh Posté le 19-11-2004 à 09:26:41    

Merci pour tes explications.
 
Y'a tellement de point d'interrogation dans mon premier post que je ne sais pas à quoi correspond ton "non" :)
 
Si j'ai bien compris, le load & execute permet d'utiliser des opérandes en mémoire.
Que se passe t'il derrière ? Les opérandes sont chargées dans des registres sse afin que l'opération se fasse bien en même temps sur mes 8 valeurs ? ("non" ? :D).
 
Si oui, est-ce une "bonne" méthode ou bien il est plus efficace de charger çà explicitement dans un registre sse ?
Si ca ne se fait pas comme çà, comment peut-on bénéficier de l'accélération SIMD de l'opération ?
 
C'est dommage de ne pas pouvoir contrôler "finement" l'utilisation des registres via les intrinsics, çà réduit leur champ d'application si on veut vraiment savoir ce qui se passe derrière. Par exemple pour améliorer "l'instruction pairing", ca ne me semble pas évident (lorsque j'ai effectué plusieurs _mm_add_ps_ entres des vecteurs indépendants, seul le registre xmm0 était utilisé). Me trompje où bien y'a t'il une façon d'utiliser les intrinsics "proprement" ?
 
Merci !


Message édité par Evadream -jbd- le 19-11-2004 à 09:33:34
Reply

Marsh Posté le 19-11-2004 à 09:40:33    

Evadream -jbd- a écrit :

Merci pour tes explications.
 
Y'a tellement de point d'interrogation dans mon premier post que je ne sais pas à quoi correspond ton "non" :)
 
Si j'ai bien compris, le load & execute permet d'utiliser des opérandes en mémoire.
 
Que se passe t'il derrière ? Les opérandes sont chargées dans des registres sse afin que l'opération se fasse bien en même temps sur mes 8 valeurs ? ("non" ? :D).


Non :o
 
c'est le CPU qui se demerde derriere
 
padds xmm0,xmm1;
padds xmm0,[eax + 20];
 
les 2 sont valids
 
 

Evadream -jbd- a écrit :


C'est dommage de ne pas pouvoir contrôler "finement" l'utilisation des registres via les intrinsics, çà réduit leur champ d'application si on veut vraiment savoir ce qui se passe derrière. Par exemple pour améliorer "l'instruction pairing", ca ne me semble pas évident (lorsque j'ai effectué plusieurs _mm_add_ps_ entres des vecteurs indépendants, seul le registre xmm0 était utilisé). Me trompje où bien y'a t'il une façon d'utiliser les intrinsics "proprement" ?
 
Merci !


 
Bin tu sais, si tu veux du plus fin fo faire de l'asm inline (les intrinsics te vire la gestion des regs, et c'est a peu pres tout)


---------------
NP: HTTP Error 764 Stupid coder found
Reply

Marsh Posté le 19-11-2004 à 10:20:30    

chrisbk a écrit :


...
Bin tu sais, si tu veux du plus fin fo faire de l'asm inline (les intrinsics te vire la gestion des regs, et c'est a peu pres tout)


 
:D Ok, merci pour toutes tes précisions !

Reply

Sujets relatifs:

Leave a Replay

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