Question

Je cherche à multiplier deux vecteurs ensemble où chaque élément d'un vecteur est multiplié par l'élément dans le même indice à l'autre vecteur. Je veux ensuite résumer tous les éléments du vecteur résultant pour obtenir un numéro. Par exemple, le calcul se présente comme suit pour les vecteurs {1,2,3,4} et {5,6,7,8}:

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

Pour l'essentiel, je prends le produit scalaire des deux vecteurs. Je sais qu'il ya une commande SSE à faire, mais la commande ne dispose pas d'une fonction intrinsèque associée. À ce stade, je ne veux pas écrire l'assembleur en ligne dans mon code C, donc je veux utiliser uniquement des fonctions intrinsèques. Cela semble être un calcul commun donc je suis surpris par moi-même que je ne pouvais pas trouver la réponse sur Google.

Note: Je suis une architecture pour l'optimisation de micro spécifique qui prend en charge jusqu'à SSE 4.2.

Merci pour votre aide.

Était-ce utile?

La solution

Si vous faites un point-produit des vecteurs plus, l'utilisation se multiplient et _mm_add_ps réguliers (ou FMA) dans la boucle intérieure. Enregistrer la somme horizontale jusqu'à la fin.


Mais si vous faites un produit scalaire d'une seule paire de vecteurs SIMD:

GCC (au moins la version 4.3) comprend <smmintrin.h> avec intrinsèques au niveau de l'SSE4.1, y compris les produits scalaires précision simple et double:

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

Sur Intel CPU Les principaux (pas Atom / Silvermont) ce sont un peu plus rapide que de le faire manuellement avec plusieurs instructions.

Mais AMD (y compris Ryzen), dpps est nettement plus lent. (Tables d'instructions Voir Agner Fog )


En tant que solution de repli pour les anciens processeurs, vous pouvez utiliser cet algorithme pour créer le produit scalaire des vecteurs a et b:

__m128 r1 = _mm_mul_ps(a, b);

puis r1 somme horizontale en utilisant moyen le plus rapide de faire la somme vectorielle de flotteur horizontal sur x86 (voir là pour une version commentée de cela, et pourquoi il est plus rapide.)

__m128 shuf   = _mm_shuffle_ps(r1, r1, _MM_SHUFFLE(2, 3, 0, 1));
__m128 sums   = _mm_add_ps(r1, shuf);
shuf          = _mm_movehl_ps(shuf, sums);
sums          = _mm_add_ss(sums, shuf);
float result =  _mm_cvtss_f32(sums);

A des coûts alternatifs lents 2 par mélange les hadd, qui goulot d'étranglement facilement sur le débit aléatoire, en particulier sur les processeurs Intel.

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

Autres conseils

Je dirais que la méthode la plus rapide SSE 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
}

suivi -. le plus rapide moyen de faire horizontale Vector Sum Sur Float x86

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

void f(int * __restrict__ a, int * __restrict__ b, int * __restrict__ c, int * __restrict__ d,
       int * __restrict__ e, int * __restrict__ f, int * __restrict__ g, int * __restrict__ h,
       int * __restrict__ 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 auto-vectorisés il:

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

Cependant, il ne le ferait que si j'utilisé une boucle avec suffisamment d'itérations - sinon la sortie verbeuse préciseraient que vectorisation n'a pas été rentable ou la boucle était trop petite. Sans les mots-clés __restrict__ il doit générer différentes, les versions non-vectorisé pour traiter les cas où la o de sortie peut indiquer dans l'une des entrées.

Je coller les instructions à titre d'exemple, mais comme une partie de la vectorisation la boucle, il déroula n'est pas très lisible.

Il y a un article par Intel qui touche le point implémentations -product.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top