Comment calculer le produit vectoriel à l’aide des fonctions insortingnsèques de SSE en C

J’essaie de multiplier deux vecteurs ensemble où chaque élément d’un vecteur est multiplié par l’élément du même index sur l’autre vecteur. Je veux ensuite additionner tous les éléments du vecteur résultant pour obtenir un nombre. Par exemple, le calcul ressemblerait à ceci pour les vecteurs {1,2,3,4} et {5,6,7,8}:

1 * 5 + 2 * 6 + 3 * 7 + 4 * 8

Je prends essentiellement le produit scalaire des deux vecteurs. Je sais qu’il existe une commande SSE pour le faire, mais aucune fonction insortingnsèque n’y est associée. À ce stade, je ne veux pas écrire d’assembly inline dans mon code C, je souhaite donc utiliser uniquement des fonctions insortingnsèques. Cela semble être un calcul courant, donc je suis surpris de ne pas pouvoir trouver la réponse sur Google.

Remarque: J’optimise pour une micro-architecture spécifique prenant en charge SSE 4.2.

Merci de votre aide.

GCC (au moins la version 4.3) inclut avec les éléments insortingnsèques de niveau SSE4.1, y compris les produits scalaires à simple et double précision:

 _mm_dp_ps (__m128 __X, __m128 __Y, const int __M); _mm_dp_pd (__m128d __X, __m128d __Y, const int __M); 

Comme solution de secours pour les processeurs plus anciens, vous pouvez utiliser cet algorithme pour créer le produit scalaire des vecteurs a et b :

 r1 = _mm_mul_ps(a, b); r2 = _mm_hadd_ps(r1, r1); r3 = _mm_hadd_ps(r2, r2); _mm_store_ss(&result, r3); 

Il existe ici un article d’Intel qui traite des implémentations de produits scalaires.

J’ai écrit ceci et l’ai compilé avec gcc -O3 -S -ftree-vectorize -ftree-vectorizer-verbose=2 sse.c

 void f(int * __ressortingct__ a, int * __ressortingct__ b, int * __ressortingct__ c, int * __ressortingct__ d, int * __ressortingct__ e, int * __ressortingct__ f, int * __ressortingct__ g, int * __ressortingct__ h, int * __ressortingct__ o) { int i; for (i = 0; i < 8; ++i) o[i] = a[i]*e[i] + b[i]*f[i] + c[i]*g[i] + d[i]*h[i]; } 

Et GCC 4.3.0 l'a auto-vectorisé:

 sse.c:5: note: LOOP VECTORIZED. sse.c:2: note: vectorized 1 loops in function. 

Cependant, cela ne serait possible que si j'utilisais une boucle avec suffisamment d'itérations - sinon, la sortie commentée clarifierait que la vectorisation n'était pas rentable ou que la boucle était trop petite. Sans les mots clés __ressortingct__ il doit générer des versions distinctes, non vectorisées pour traiter les cas où la sortie o peut pointer vers l'une des entrées.

Je voudrais coller les instructions à titre d'exemple, mais depuis qu'une partie de la vectorisation a déroulé la boucle, ce n'est pas très lisible.

Je dirais que la méthode SSE la plus rapide serait:

 static inline float CalcDotProductSse(__m128 x, __m128 y) { __m128 mulRes, shufReg, sumsReg; mulRes = _mm_mul_ps(x, y); // Calculates the sum of SSE Register - https://stackoverflow.com/a/35270026/195787 shufReg = _mm_movehdup_ps(mulRes); // Broadcast elements 3,1 to 2,0 sumsReg = _mm_add_ps(mulRes, shufReg); shufReg = _mm_movehl_ps(shufReg, sumsReg); // High Half -> Low Half sumsReg = _mm_add_ss(sumsReg, shufReg); return _mm_cvtss_f32(sumsReg); // Result in the lower part of the SSE Register } 

J’ai suivi – le moyen le plus rapide de réaliser une sum vectorielle de flotteurs horizontaux sur x86 .