Esta publicación está estrechamente relacionada con otra que publiqué some days ago. Esta vez, escribí un código simple que simplemente agrega un par de matrices de elementos, multiplica el resultado por los valores en otra matriz y lo almacena en una cuarta matriz, todas las variables de precisión doble de tipo punto flotante.GCC SSE código de optimización
Hice dos versiones de ese código: uno con instrucciones SSE, usando llamadas ay otro sin ellas, luego las compilé con gcc y -O0 nivel de optimización. Las escribo a continuación:
// SSE VERSION
#define N 10000
#define NTIMES 100000
#include <time.h>
#include <stdio.h>
#include <xmmintrin.h>
#include <pmmintrin.h>
double a[N] __attribute__((aligned(16)));
double b[N] __attribute__((aligned(16)));
double c[N] __attribute__((aligned(16)));
double r[N] __attribute__((aligned(16)));
int main(void){
int i, times;
for(times = 0; times < NTIMES; times++){
for(i = 0; i <N; i+= 2){
__m128d mm_a = _mm_load_pd(&a[i]);
_mm_prefetch(&a[i+4], _MM_HINT_T0);
__m128d mm_b = _mm_load_pd(&b[i]);
_mm_prefetch(&b[i+4] , _MM_HINT_T0);
__m128d mm_c = _mm_load_pd(&c[i]);
_mm_prefetch(&c[i+4] , _MM_HINT_T0);
__m128d mm_r;
mm_r = _mm_add_pd(mm_a, mm_b);
mm_a = _mm_mul_pd(mm_r , mm_c);
_mm_store_pd(&r[i], mm_a);
}
}
}
//NO SSE VERSION
//same definitions as before
int main(void){
int i, times;
for(times = 0; times < NTIMES; times++){
for(i = 0; i < N; i++){
r[i] = (a[i]+b[i])*c[i];
}
}
}
Al compilar con -O0, gcc hace uso de registros XMM/MMX y SSE intstructions, si no se da específicamente el opciones -mno-sse (y otros). Inspeccioné el código de ensamblado generado para el segundo código y noté que usa movsd, suma y mulsd instrucciones. Por lo tanto, utiliza las instrucciones de SSE, pero solo las que usan la parte más baja de los registros, si no estoy equivocado. El código de ensamblado generado para el primer código C hizo uso, como se esperaba, de las instrucciones addp y mulpd, aunque se generó un código de ensamblaje bastante más grande.
De todos modos, el primer código debería obtener un mejor beneficio, hasta donde yo sé, del paradigma SIMD, ya que cada iteración dos valores de resultado se calculan. Aún así, el segundo código realiza algo como un 25 por ciento más rápido que el primero. También realicé una prueba con valores de precisión simples y obtuve resultados similares. ¿Cuál es la razón para eso?
Comparar el rendimiento al compilar sin optimizaciones es bastante insignificante. – interjay
Está realizando 3 cargas xy 1 x tienda solo por 2 operaciones aritméticas, por lo que lo más probable es que tenga un ancho de banda limitado. –
¿Qué sucede cuando elimina las llamadas _mm_prefetch? Creo que pueden lastimarte – TJD