Anuncios

Bienvenidos sean a este post, hoy veremos una de las etapas de TMP

Anuncios
Anuncios

En el post anterior, vimos como se puede calcular el factorial de un valor entero en el momento de la compilacion. En este post nos centraremos en analizar como utilizar un bucle en la ejecucion para poder desenrrollar los productos escalares de dos vectores con una longitud n. Siendo que n sera un valor conocido al momento de compilacion. El beneficio de un vector de longitud n mas tradicional es que el desarrollo de los bucles es factible. Lo cual desemboca en un codigo muy optimizado. Analicemos el siguiente ejemplo:

#include <iostream>

template <typename T>
T prod(int n, const T* a, const T* b)
{
        T ret = 0;
        for (int i = 0; i < n; ++i)
                ret += a[i] * b[i];
        return ret;
}

int main()
{
        float a[5] = { 1, 2, 3, 4, 5 };
        float b[5] = { 6, 7, 8, 9, 10 };
        std::cout << "Producto_escalar(5, a, b)=";
        std::cout << prod<float>(5, a, b) << std::endl;
        std::cout << "Producto_escalar(5, a, a)=";
        std::cout << prod<float>(5, a, a) << std::endl;
}
Anuncios
Anuncios

En este ejemplo, implementamos un tradicional template de funcion para calcular el producto escalar de dos vectores. La funcion recibira tres valores, siendo el primero con la cantidad de productos que calcularemos en cada vector recibido y los otros dos seran los vectores. El primero al ser el que usaremos de limite sera de un tipo definido, int, pero los otros dos al ser los datos que manipularemos seran del tipo generico de la template. Primero iniciamos la variable que devolveremos, luego tenemos un bucle donde lo repetiremos en base al valor recibido en el primer argumento. En el bucle calcularemos el producto entre cada posicion en cada vector y el resultado lo incrementaremos al anterior que esta registrado en ret. Una vez finalizado, devolveremos el resultado almacenado en ret. En main, definimos dos arrays de tipo float con cinco valores en cada uno. Para finalmente, mostrar los resultados de utilizar a la funcion dos veces. En el primer caso, lo haremos entre los dos arrays y en el segundo caso, pasaremos el primer array. Compilemos y veamos como es la salida:

$ ./prod
Producto_escalar(5, a, b)=130
Producto_escalar(5, a, a)=55
$
Anuncios

La capacidad de un bucle desenrrollador significa que si podemos optimizar el bucle interno, nos permite ahorrar rutinas de computacion. Esto lo podemos hacer con metaprogramacion, tomemos el codigo anterior y realicemos la siguiente modificacion:

#include <iostream>

template <int N, typename T>
class prod {
public:
        static T res(T* a, T* b) {
                return (*a)*(*b) + prod<N - 1, T>::res(a + 1, b + 1);
        }
};

template <typename T>
class prod<1, T> {
public:
        static T res(T* a, T* b) {
                return (*a) * (*b);
        }
};

int main()
{
        float a[5] = { 1, 2, 3, 4, 5 };
        float b[5] = { 6, 7, 8, 9, 10 };
        std::cout << "Producto_escalar(5, a, b)=";
        std::cout << prod<5, float>::res(a, b) << std::endl;
        std::cout << "Producto_escalar(5, a, a)=";
        std::cout << prod<5, float>::res(a, a) << std::endl;
}
Anuncios
Anuncios

En este nuevo codigo, en lugar de usar un template con una funcion lo haremos con una clase. La primer diferencia es que en el template tendremos uno de tipo int y otro generico. Siendo el primero como control como teniamos anteriormente. En esta clase definimos un metodo de tipo estatico, y sera la encargada de recibir a los dos vectores y realizar el calculo. Para ello, usaremos una recursion de esta clase para realizar el incremento de cada producto entre los dos vectores. La siguiente clase es la especializacion de la clase anterior. En este caso se usara cuando N tenga el valor de 1, donde no realizara la recursion sino que simplemente realiza el producto entre los dos vectores. En el main, mantenemos una estructura similar a lo visto en el codigo anterior. La unica diferencia es que en lugar de llamar a la funcion, usaremos al metodo de la clase. Observen que pasamos la cantidad en la clase y los vectores en el metodo. Si lo compilan y ejecutan deben tener la misma salida. Pero cual es la optimizacion con respecto al anterior? Basicamente que el llamado que hacemos se procesa y envia en las valores de 2 y 1 de N, ahorrando ciclos de computacion.

Anuncios

En codigos cortos como este no se notara pero en codigos donde se procesa mas informacion si notaran la diferencia.

Anuncios

En resumen, hoy hemos visto otra etapa de TMP, en este caso la optimizacion del codigo, como aplicarlo, como podemos utilizarlo y un ejemplo practico para entender el concepto. Espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Donatión

It’s for site maintenance, thanks!

$1.50