Jak obliczyć iloczyn wektorowy z wykorzystaniem funkcji wewnętrznych SSE w C

Staram się mnożyć dwa wektory razem, gdzie każdy element jednego wektora jest mnożony przez element w tym samym indeksie na drugim wektorze. Następnie chcę zsumować wszystkie elementy wektora wynikowego, aby uzyskać jedną liczbę. Na przykład obliczenia wyglądałyby tak dla wektorów {1,2,3,4} i {5,6,7,8}:

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

Zasadniczo biorę iloczyn punktowy dwóch wektorów. Wiem, że jest do tego polecenie SSE, ale polecenie nie mają z nią związaną wewnętrzną funkcję. W tym momencie nie chcę pisać inline assembly w moim kodzie C, więc chcę używać tylko funkcji wewnętrznych. Wydaje się to powszechną kalkulacją, więc jestem zaskoczony, że nie mogłem znaleźć odpowiedzi w Google.

Uwaga: optymalizuję pod kątem konkretnej mikro architektury, która obsługuje do SSE 4.2.

Dzięki za pomoc.

Author: Royi, 2010-11-08

4 answers

GCC (co najmniej Wersja 4.3) zawiera <smmintrin.h> z wewnętrznymi elementami poziomu SSE4.1, w tym produkty dot z pojedynczą i podwójną precyzją:

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

Jako alternatywa dla starszych procesorów, możesz użyć tego algorytmu do stworzenia iloczynu kropkowego wektorów a i b:

r1 = _mm_mul_ps(a, b);
r2 = _mm_hadd_ps(r1, r1);
r3 = _mm_hadd_ps(r2, r2);
_mm_store_ss(&result, r3);
 17
Author: caf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-08 04:33:39

Znajduje się artykuł Intela tutaj , który dotyka implementacji dot-product.

 3
Author: DennyRolling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-08 01:42:59

Napisałem to i skompilowałem z 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];
}

I GCC 4.3.0 auto-wektoryzował go:

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

Zrobiłoby to jednak tylko wtedy, gdybym użył pętli z wystarczającą liczbą iteracji - w przeciwnym razie obszerne wyjście wyjaśniałoby, że wektoryzacja była nieopłacalna lub pętla była zbyt mała. Bez słów kluczowych __restrict__ musi wygenerować oddzielne, Nie wektoryzowane wersje, aby poradzić sobie z przypadkami, w których wyjście o może wskazywać na jedno z wejść.

Wkleiłbym instrukcję jako przykład, ale ponieważ część wektoryzacji rozwijała pętlę nie jest zbyt czytelna.

 2
Author: Ben Jackson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-08 01:42:50

Powiedziałbym, że najszybsza Metoda SSE to:

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
}

I followed - najszybszy sposób, aby zrobić poziomą sumę wektora zmiennoprzecinkowego na x86 .

 1
Author: Royi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:20