Domanda

sto cercando di moltiplicare due vettori insieme dove ogni elemento di un vettore viene moltiplicato per l'elemento nello stesso indice all'altra vettore. Allora voglio sommare tutti gli elementi del vettore risultante per ottenere un numero. Per esempio, il calcolo sarebbe simile a questa per i vettori {1,2,3,4} e {5,6,7,8}:

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

In sostanza, sto prendendo il prodotto scalare di due vettori. So che c'è un comando SSE per fare questo, ma il comando non dispone di una funzione intrinseca ad esso associati. A questo punto, non voglio scrivere in linea di montaggio nel mio codice C, quindi voglio usare solo funzioni intrinseche. Questo mi sembra un comune di calcolo quindi sono sorpreso da me stesso che non riuscivo a trovare la risposta su Google.

Nota: Sto Ottimizzazione per una specifica micro architettura che supporta fino a SSE 4.2.

Grazie per il vostro aiuto.

È stato utile?

Soluzione

Se stai facendo un dot-prodotto di vettori più lunghi, moltiplica l'uso e _mm_add_ps regolare (o FMA) all'interno del ciclo interno. Salva la somma orizzontale fino alla fine.


Ma se si sta facendo un prodotto scalare di una sola coppia di vettori SIMD:

GCC (almeno la versione 4.3) include <smmintrin.h> con intrinseci livello SSE4.1, compresi i prodotti singola e doppia precisione dot:

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

Su Intel CPU tradizionali (non Atom / Silvermont) questi sono un po 'più veloce di farlo manualmente con più istruzioni.

Ma su AMD (compresi Ryzen), dpps è molto più lento. (Tabelle operazioni Vedere Agner Fog )


come ripiego per i processori più anziani, è possibile utilizzare questo algoritmo per creare il prodotto scalare dei vettori a e b:

__m128 r1 = _mm_mul_ps(a, b);

e poi somma orizzontale r1 utilizzando modo più veloce per farlo galleggiare orizzontale somma vettoriale su x86 (vedi qui per una versione commentata di questo, e perché è più veloce.)

__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);

Un lento costi alternativi 2 Confonde per hadd, che sarà facilmente collo di bottiglia sulla velocità riordino, in particolare su CPU Intel.

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

Altri suggerimenti

direi che il metodo più veloce SSE potrebbe essere:

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
}

Ho seguito -. modo più veloce per fare orizzontale Float Vector Sum Su x86

Ho scritto questo e compilato con 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];
}

E GCC 4.3.0 auto-vettorizzate esso:

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

Tuttavia, sarebbe farlo solo se ho usato un ciclo con un numero sufficiente iterazioni - altrimenti l'output dettagliato sarebbe chiarire che vettorializzazione era inutile o il ciclo era troppo piccola. Senza le parole chiave __restrict__ deve generare versioni separate, non vectorized per affrontare casi in cui il o uscita può puntare in uno degli ingressi.

vorrei incollare le istruzioni come esempio, ma dal momento che una parte del vettorializzazione srotolato il ciclo non è molto leggibile.

C'è un articolo da Intel qui che tocca il punto implementazioni -Product.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top