Quel est le moyen le plus efficace de multiplier 4 flotteurs par 4 avec SSE?
Question
J'ai actuellement le code suivant:
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 tout d'abord quelques questions:
(1) Si je voulais aligner les tableaux sur des limites de 16 octets, cela fonctionnerait-il? Puisque les tableaux sont alloués sur la pile, est-il vrai qu’il est presque impossible de les aligner?
voir la réponse sélectionnée pour ce message: Les variables de pile sont-elles alignées par le GCC __attribute __ ((aligné (x)) )?
(2) Le code pourrait-il être remanié pour le rendre plus efficace? Que se passe-t-il si je mets les deux tableaux flottants dans des registres plutôt qu’un seul?
Merci
La solution
si je voulais aligner les tableaux sur des limites de 16 octets, cela fonctionnerait-il? Puisque les tableaux sont alloués sur la pile, est-il vrai qu’il est presque impossible de les aligner?
Il est nécessaire que l’alignement sur la pile fonctionne. Sinon, les éléments intrinsèques ne fonctionneraient pas. Je suppose que le message que vous avez cité concerne la valeur exorbitante qu'il a sélectionnée pour la valeur d'alignement.
à 2:
Non, il ne devrait pas y avoir de différence de performance. Consultez ce site pour connaître le minutage des instructions de plusieurs processeurs.
Fonctionnement de l'alignement des variables de pile:
push ebp
mov ebp, esp
and esp, -16 ; fffffff0H
sub esp, 200 ; 000000c8H
Les et alignent le début de la pile sur 16 octets.
Autres conseils
Ecrivez-le en C, utilisez
gcc -S -mssse3
si vous avez une version assez récente de gcc.
(1) si je voulais aligner les tableaux sur des limites de 16 octets, cela fonctionnerait-il même? Puisque les tableaux sont alloués sur la pile, est-il vrai qu’il est presque impossible de les aligner?
Non, il est assez simple d'aligner le pointeur de pile en utilisant et
:
and esp, 0xFFFFFFF0 ; aligned on a 16-byte boundary
Toutefois, vous devez utiliser les ressources fournies par GCC, telles qu'un type 16 octets ou un __ attribut __
pour personnaliser l'alignement.
GCC prend-il en charge le type de données __ m128
? Si tel est le cas, c'est votre meilleur plan pour garantir un type de données aligné sur 16 octets. Néanmoins, __ attribut __ ((aligné (16)))
permet d’aligner des éléments. 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:)
L'utilisation intrinsèque est beaucoup plus rapide, en particulier avec l'optimisation. J'ai écrit un 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);
Horodatages de processeur version 50-60 intrinsèques Asm Version ~ 1000 timestamps proc
Vous pouvez le tester sur votre ordinateur
À 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 l'optimisation gcc ( -O2
, -O3
), il est possible que le travail soit plus rapide que l'ASM.