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

Math Engine : Capitulo 5. LPVector con SSE(I).

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

Hola a todos....

Aquí os dejo la implementación de la suma de vectores mediante SSE.

int main (int argc, char* argv[])
{
    LPVector v1,v2,v3;
    clock_t timer;
    float delay;

    //Creamos los vectores aleatorios
    v1.random(DIM,0.0f,500.0f);
    v2.random(DIM,0.0f,500.0f);
    v3 = v1;

    //Sumamos los vectores
    cout << "Version sin optimizar\n";
    timer = clock();
    v1.addOld ( v2 );
    delay = ( clock() - timer ) ;
    cout << "Tiempo gastado: " << delay << "milisegundos\n";

    cout << "Version optimizada con SSE\n";
    timer = clock();
    v3.addSSE ( v2 );
    delay = ( clock() - timer ) ;
    cout << "Tiempo gastado: " << delay << "milisegundos\n";

    if ( v1 != v3)
        cout << "Error: La operacion da diferente resultado!!!\n";

    if ( v1 == v3)
        cout << "Calculo correcto\n";

    //Eliminamos los vectores
    v1.Delete();
    v2.Delete();
    v3.Delete();

    system("PAUSE");
}

void LPVector::addOld(LPVector vector)                //Operación de += modo antiguo (solo C)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] += vector.v[i];
}

void LPVector::addSSE(LPVector vector)                //Operación de += modo ensamblador SSE
{
    //Fast SSE code when float specified
    float* const row0 = (float*) &v[0];
    float* const row1 = (float*) &(vector.v[0]);
    int i = 0;

    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return;
    }

    __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
    }

    i = n;
   
    while( i >= 8 )
    {
        __asm
        {
            //Copiamos el puntero al vector v donde almacenaremos el valor final
            mov        edi,    edx

            //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]

       
            //Hacemos la operación que toca, en nuestro caso, sumar
            addps    xmm0 , xmm1    //Sumamos de 4 en 4
            addps    xmm2 , xmm3    //Sumamos de 4 en 4

            //En edi y edx es donde teniamos cargado los trozo de vector v y es donde pondremos el resultado
            movups     [edi], xmm0
            movups   [edx], xmm2

            //Aumentamos los contadores en 16 = 4 elementos * 4 bytes por elemento (float)
            add     edx,16
            add  esi,16
        }
        i -= 8;
    }
   
    //Los flecos los rematamos de la manera tradicional
    for(int j = n-i ; j < n ; ++j)
        v[j] += vector.v[j];
}

Al ejecutar todo este código tendreis una salida de este estilo:
Version sin optimizar
Tiempo gastado: 321 milisegundos
Version optimizada con SSE
Tiempo gastado : 240 milisegundos
Calculo correcto.

Podreis observar que la ganancia no es comparable a multiplicar por 4 la velocidad como predicen las operaciones SSE sino que se queda en un 25-30% de mejora. Esto es debido a que las operaciones de entrada-salida (movups) gastan muchos recursos y hacen que las operaciones sencillas ( por ejemplo un suma sencilla ) se vean menos mejoradas que las operaciones complicadas ( muchos cálculos y pocos accesos a memoria ).

Espero que os guste, nos vemos

Math Engine : Capitulo 4. Clase LPData

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


Bienvenidos a un nuevo capitulo de como crear tu propio math engine. El capitulo de hoy es la presentación del tercer (y si no estoy equivocado, último) tipo de dato que se usará en el math engine: el LPData es decir, series de datos. Con series de datos se representarán tanto muestreo de señales (con lo que se podrán realizar FFT  y otras transformaciones, de audio sobretodo) como series de datos estadísticos o muestreo de funciones. (maximos, mínimos, medias, rectas de regresión, integración, derivación,etc....)

Por ahora solo os pongo la estructura, que funciones se irán implementando lo dejo para vosotros para que me lo vayais diciendo a medida que las vayais necesitando.

LPData.h:
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/


/**************************************************************************************************/
// LPData.h
/**************************************************************************************************/

#ifndef __LPData__
#define __LPData__

class LPData
{
public:
    LPData();
    ~LPData();

    //Funciones anexas.
    void Set(int t, float* vector);                //Funcion para inicializar un vector de n elementos
    void Delete();                                //Función para limpiar la variable vector
    void print();                                //Función para imprimir el vector por la consola
    void random(int t,float min, float max);    //Función para rellenar de random un vector de t posiciones

    float Max();
    float Min();
    float Average();

    //OPERACIONES DE AMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
    //Operaciones vectoriales
    void operator +=(LPData vector);           
    void operator -=(LPData vector);   
   
    //Operaciones con escalares ( sumar , restar , etc ... todo un vector por un escalar)
    void operator +=(float scal);
    void operator -=(float scal);
    void operator *=(float scal);
    void operator /=(float scal);


    //OPERACIONES QUE DEVUELVEN ALGO (NO ACUMULADORES: +,-,*, etc...)

public:
    int n;         //número de elementos del vector
    float *v;    //puntero a memoria dinámica
};

#endif

LPData.cpp:
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

#include "LPData.h"
#include <math.h>
#include <iostream>                //Usada para imprimir por consola

using namespace std;

//Constructor
LPData::LPData()
{
    n = 0;
    v = NULL;
}

//Destructor
LPData::~LPData()
{
    //if (n)
    //{
    //    if( v != NULL )
    //        free(v);
    //}
}

//Funcion para inicializar una serie de datos de n elementos
void LPData::Set(int t, float* vector)
{
    n = t; //Inicializamos el número de elementos del vector

    //Creamos un vector de memoria dinámica.
    v = (float*) malloc( sizeof(float) * n );

    //Copiamos el vector externo en el vector interno
    memcpy(v,vector,sizeof(float) * n );
}

//Función para limpiar la variable vector
void LPData::Delete()
{
    n=0;
    free(v);
}

//Imprimimos el valor del vector
void LPData::print()
{
    for(int i = 0 ; i < n ; ++i)
    {
        cout << (float) v[i] << " ";
    }
}

void LPData::random(int t, float min, float max)
{
    n = t; //Inicializamos el número de elementos del vector

    //Creamos un vector de memoria dinámica.
    v = (float*) malloc( sizeof(float) * n );

    for(int i = 0 ; i < n ; ++i)
        v[i] = (float)(rand()%(int)(max*1000 - min*1000))/1000 + min;

}

float LPData::Max()
{
    float max;

    max = v[0];
    for(int i = 1 ; i < n ; ++i)
        if( v[i] > max )
            max = v[i];

    return max;
}

float LPData::Min()
{
    float min;

    min = v[0];
    for(int i = 1 ; i < n ; ++i)
        if( v[i] < min )
            min = v[i];

    return min;
}

float LPData::Average()
{
    float ave = 0;

    for(int i = 0 ; i < n ; ++i)
        ave += v[i];
   
    ave /= n;

    return ave;
}

//Sobrecarga operador +=
void LPData::operator +=(LPData vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE SERIES DE DATOS\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] += vector.v[i];
   
}

//Sobrecarga operador -=
void LPData::operator -=(LPData vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE SERIES DE DATOS\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] -= vector.v[i];
}

//Sobrecarga operador +=
void LPData::operator +=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] += scal;
}

//Sobrecarga operador -=
void LPData::operator -=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] -= scal;
}

//Sobrecarga operador *=
void LPData::operator *=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] *= scal;
   
}

//Sobrecarga operador /=
void LPData::operator /=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] /= scal;
   
}




Salvo fallo de planificación a partir de ahora solo es ir "rellenando" la estructura que tenemos con nuevas funcionalidades e implementaciones más eficientes.

Espero que os haya gustado,

Nos vemos

Math Engine : Capitulo 3. Clase LPMatrix

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


Bienvenidos a un nuevo capitulo de como crear un math engine. El capitulo de hoy se encargará de mostrar el tipo de variable que seguramente más se usará en las aplicaciones de cálculo, las matrices.

Dado que es totalmente imposible poner todas las funciones que afectan a matrices (solamente con factorizaciones podria llenar miles de lineas de código) este capitulo lo dejaré totalmente abierto (como el del LPVector) para que me vayais proponiendo las funciones que querais que introduzcamos en este módulo.

No obstante, si quereis ir probando código aquí os dejo lo que he implementado hasta el momento:
LPMatrix.h:
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/


/**************************************************************************************************/
// LPMatrix.h
/**************************************************************************************************/

#ifndef __LPMatrix__
#define __LPMatrix__

class LPMatrix
{
public:
    LPMatrix();
    ~LPMatrix();

    //Funciones anexas.
    void Set(int t, int k, float* vector); //Funcion para inicializar una matriz de t*k elementos
    void Delete();                    //Función para limpiar la variable matriz
    void print();                    //Función para imprimir la matriz por la consola


    //OPERACIONES DE ACUMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
    //Operaciones vectoriales
    void operator +=(LPMatrix matrix);           
    void operator -=(LPMatrix matrix);           
   
    //Operaciones con escalares ( sumar , restar , etc ... todo una matriz por un escalar)
    void operator +=(float scal);
    void operator -=(float scal);
    void operator *=(float scal);
    void operator /=(float scal);

public:
    int m,n;      //dimensiones de la matriz
    float **p;    //puntero a memoria dinámica para matrices
};

#endif

LPMatrix.cpp:
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

#include "LPMatrix.h"
#include <math.h>
#include <iostream>                //Usada para imprimir por consola

using namespace std;

//Constructor
LPMatrix::LPMatrix()
{
    n = 0;
    m = 0;
    p = NULL;
}

//Destructor
LPMatrix::~LPMatrix()
{

}

//Funciones anexas.
void LPMatrix::Set(int t, int k, float* vector) //Funcion para inicializar una matriz de t*k elementos
{
    //Inicializamos el número de elementos del vector
    n = t;
    m = k;

    //Creamos un vector de memoria dinámica donde almacenaremos los punteros subsiguientes.
    p = (float**) malloc( sizeof(float *) * n );
   
    //Creamos cada una de las filas o columnas (como queramos verlo) de la matriz
    for(int i = 0 ; i < n ; ++i )
        p[i] = (float*) malloc( sizeof(float) * m );
   
    //Copiamos el vector externo en el vector interno
    for(int i = 0 ; i < n*m ; ++i )
        p[i/m][i%m] = vector[i];
   
}

void LPMatrix::Delete()                    //Función para limpiar la variable matriz
{
    for(int i = 0 ; i < n ; ++i )
        free(p[i]);

    n=0;
    m=0;

    free(p);
}

void LPMatrix::print()                    //Función para imprimir la matriz por la consola
{
    for(int i = 0 ; i < n*m ; ++i)
    {
        if(!(i%m))
            cout << "\n";

        cout << (float) p[i/m][i%m] << " ";
    }
}


//OPERACIONES DE ACUMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
//Operaciones vectoriales
void LPMatrix::operator +=(LPMatrix matrix)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if ( (n != matrix.n) || (m != matrix.m) )
    {
        cout << "ERROR DIMENSIONAL ENTRE MATRICES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] += matrix.p[i][j];
}

void LPMatrix::operator -=(LPMatrix matrix)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if ( (n != matrix.n) || (m != matrix.m) )
    {
        cout << "ERROR DIMENSIONAL ENTRE MATRICES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] -= matrix.p[i][j];
}

   
//Operaciones con escalares ( sumar , restar , etc ... todo una matriz por un escalar)
void LPMatrix::operator +=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] += scal;
}

void LPMatrix::operator -=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] -= scal;
}

void LPMatrix::operator *=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] *= scal;
}

void LPMatrix::operator /=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] /= scal;
}

main.cpp
int main (int argc, char* argv[])
{
    LPMatrix m1,m2;
    float mat1[15] = { 1.0f ,1.1f ,1.5f , 1.0f ,1.1f ,1.5f , 1.0f ,1.1f ,1.5f , 1.0f ,1.1f ,1.5f , 1.0f ,1.1f ,1.5f };
    float mat2[15] = { 3.0f ,4.1f ,0.5f , 3.0f ,4.1f ,0.5f , 3.0f ,4.1f ,0.5f , 3.0f ,4.1f ,0.5f , 3.0f ,4.1f ,0.5f };
   
    m1.Set(3,5,mat1);
    m2.Set(3,5,mat2);

    cout << "Matriz 1:";
    m1.print();
    cout << "\n";

    cout << "Matriz 2:";
    m2.print();
    cout << "\n";   

   
    cout << "Suma de matrices:";
    m2 += m1;
    m2.print();
    cout << "\n";   

    //Eliminamos las matrices
    m1.Delete();
    m2.Delete();

    system("PAUSE");
}

Espero que os haya servido y os haya gustado.

Nos vemos

Math Engine : Capitulo 2. Clase LPVector

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

El capitulo de hoy trata sobre la clase LPVector que es la que se encarga de almacenar un vector de cualquier tamaño y poder operar con el.

La lista de operaciones que he considerado imprescindibles son las siguientes, todas las que querais que pongamos adicionales las ponemos... a la que tengamos una lista estable de operaciones nos dedicaremos a optimizarlas con SSE.:
//Funciones anexas.
    void Set(int t, float* vector); //Funcion para inicializar un vector de n elementos
    void Delete();                    //Función para limpiar la variable vector
    void print();                    //Función para imprimir el vector por la consola


    //OPERACIONES DE AMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
    //Operaciones vectoriales
    void operator +=(LPVector vector);           
    void operator -=(LPVector vector);           
    void sqrt_vec();                        //Calculamos la raíz cuadrada de todo los elementos del vector
    void normalize();                        //Normaliza el vector (hace que el módulo sea 1)

    //Operaciones con escalares ( sumar , restar , etc ... todo un vector por un escalar)
    void operator +=(float scal);
    void operator -=(float scal);
    void operator *=(float scal);
    void operator /=(float scal);


    //OPERACIONES QUE DEVUELVEN ALGO (NO ACUMULADORES: +,-,*, etc...)
    //Operaciones vectoriales que devuelven escalares
    float operator ^(LPVector vector);            //Operador producto escalar.
    float module();                                //Calcula el módulo del vector

El código se organiza en dos archivos LPVector (.cpp y .h) junto con los cambios del main para demostrar que funcionan:

main.cpp:
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

/**************************************************************************************************/
// main.cpp : Main de nuestro MathEngine
/**************************************************************************************************/

//Include del Math engine
#include "LPMath.h"

//Delete console
//#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

#include <iostream>                //Usada para imprimir por consola

using namespace std;

int main (int argc, char* argv[])
{
    LPVector v1,v2;
    float vec1[4] = { 1.0f ,1.1f ,1.5f ,2.5f };
    float vec2[4] = { 3.0f ,4.1f ,0.5f ,0.4f };

    float n;

    //Creamos los vectores
    v1.Set(4,vec1);
    v2.Set(4,vec2);

    cout << "Vector 1: ";
    v1.print();
    cout << "\n";

    cout << "Vector 2: ";
    v2.print();
    cout << "\n";
   
    n = v1^v2;
    cout << "Producto escalar: " <<    n << "\n";

    n = v1.module();
    cout << "Modulo vector 1: " <<    n << "\n";

    n = v2.module();
    cout << "Modulo vector 2: " <<    n << "\n";

    v1.normalize();
    n = v1.module();
    cout << "Modulo vector 1 normalizado: " <<    n << "\n";

    v2.normalize();
    n = v2.module();
    cout << "Modulo vector 2 normalizado: " <<    n << "\n";

    v2 += v1;
    cout << "Suma de vectores: ";
    v2.print();
    cout << "\n";

    n = v2.module();
    cout << "Modulo de suma de vectores: " <<    n << "\n";

    v2.normalize();
    n = v2.module();
    cout << "Modulo de suma de vectores normalizado: " <<    n << "\n";


    //Eliminamos los vectores
    v1.Delete();
    v2.Delete();

    system("PAUSE");
}

LPVector.cpp
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

#include "LPVector.h"
#include <math.h>
#include <iostream>                //Usada para imprimir por consola

using namespace std;

//Constructor
LPVector::LPVector()
{
    n = 0;
    v = NULL;
}

//Destructor
LPVector::~LPVector()
{
    //if (n)
    //{
    //    if( v != NULL )
    //        free(v);
    //}
}

//Funcion para inicializar un vector de n elementos
void LPVector::Set(int t, float* vector)
{
    n = t; //Inicializamos el número de elementos del vector

    //Creamos un vector de memoria dinámica.
    v = (float*) malloc( sizeof(float) * n );

    //Copiamos el vector externo en el vector interno
    memcpy(v,vector,sizeof(float) * n );
}

//Función para limpiar la variable vector
void LPVector::Delete()
{
    n=0;
    free(v);
}

//Imprimimos el valor del vector
void LPVector::print()
{
    for(int i = 0 ; i < n ; ++i)
    {
        cout << (float) v[i] << " ";
    }
}

void LPVector::random(int t, float min, float max)
{
    n = t; //Inicializamos el número de elementos del vector

    //Creamos un vector de memoria dinámica.
    v = (float*) malloc( sizeof(float) * n );

    for(int i = 0 ; i < n ; ++i)
        v[i] = (float)(rand()%(int)(max*1000 - min*1000))/1000 + min;

}

void LPVector::cross(LPVector vector)
{
    //POR IMPLEMENTAR
}

void LPVector::sqrt_vec()
{
    for(int i = 0 ; i < n ; ++i)
        v[i] = sqrt(v[i]);
}

void LPVector::normalize()
{
    //Dividimos cada componente por el módulo
    *this /= this->module();
}

//Sobrecarga operador +=
void LPVector::operator +=(LPVector vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] += vector.v[i];
  
}

//Sobrecarga operador -=
void LPVector::operator -=(LPVector vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] -= vector.v[i];
}

//Sobrecarga operador +=
void LPVector::operator +=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] += scal;
}

//Sobrecarga operador -=
void LPVector::operator -=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] -= scal;
}

//Sobrecarga operador *=
void LPVector::operator *=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] *= scal;
  
}

//Sobrecarga operador /=
void LPVector::operator /=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] /= scal;
  
}
//Sobrecarga operador *= //Este operador es el que nos devolverá matrices como resultado del producto de una columna por un fila.
                         //Es decir, es el producto vectorial. Ya lo veremos a su momento
/*void LPVector::operator *=(LPVector vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] += vector.v[i];
}
*/

//Sobrecarga operador ^ A partir de ahora se entederá que es el producto escalar.
float LPVector::operator ^(LPVector vector)
{
    float a = 0.0;

    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return 0.0;
    }

    for(int i = 0 ; i < n ; ++i)
        a += v[i] * vector.v[i];

    return a;
}

float LPVector::module()
{
    float a = 0.0;

    for(int i = 0 ; i < n ; ++i)
        a += v[i] * v[i];

    return sqrt(a);
}

float LPVector::distance(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);
}


LPVector.h
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/


/**************************************************************************************************/
// LPVector.h
/**************************************************************************************************/

#ifndef __LPVector__
#define __LPVector__

class LPVector
{
public:
    LPVector();
    ~LPVector();

    //Funciones anexas.
    void Set(int t, float* vector); //Funcion para inicializar un vector de n elementos
    void Delete();                    //Función para limpiar la variable vector
    void print();                    //Función para imprimir el vector por la consola
    void random(int t,float min, float max);                //Función para rellenar de random un vector de t posiciones

    //OPERACIONES DE AMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
    //Operaciones vectoriales
    void operator +=(LPVector vector);          
    void operator -=(LPVector vector);  
    void cross(LPVector vector);            //Calculamos el producto vectorial (cross) POR HACER
    void sqrt_vec();                        //Calculamos la raíz cuadrada de todo los elementos del vector
    void normalize();                        //Normaliza el vector (hace que el módulo sea 1)
          

    //Operaciones con escalares ( sumar , restar , etc ... todo un vector por un escalar)
    void operator +=(float scal);
    void operator -=(float scal);
    void operator *=(float scal);
    void operator /=(float scal);


    //OPERACIONES QUE DEVUELVEN ALGO (NO ACUMULADORES: +,-,*, etc...)
    //Operaciones vectoriales que devuelven escalares
    float operator ^(LPVector vector);            //Operador producto escalar.
    float module();                                //Calcula el módulo del vector
    float distance(LPVector vector);

public:
    int n;         //número de elementos del vector
    float *v;    //puntero a memoria dinámica
};

#endif
Cualquier otra cosa que se os ocurra que le pongamos o cualquier fallo que veais, hacedmelo llegar

Espero que os haya gustado,

Nos vemos

Math Engine : Capitulo 1. Sobrecarga de operadores

El artículo original lo podeís encontrar aqui

En este capitulo no haremos mucho más que iniciar las bases del proyecto.

Conceptos fundamentales:
1. Todo el código se escribirá en un inicio en C/C++
2. Todo el código se intentará encapsular en clases (aunque en prinicipio no habrá gestores como en el game engine, usease el MathEngine será una colección de funcionalidades matematicas no un motor que correrá de fondo gestionandolo todo).
3. Todas las optimizaciones irán con ifdef (así se podrán hacer diferentes compilaciones optimizadas en función de la arquitectura en que se trabaje : tipo de ensamblador, tipo de procesador, etc...). Las optimizaciones mínimas que se tienen en cuenta ahora mismo son paralelización multicore y ensamblador vectorial SSE.
4. El código que gestione el usuario ha de ser mínimo , claro y sencillo.
5. Habrá 3 grandes bloques de trabajo: operaciones entre vectores, operaciones entre matrices y operaciones vector-matriz.
6. Las operaciones se irán implementado a medida que vosotros las vayais pidiendo así que si os interesa algún función en particular no dudeis en pedirla.


Dicha toda esta parrafada, expondremos el capitulo como tal.

Lo primero será crear el proyecto. Deberiais saber hacerlo pero si no teneis mucha memoria :D, podeis mirar como se creó el proyecto en el game engine (http://lordpakus.blogspot.com/2011/05/gameengine-capitulo-1.html).

Una vez tengais el proyecto creado cread los siguientes archivos y copiad el siguiente código. Vereis que no es más un código muy sencillo de operación entre vectores.

main.cpp:
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

/**************************************************************************************************/
// main.cpp : Main de nuestro MathEngine
/**************************************************************************************************/

//Include del Math engine
#include "LPMath.h"

//Delete console
//#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

#include <iostream>                //Usada para imprimir por consola

using namespace std;

int main (int argc, char* argv[])
{
    LPVector4 v1,v2;

    v1.Set(0.0f,1.0f,2.0f,3.0f);
    v2.Set(1.1f,1.2f,1.3f,1.4f);

    v1 += v2;

    cout << "Vector 1: ";
    v1.print();
    cout << "\n";

    cout << "Vector 2: ";
    v2.print();
}

LPMath.h
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

//Lista de todos los includes necesarios para el math engine.

#include "LPVector4.h"


LPVector4.h
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/


/**************************************************************************************************/
// LPVector.h
/**************************************************************************************************/

#ifndef __LPVector4__
#define __LPVector4__

class LPVector4
{
public:
    void Set(float x, float y , float z , float t); //Funcion para inicializar un vector de 4 elementos
    void print();                                    //Función para imprimir el vector por la consola

    void operator +=(const LPVector4 v);            //Sobrecarga de operador +=
   

public:
    float xv,yv,zv,tv;
};

#endif



LPVector4.cp
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

#include "LPVector4.h"
#include <iostream>                //Usada para imprimir por consola

using namespace std;

void LPVector4::Set(float x, float y , float z , float t) //Funcion para inicializar un vector de 4 elementos
{
    xv = x;
    yv = y;
    zv = z;
    tv = t;
}

//Imprimimos el valor del vector
void LPVector4::print()
{
    cout << "x: " << xv << " y: " << yv << " z: " << zv << " t: " << tv;
}

//Sobrecarga operador +=
void LPVector4::operator +=(const LPVector4 v)
{
    xv += v.xv;
    yv += v.yv;
    zv += v.zv;
    tv += v.tv;
}



Para probar el ejecutable debereis hacerlo desde una consola para que os permita ver el resultado.



Por ahora lo único que os debe interesar es como se ha hecho la sobrecarga del operador += aplicado a los vectores ya que esta sobrecarga es de lo que más se usara en el MathEngine para hacer que el código quede limpio de cara al usuario.

Sin más, aqui os dejo, espero que os lo hayais pasado bien y hayais aprendido

Nos vemos

Math Engine : Capitulo 0. Esquema de un math engine

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

Hola a todos...

Igual que hice con el game engine aquí os dejo un esquema del math engine para que podais entender y acceder más facilmente a cada una de sus partes.

El LP Math Engine  tiene tres clases principales

Vector: La clase vector contiene un vector de floats de tamaño arbitrario y todas las funciones asociadas de intereacción entre vector-vector y vector-escalar. http://lordpakus.blogspot.com/2011/08/math-engine-capitulo-2-clase-lpvector.html
    
Matriz: La clase matriz contiene un matriz de floats de dimensiones arbiarias y todas las funciones asociadas de interacción entre matriz-matriz, matriz-vector y matriz-escalar.http://lordpakus.blogspot.com/2011/08/math-engine-capitulo-3-clase-lpmatrix.html

Serie de datos: La clase serie de datos contiene un vector de datos de longitud arbitraria que se pueden interpretar como una serie estadistica, una discretización de señal, un muestreo de función matemática,etc.. Incorpora funciones de tratamiento de la señal (FFT por ejemplo), de tratamiento de funciones (derivación e integración) y de tratamiento estadístico (medias, varianzas, modelos estadisticos,rectas de regresión) http://lordpakus.blogspot.com/2011/08/math-engine-capitulo-4-clase-lpdata.html

Aparte de estas clases el math engine tiene dos caracteristicas importantes:
-> Sobrecarga de operadores: Es decir, se basa en esta caracteristica de la POO para facilitar el trabajo al programador de aplicaciones matematicas, escondiendo cientos o miles de lineas de código en operciones sencillas de entender conceptualmente http://lordpakus.blogspot.com/2011/08/math-engine-capitulo-1-sobrecarga-de.html

-> Uso de optimizaciones:
                - Ensamblador SSE: El ensamblador SSE es un conjunto de operaciones que tienen la mayoria de procesadores actuales que permiten realizar numerosas operaciones en paralelo. Para más información podeis mirar: http://lordpakus.blogspot.com/2011/08/curso-de-programacion-capitulo-7.html


  Ejemplo de SSE en la suma de vectores:  http://lordpakus.blogspot.com/2011/09/math-engine-capitulo-5-lpvector-con.html

  Ejemplo de SSE en la distancia entre vectores: http://lordpakus.blogspot.com/2011/09/math-engine-capitulo-6-vectordistance.html

                 - Paralelización multicore: WORKING. En breve tendremos tutoriales de paralelización

Espero que os haya gustado y que os haya ayudado a organizar la información

Nos vemos