Frage

Ich versuche, zwei Vektoren zusammen zu multiplizieren, wobei jedes Element eines Vektors durch das Element in dem gleichen Index am anderen Vektor multipliziert wird. Ich möchte dann alle Elemente des resultierenden Vektors summieren eine Zahl zu erhalten. Zum Beispiel würde sich die Berechnung, wie dies für die Vektoren {1,2,3,4} und {5,6,7,8}:

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

Im Wesentlichen Ich nehme das Skalarprodukt der beiden Vektoren. Ich weiß, dass es ein SSE-Befehl ist, dies zu tun, aber der Befehl nicht über eine eigene Funktion, mit ihm verbunden ist. An dieser Stelle möchte ich nicht Inline-Assembler in meinem C-Code schreiben, so dass ich nur intrinsische Funktionen verwenden möchten. Dies scheint eine gemeinsame Berechnung so dass ich von mir selbst ist überrascht, dass ich nicht die Antwort auf Google finden konnte.

Hinweis: I für eine bestimmte Mikroarchitektur an Optimierung, die 4,2 bis SSE unterstützt werden.

Danke für Ihre Hilfe.

War es hilfreich?

Lösung

Wenn Sie ein Punktprodukt von mehr Vektoren tun, Verwendung multiplizieren und regelmäßigen _mm_add_ps (oder FMA) innerhalb der inneren Schleife. Speichern Sie die horizontale Summe bis zum Ende.


Aber wenn Sie tun, ein Punktprodukt von nur einem einzigen Paar von SIMD-Vektoren:

GCC (mindestens Version 4.3) enthält <smmintrin.h> mit SSE4.1- Ebene intrinsics, einschließlich der Einzel- und Doppelpräzisions-Punktprodukte:

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

Auf Intel Mainstream-CPUs (nicht Atom / Silvermont) diese sind etwas schneller als es manuell mit mehreren Anweisungen zu tun.

Aber auf AMD (einschließlich Ryzen), ist dpps deutlich langsamer. (Siehe Agner Fog Anweisung Tabellen )


Als Absicherung für ältere Prozessoren, können Sie diesen Algorithmus verwenden, um das Punktprodukt der Vektoren a und b zu erstellen:

__m128 r1 = _mm_mul_ps(a, b);

und dann horizontal Summe r1 mit Die schnellste Weg horizontal Schwimmer Vektorsumme auf x86 (siehe dort für eine kommentierte Version davon, und warum ist es schneller.)

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

Eine langsame Alternative kostet 2 Shuffles pro hadd, der auf Shuffle Durchsatz leicht Engpass, vor allem auf Intel-CPUs.

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

Andere Tipps

Ich würde sagen, die schnellste SSE Methode wäre:

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
}

Ich folgte -. schnellste Weg zu Do Horizontal Float Vector Sum Auf x86

Ich schrieb dieses und es mit gcc -O3 -S -ftree-vectorize -ftree-vectorizer-verbose=2 sse.c zusammengestellt

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];
}

Und GCC 4.3.0 Auto-vektorisiert es:

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

Allerdings wäre es nur tun, wenn ich eine Schleife mit genügend Iterationen verwendet - sonst wird die ausführliche Ausgabe, dass Vektorisierung war unrentabel klären würde oder die Schleife zu klein war. Ohne die __restrict__ Schlüsselwörter hat es getrennte, nicht vektorisiert Versionen solche Fälle zu behandeln zu erzeugen, wo die Ausgabe o in einem der Eingänge zeigen kann.

Ich würde die Anweisungen als Beispiel einzufügen, aber da ein Teil der Vektorisierung die Schleife entrollt es ist nicht sehr gut lesbar.

Es ist ein Artikel von Intel hier , der auf Punkt berührt -Produkt-Implementierungen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top