2013-07-20 7 views
8

Wie kann ich Summenelemente (Reduktion) von Float-Vektor mit sse-intrinsics bekommen?SSE-Reduktion von Float-Vektor

Einfache Seriencode:

void(float *input, float &result, unsigned int NumElems) 
{ 
    result = 0; 
    for(auto i=0; i<NumElems; ++i) 
     result += input[i]; 
} 
+3

Haben Sie etwas versucht? – harold

+2

Haben Sie sich den generierten Code angesehen? Zumindest meine Erfahrung mit gcc ist, dass es einen ziemlich guten Job macht, SSE-Anweisungen zu machen, wenn es möglich ist - aber es kann -O3 erfordern. –

Antwort

14

Normalerweise erzeugen Sie 4 Teilsummen in der Schleife und dann nur horizontal nach der Schleife über die 4 Elemente zusammenzufassen, z.B.

#include <cassert> 
#include <cstdint> 
#include <emmintrin.h> 

float vsum(const float *a, int n) 
{ 
    float sum; 
    __m128 vsum = _mm_set1_ps(0.0f); 
    assert((n & 3) == 0); 
    assert(((uintptr_t)a & 15) == 0); 
    for (int i = 0; i < n; i += 4) 
    { 
     __m128 v = _mm_load_ps(&a[i]); 
     vsum = _mm_add_ps(vsum, v); 
    } 
    vsum = _mm_hadd_ps(vsum, vsum); 
    vsum = _mm_hadd_ps(vsum, vsum); 
    _mm_store_ss(&sum, vsum); 
    return sum; 
} 

Hinweis: für das obige Beispiel a muss 16 Byte ausgerichtet sein und n muss ein Vielfaches von 4 sein, wenn die Ausrichtung der a kann dann nicht _mm_loadu_ps statt _mm_load_ps verwenden garantiert werden. Wenn n nicht garantiert ein Vielfaches von 4 ist, fügen Sie am Ende der Funktion eine Skalarschleife hinzu, um alle verbleibenden Elemente zu akkumulieren.

+1

Wenn das Eingabearray potenziell groß ist, lohnt es sich auch, am Anfang eine Skalarschleife zu verwenden, die 0-3 Mal ausgeführt wird, bis der Eingang an einer 16B-Grenze für die SSE-Schleife ausgerichtet ist. Dann haben Sie keine Lasten, die Cache/Seitenzeilen kreuzen, die Ihre Schleife verlangsamen. Und es kann "ADDPS" mit einem Speicheroperanden verwenden, der möglicherweise mikrofusioniert und Overhead reduziert. Sie können auch 2 oder 4 Abhängigkeitsketten durchlaufen, indem Sie mehrere Akkumulatoren verwenden, so dass Ihre Schleife 1 Vektor-FP-Add pro Zyklus erhalten kann, anstatt 1 pro (Latenz von "ADDPS" = 3). –