Capítulo perteneciente al math engine LordPakus
Hola a todos
No se si recordareis que Thathi me pidió en el capitulo 2 que implementara la distancia entre dos vectores. y que tal y como os dije en el capitulo 5 cuanto más compleja es la operación más mejora de rendimiento se puede llegar a conseguir.
Pues bien, concretamente para la optimización SSE, para el caso de la distancia entre dos vectores se ha conseguido unos valores de tiempo de :
Version sin optimizar Tiempo gastado: 680milisegundos
Version optimizada con SSE Tiempo gastado: 68milisegundos
Es decir se ha CONSEGUIDO MULTIPLICAR POR 10 EL RENDIMIENTO!!!. (os lo dije :D)
Aqui os dejo el código para que veais como lo he hecho:
float LPVector::distanceOld(LPVector vector)
{
float a = 0.0;
float temp = 0.0;
//Si las dimensiones son diferentes , tenemos un problema grave.
if (n != vector.n)
{
cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
return (-1.0);
}
for(int i = 0 ; i < n ; ++i)
{
temp = (v[i] - vector.v[i]);
a += ( temp * temp ) ;
}
return sqrt(a);
}
float LPVector::distanceSSE(LPVector vector)
{
//Fast SSE code when float specified
float b[4];
float* const row0 = (float*) &v[0];
float* const row1 = (float*) &(vector.v[0]);
float* const a = (float*) &b[0];
float temp = 0.0;
int i = 0;
//Si las dimensiones son diferentes , tenemos un problema grave.
if (n != vector.n)
{
cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
return (-1.0);
}
__asm
{
// Carga trozos de los vectores de 4 en 4
// La carga se hace "desordenada para que siempre haya una instrucción como mínimo entre uso y uso de registros."
mov edx, row0
mov esi, row1
mov edi, a
//Al hacer una XOR ponemos el registro a 0 más rapidamente que hacciendo un mov de 0
//El registro xmm7 (el último) lo ponemos de variable acumuladora de resultado
xorps xmm7, xmm7
}
i = n;
while( i >= 12 )
{
__asm
{
//Cargamos la info en los registros SSE
movups xmm0, [edx]
movups xmm1, [esi]
//Aumentamos los contadores en 16 = 4 elementos * 4 bytes por elemento (float)
add edx,16
add esi,16
//Cargamos la info en los registros SSE(tenemos 8 registros donde guardar info)
movups xmm2, [edx]
movups xmm3, [esi]
//Aumentamos los contadores en 16 = 4 elementos * 4 bytes por elemento (float)
add edx,16
add esi,16
//Cargamos la info en los registros SSE(tenemos 8 registros donde guardar info)
movups xmm4, [edx]
movups xmm5, [esi]
//Aumentamos los contadores en 16 = 4 elementos * 4 bytes por elemento (float)
add edx,16
add esi,16
//Hacemos la operación que toca, en nuestro caso, restar , elevar el resultado al cuadrado y acumular el resultado
subps xmm0 , xmm1 //Restamos de 4 en 4
subps xmm2 , xmm3 //Restamos de 4 en 4
subps xmm4 , xmm5 //Restamos de 4 en 4
//Elevamos al cuadrado la diferencia
mulps xmm0,xmm0
mulps xmm2,xmm2
mulps xmm4,xmm4
//Sumamos resultados parciales al total (xmm7)
addps xmm7,xmm0
addps xmm7,xmm2
addps xmm7,xmm4
}
i -= 12;
}
__asm
{
movups [edi], xmm7
}
b[0] += (b[1] + b[2] + b[3]);
//Los flecos los rematamos de la manera tradicional
for(int j = n-i ; j < n ; ++j)
{
temp = (v[i] - vector.v[i]);
b[0] += temp*temp;
}
return sqrt(b[0]);
}
int main (int argc, char* argv[])
{
LPVector v1,v2;
float d1,d2;
clock_t timer;
float delay;
//Creamos los vectores aleatorios
v1.random(DIM,0.0f,100.0f);
v2.random(DIM,0.0f,100.0f);
//Sumamos los vectores
cout << "Version sin optimizar\n";
timer = clock();
for(int i=0 ; i < 100000 ; i++ )
d1 = v1.distanceOld ( v2 );
delay = ( clock() - timer ) ;
cout << "Tiempo gastado: " << delay << "milisegundos\n";
cout << "Version optimizada con SSE\n";
timer = clock();
for(int i=0 ; i < 100000 ; i++ )
d2 = v1.distanceSSE ( v2 );
delay = ( clock() - timer ) ;
cout << "Tiempo gastado: " << delay << "milisegundos\n";
if ( d1 != d2)
cout << "Error: La operacion da diferente resultado!!!\n";
if ( d1 == d2)
cout << "Calculo correcto\n";
//Eliminamos los vectores
v1.Delete();
v2.Delete();
system("PAUSE");
}
Si hay algo que no entendais o quereis que implemente en SSE alguna función en especial no dudeis en hacerme llegar vuestras sugerencias o preocupaciones...
Espero que os haya gustado y que hayais aprendido...
Nos vemos
Blog de programación enfocado a estudiantes principiantes de C/C++ en español. Dispone de cursos de todos los niveles y para multitud de usos.
Entradas populares
-
Una pregunta que me hacen en muchas ocasiones es ¿¿qué significa %2?? La respuesta tiene dos acepciones en función de si lo estamos u...
-
<< Ejemplo anterior Artículos Relacionados Ejemplo siguiente >> Hola a todos, ASCII Art es el hecho de hacer di...
-
Articulo perteneciente a : Referencias de programación Hola a todos Os pongo una aportación que a más de uno le irá bien, un resumen de ...
-
<< Ejemplo anterior Artículos Relacionados Ejemplo siguiente >> Hola a todos, El ejercicio de hoy se basa en c...
-
Capítulo perteneciente al tutorial de opengl desde cero Hola a todos, Este capítulo tal vez es de lo más complicados de la teoría necesa...
-
<< Capítulo anterior Artículos Relacionados Capítulo siguiente >> Hola a todos, Este tutorial intenta ser e...
-
Hola a todos... He aquí la primera entrega "Como crear tu propio motor de videojuegos y no morir en el intento". Antes que nada ...
-
<< Capítulo anterior Artículos Relacionados Capítulo siguiente >> Hola a todos... Un compañero vuestro ha...
-
Hola a todos Ya que he recibido un par de mails pidiéndome información sobre SEO, os adjunto unos cuantos trucos que he ido aprendiendo du...
-
<< Capítulo anterior Artículos Relacionados Capítulo siguiente >> El c apitulo de hoy trata sobre las instrucc...
No me esperaba este rendimiento, es la ostia!!!
ResponderEliminarxDDD
Pues con matrices(que son operaciones más costosas) y paralelización con threads la mejora de rendimiento se puede disparar hasta x50 en casos muy especificos.... en otro momento os enseñaré gráficas de elaboración propia que tengo por aquí de comparación de rendimientos...
ResponderEliminarUna duda que me entra ahora, en un juego normal 3D, donde tienes que pintar varias meses (decírselo a la gráfica), hacer el update de toda la escena, etc... ¿Se notaria la diferencia entre tener el código de operaciones en SSE o no? o ¿la mejora rendimiento no se notaria porque en comparación a las otras operaciones no es significativa?
ResponderEliminarYa sé que es un poco subjetiva, que depende del juego, pero bueno ahí lo dejo
Venga nos vemos!!
Se tendria que probar, pero en principio no se notaria mucho. Se notaria mucho en el calculo de físicas, de la inteligencia artificial, etc... teniendo tarjeta gráfica , gráficos y SSE son mala combinación (otra cosa diferente seria que no tuvieras tarjeta gráfica, que entonces se notaria muchisimo,pero es una situación que dudo que exista...)
ResponderEliminar