¿Cuál es la forma más eficiente de multiplicar 4 flotadores por 4 flotadores utilizando SSE?
Pregunta
Actualmente tengo el siguiente código:
float a[4] = { 10, 20, 30, 40 };
float b[4] = { 0.1, 0.1, 0.1, 0.1 };
asm volatile("movups (%0), %%xmm0\n\t"
"mulps (%1), %%xmm0\n\t"
"movups %%xmm0, (%1)"
:: "r" (a), "r" (b));
Primero tengo algunas preguntas:
(1) si FUERA a alinear las matrices en límites de 16 bytes, ¿funcionaría? Dado que las matrices están asignadas en la pila, ¿es cierto que alinearlas es casi imposible?
vea la respuesta seleccionada para esta publicación: ¿Las variables de la pila están alineadas por el __attribute __ de GCC ((alineado (x)) )?
(2) ¿Podría el código ser refactorizado para hacerlo más eficiente? ¿Qué sucede si pongo ambas matrices flotantes en registros en lugar de solo una?
Gracias
Solución
si quisiera alinear las matrices en límites de 16 bytes, ¿funcionaría? Dado que las matrices están asignadas en la pila, ¿es cierto que alinearlas es casi imposible?
Se requiere que la alineación en la pila funcione. De lo contrario, los intrínsecos no funcionarían. Supongo que la publicación que citó tenía que ver con el valor exorbitante que seleccionó para el valor de alineación.
a 2:
No, no debería haber una diferencia en el rendimiento. Consulte este sitio para conocer los tiempos de instrucción de varios procesadores.
Cómo funciona la alineación de las variables de la pila:
push ebp
mov ebp, esp
and esp, -16 ; fffffff0H
sub esp, 200 ; 000000c8H
y alinean el comienzo de la pila a 16 bytes.
Otros consejos
Escríbelo en C, usa
gcc -S -mssse3
si tiene una versión bastante reciente de gcc.
(1) si quisiera alinear las matrices en límites de 16 bytes, ¿funcionaría? Dado que las matrices están asignadas en la pila, ¿es cierto que alinearlas es casi imposible?
No, es bastante simple alinear el puntero de la pila usando y
:
and esp, 0xFFFFFFF0 ; aligned on a 16-byte boundary
Pero debe usar lo que proporciona GCC, como un tipo de 16 bytes, o __attribute__
para personalizar la alineación.
¿GCC proporciona soporte para el tipo de datos __m128
? Si es así, ese es su mejor plan para garantizar un tipo de datos alineados de 16 bytes. Sin embargo, hay __attribute __ ((alineado (16)))
para alinear cosas. Defina sus matrices de la siguiente manera
float a[4] __attribute__((aligned(16))) = { 10, 20, 30, 40 };
float b[4] __attribute__((aligned(16))) = { 0.1, 0.1, 0.1, 0.1 };
y luego usa movaps :)
Usar intrínseco es mucho más rápido, especialmente con la optimización. Escribí una prueba simple y comparo ambas versiones (asm e intrínseca)
unsigned long long time1;
__m128 a1,b1;
a1=_mm_set_ps(10, 20,30,40);
b1=_mm_set_ps(0.1, 0.1, 0.1, 0.1);
float a[4] = { 10, 20, 30, 40 };
float b[4] = { 0.1, 0.1, 0.1, 0.1 };
time1=__rdtsc();
a1=_mm_mul_ps(a1,b1);
time1=__rdtsc() - time1 ;
printf("Time: %llu\n",time1);
time1=__rdtsc();
asm volatile("movups (%0), %%xmm0\n\t"
"mulps (%1), %%xmm0\n\t"
"movups %%xmm0, (%1)"
:: "r" (a), "r" (b));
time1=__rdtsc() - time1 ;
printf("Time: %llu\n",time1);
Marcas de tiempo del procesador de la versión intrínseca 50-60 Versión de Asm ~ 1000 marcas de tiempo de proceso
Puedes probarlo en tu máquina
Sobre la refactorización. Puedes usar intrínseco. Ejemplo:
#include <emmintrin.h>
int main(void)
{
__m128 a1,b1;
a1=_mm_set_ps(10, 20,30,40);
b1=_mm_set_ps(0.1, 0.1, 0.1, 0.1);
a1=_mm_mul_ps(a1,b1);
return 0;
}
Con la optimización gcc ( -O2
, -O3
) puede funcionar más rápido que asm.