Mostrando entradas con la etiqueta improvement SSE. Mostrar todas las entradas
Mostrando entradas con la etiqueta improvement SSE. Mostrar todas las entradas

lunes, 5 de septiembre de 2011

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

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

Entradas populares