2009-08-04 7 views
2

J'ai actuellement le code suivant:Quel est le moyen le plus efficace de multiplier 4 flotteurs par 4 flottants en utilisant SSE?

float a[4] = { 10, 20, 30, 40 }; 
float b[4] = { 0.1, 0.1, 0.1, 0.1 }; 
asm volatile("movups (%0), %%xmm0\n\t" 
      "mulps (%1), %%xmm0\n\t"    
      "movups %%xmm0, (%1)"    
      :: "r" (a), "r" (b)); 

J'ai d'abord les quelques questions:

(1) si je devais aligner les tableaux sur les limites de 16 octets, ça marcherait même? Depuis les tableaux sont alloués sur la pile est-il vrai que leur alignement est presque impossible?

voir la réponse choisie pour ce poste: Are stack variables aligned by the GCC __attribute__((aligned(x)))?

(2) Le code pourrait être refactorisé du tout pour le rendre plus efficace? Que faire si je mets les deux tableaux flottants dans des registres plutôt qu'un seul?

Merci

Répondre

1

si je devais aligner les tableaux sur les limites de 16 octets, serait-il fonctionner même? Depuis les tableaux sont alloués sur la pile est-il vrai que leur alignement est presque impossible?

Il est nécessaire que l'alignement sur la pile fonctionne. Sinon, l'intrinsèque ne fonctionnerait pas. Je suppose que le post que vous avez cité a à voir avec la valeur exorbitante qu'il a choisie pour la valeur d'alignement.

-2:

Non, il ne devrait pas être une différence de performance. Voir ce site pour les horaires d'instruction de plusieurs processeurs.


Comment alignement des variables de pile fonctionne:

push ebp 
mov ebp, esp 
and esp, -16    ; fffffff0H 
sub esp, 200    ; 000000c8H 

Le et aligne la commencent de la pile à 16 octets.

1

Est-ce que GCC fournir un soutien pour le type de données __m128? Si oui, c'est votre meilleur plan pour garantir un type de données aligné sur 16 octets. Néanmoins, il y a __attribute__((aligned(16))) pour aligner les choses. Définissez vos tableaux comme suit

float a[4] __attribute__((aligned(16))) = { 10, 20, 30, 40 }; 
float b[4] __attribute__((aligned(16))) = { 0.1, 0.1, 0.1, 0.1 }; 

puis utilisez movaps à la place :)

+0

merci ; mais comme indiqué dans cet article http://stackoverflow.com/questions/841433/gcc-attributealignedx-explanation il semble impossible d'aligner les tableaux qui sont alloués sur la pile? (par opposition aux tableaux globaux alloués en .data) – horseyguy

+0

merci pour le correctif Bastien :) Banister ... pouvez-vous faire un essai et voir ce qui se passe? Si cela est lié à l'explication, alors il serait impossible d'aligner les choses comme doubler correctement, mais ils sont alignés. – Goz

+0

oui je vais bientôt ... J'ai le sentiment que l'explication liée est fausse, comme tout le monde dans cette question semble impliquer. Merci tout le monde! :) – horseyguy

1

(1) si je devais aligner les tableaux sur des limites de 16 octets, cela fonctionnerait-il même? Depuis les tableaux sont alloués sur la pile est-il vrai que leur alignement est presque impossible?

Non, il est assez simple d'aligner le pointeur de la pile à l'aide and:

and esp, 0xFFFFFFF0 ; aligned on a 16-byte boundary 

Mais vous devez utiliser ce GCC fournit, comme un type de 16 octets ou __attribute__ pour personnaliser l'alignement.

+0

merci pour votre réponse, seriez-vous en mesure de m'expliquer comment vous pouvez utiliser 'et' pour l'alignement? Je ne comprends pas tout à fait :) – horseyguy

+1

Rappelons que 'some_bit et 0 = 0' et' a/16 = a >> 4' si a est non signé. En utilisant 'et 'comme ceci, les quatre bits les moins significatifs seront mis à zéro et laisseront les autres inchangés. Que se passe-t-il si vous divisez 'esp' par 16, en fait? Il est décalé à droite de 4, et les quatre bits "perdus" sont le reste. Ainsi, ces quatre bits devraient être 0, de sorte que 'esp' est divisible par 16. Ce qui se passe réellement, c'est qu'il soustrait * au plus * 15, de sorte que' esp'% 16 == 0. (Soustraire de 'esp' signifie allouer plus d'espace sur la pile). –

7

écrire en C, utiliser

gcc -S -mssse3 

si vous avez une version assez récente de gcc.

+0

que le code C compilerait à ces instructions sse? Avez-vous un exemple? – horseyguy

+1

float a [4] = {10, 20, 30, 40}; Flotteur b [4] = {0,1, 0,1, 0,1, 0,1}; int foo (vide) { int i; pour (i = 0; i <4; i ++) a [i] * = b [i]; } Compilez comme indiqué et examinez le fichier .s. – xcramps

+0

intéressant, merci! – horseyguy

0

A propos du refactoring. Vous pouvez utiliser intrinsèque. Exemple:

#include <emmintrin.h> 

int main(void) 
{ 
    __m128 a1,b1; 

    a1=_mm_set_ps(10, 20,30,40); 
    b1=_mm_set_ps(0.1, 0.1, 0.1, 0.1); 

    a1=_mm_mul_ps(a1,b1); 

    return 0; 
} 

Avec gcc d'optimisation (-O2, -O3), il peut être un travail plus rapide, puis asm.

+0

combien plus rapide pensez-vous qu'il fonctionnerait? pourriez-vous comparer? – toxicate20

+0

voir prochain message, je suis le tester – AlekseyM

1

L'utilisation de intrinsic est beaucoup plus rapide en particulier avec l'optimisation. j'ai écrit test simple et comparer les deux versions (asm et intrinsèque)

unsigned long long time1; 
__m128 a1,b1; 


a1=_mm_set_ps(10, 20,30,40); 
b1=_mm_set_ps(0.1, 0.1, 0.1, 0.1); 
float a[4] = { 10, 20, 30, 40 }; 
float b[4] = { 0.1, 0.1, 0.1, 0.1 }; 

time1=__rdtsc(); 
a1=_mm_mul_ps(a1,b1); 
time1=__rdtsc() - time1 ; 
printf("Time: %llu\n",time1); 


time1=__rdtsc(); 
asm volatile("movups (%0), %%xmm0\n\t" 
       "mulps (%1), %%xmm0\n\t" 
       "movups %%xmm0, (%1)" 
       :: "r" (a), "r" (b)); 
time1=__rdtsc() - time1 ; 
printf("Time: %llu\n",time1); 

Version Intrinsèque 50-60 processeur horodatages Asm Version ~ 1000 proc horodatages

Vous pouvez le tester sur votre machine

Questions connexes