Adicionalmente, si estais interesados en la programación de videojuegos, motores de juegos y matemáticas y demás frikadas podeis echarle un ojo a mi otro blog : http://lordpakus.blogspot.com.es/

jueves, 8 de septiembre de 2011

Math Engine : Capitulo 6. Vector.distance con SSE (x10 al rendimiento)

El artículo original lo podeís encontrar aquí

Computacionalmente hablando 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 (algo más complejo que una simple suma de 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!!!.

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

No hay comentarios:

Publicar un comentario