2010-11-08 8 views
9

Estoy tratando de multiplicar dos vectores juntos donde cada elemento de un vector se multiplica por el elemento en el mismo índice en el otro vector. Luego quiero sumar todos los elementos del vector resultante para obtener un número. Por ejemplo, el cálculo se vería así para los vectores {1,2,3,4} y {5,6,7,8}:Cómo calcular el producto vector punto utilizando funciones intrínsecas SSE en C

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

Esencialmente, estoy tomando el producto punto de los dos vectores. Sé que hay un comando SSE para hacer esto, pero el comando no tiene una función intrínseca asociada. En este punto, no quiero escribir ensambles en línea en mi código C, por lo que quiero usar solo funciones intrínsecas. Esto parece un cálculo común, así que estoy sorprendido de que no haya encontrado la respuesta en Google.

Nota: Estoy optimizando para una micro arquitectura específica que admite hasta SSE 4.2.

Gracias por su ayuda.

Respuesta

15

GCC (al menos la versión 4.3) incluye <smmintrin.h> con las características intrínsecas de nivel SSE4.1, incluyendo los puntos individuales y de doble precisión los productos

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

como un mensaje para los procesadores anteriores, se pueden utilizar este algoritmo para crear el producto escalar de los vectores a y b:

r1 = _mm_mul_ps(a, b); 
r2 = _mm_hadd_ps(r1, r1); 
r3 = _mm_hadd_ps(r2, r2); 
_mm_store_ss(&result, r3); 
+7

Como nota, me gustaría señalar que calcular el producto Dot usando el _dp_ intrinsic es más lento que hacerlo de la segunda manera. –

+0

@SergueiFedorov que depende completamente de su hardware, no hay un caso global que sea más lento. – CoffeeandCode

+0

Creo que hay mejores formas para la suma horizontal que usar '_mm_hadd_ps'. Ver http://stackoverflow.com/a/35270026/195787. – Royi

2

escribí esto y compilado 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]; 
} 

y GCC 4.3.0 auto-vectorizado que:

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

Sin embargo, sólo lo haría si usara un bucle con suficiente iteraciones: de lo contrario, la salida detallada aclararía que la vectorización no era rentable o que el ciclo era demasiado pequeño. Sin las palabras clave __restrict__ tiene que generar versiones separadas, no vectorizadas para tratar casos en los que la salida o puede apuntar a una de las entradas.

Pegaría las instrucciones como un ejemplo, pero como parte de la vectorización desenrolló el bucle no es muy legible.

+0

Creo que quiso decir algo más. Me gusta 2 arreglos de 4 elementos cada uno. Lo que haces aquí es otra cosa. Algo así como un producto escalar de una variedad de vectores. – Royi

3

Hay un artículo de Intel here que se refiere a las implementaciones de productos dot.

1

yo diría que el método más rápido sería SSE:

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 
} 

Seguí - Fastest Way to Do Horizontal Float Vector Sum On x86.

+0

Gran hallazgo, las instrucciones hadd se expanden a varios uops. – caf

Cuestiones relacionadas