2013-07-20 17 views
8

Jak uzyskać elementy sumujące (redukcję) wektora zmiennoprzecinkowego za pomocą sse intrinsics?Redukcja SSE wektora zmiennoprzecinkowego

prosty kod seryjny

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

Czy próbowałeś czegoś? – harold

+2

Czy rzeczywiście spojrzałeś na wygenerowany kod? Przynajmniej moim doświadczeniem z gcc jest to, że wykonuje on całkiem dobrą robotę przy wykonywaniu instrukcji SSE, ale może wymagać -O3. –

Odpowiedz

14

Zazwyczaj generowania 4 częściowych obliczeń w pętli, a następnie po prostu zsumować poziomo w 4 elementów po pętli, np

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

Uwaga: na powyższym przykładzie a musi być 16 bajty i n musi być wielokrotnością liczby 4, jeśli wyrównanie a nie można zagwarantować wtedy użyć _mm_loadu_ps zamiast _mm_load_ps. Jeśli n nie ma pewności, że jest wielokrotnością 4, dodaj pętlę skalarną na końcu funkcji, aby zebrać wszystkie pozostałe elementy.

+1

Jeśli tablica wejściowa jest potencjalnie duża, warto mieć pętlę skalarną na początku, która działa 0-3 razy, dopóki wejście nie zostanie ustawione na granicy 16B dla pętli SSE. Wtedy nie będziesz mieć ładunków, które przekraczają pamięć podręczną/linie strony spowalniające twoją pętlę. I może używać 'ADDPS' z operandem pamięci, który może potencjalnie mikro-fuse, zmniejszając narzut. Możesz również uzyskać 2 lub 4 łańcuchy zależności, używając wielu akumulatorów, aby Twoja pętla mogła utrzymać 1 wektor FP dodany na cykl, zamiast 1 na (opóźnienie 'ADDPS' = 3). –