1. Indice de pagina
  2. 1. Objetivos
  3. 2. Enunciado
  4. 2.1. Pistas
  5. 2.2. Código secuencial de inicio
  6. 2.3. Funciones necesarias
  7. 3. Solución
  8. 3.1. Código
  9. 3.2. Salida

Multiplicación Matriz Vector

Objetivos

Enunciado

Realizar la multiplicación paralela de una matriz A (NxN) con un vector x (Nx1) uando P procesos.

En esta formulación paralela del algoritmo, descomponemos la matriz A en bloques de N/P filas y asignamos un bloque a cada proceso.
Por lo tanto, cada proceso necesitará N/P filas de la matriz A así como todo el vector x por el que se va a multiplicar.

1. El proceso 0 genera la matriz A y el vector x.
2. La matriz A es distribuida (MPI_Scatter), y el vector difundido (MPI_Bcast).
3. Cada proceso opera con los datos que tiene.
4. Se reconstruye el vector solución en el proceso 0 mediante MPI_Gather.
Imagen Tutorial 5 Producto Matriz - Vector (Pulsar sobre la imagen para ver la animación)

Pistas

Cada proceso obtendrá como resultado un subvector y, al unir todos los subvectores (MPI_Gather) en el proceso 0, se obtiene el vector solución.

Gracias a MPI_Wtime podemos tomar tiempos, pero hay que tener en cuenta que los tiempos son locales a un proceso. Quizás te interesaría tomar tiempos asegurando que todos los procesos inician el cálculo al mismo tiempo y que, cuando se toma el tiempo final, todos han finalizado dicho cálculo. Esto se consigue con MPI_Barrier que fuerza un punto de sincronización o reunión de todos los procesos del comunicador.

Código secuencial de inicio Descarga

#include <iostream>
#include <cstdlib>
#include <ctime>
 
using namespace std;
 
int main(int argc, char * argv[]) {
 
    float *A, // Matriz a multiplicar
	 *x, // Vector que vamos a multiplicar
	 *comprueba; // Guarda el resultado final (calculado secuencialmente), su valor
 
    int n;
 
    if (argc <= 1) {// si no se pasa el size de la matriz, se coge n=10
        cout << "Square Matrix dimension? (Default: 10)"<< endl;
        n = 10;
    } else 
        n = atoi(argv[1]);
 
 
 
	A = new float[n*n];//reservamos espacio para la matriz (n x n floats)
	x = new  float[n]; //reservamos espacio para el vector (n floats).
 
    //Rellena la matriz y el vector
    for (int i = 0; i < n; i++) {
        x[i] = (float) (1.5*(1+(5*(i))%3)/(1+(i)%5));
        for (int j = 0; j < n; j++) {
	        A[i*n+j] = (float) (1.5*(1+(5*(i+j))%3)/(1+(i+j)%5));
        }
    }
 
 
    // Muestra A y x
    cout << "The matrix and vector are " << endl<<endl;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
	        if (j == 0) cout << "[";
	        cout << A[i*n+j];
	        if (j == n - 1) cout << "]"; 
            else cout << "  ";
	    }
	    cout << "\t  [" << x[i] << "]" << endl;
    }
    cout << "\n";
 
    comprueba = new float [n];
    //Calculamos la multiplicacion secuencial para 
    //despues comprobar que es correcta la solucion.
    for (int i = 0; i < n; i++) {
	    comprueba[i] = 0;
	    for (int j = 0; j < n; j++) {
	        comprueba[i] += A[i*n+j] * x[j];
	    }
    }
 
    cout << "The result is:" << endl<<endl;
    for (int i = 0; i < n; i++) {
	    cout << comprueba[i] << endl;
    }
 
    delete [] comprueba;
    delete [] A;
    delete [] x;
 
 
    return 0;
 
}

Funciones necesarias

Solución

Código Descarga

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <mpi.h>
 
using namespace std;
 
int main(int argc, char * argv[]) {
 
    int numeroProcesadores, id_Proceso;
 
    float *A, // Matriz global a multiplicar
	    *x, // Vector a multiplicar
        *y, // Vector resultado
        *local_A,  // Matriz local de cada proceso
        *local_y;  // POrción local del resultado en  cada proceso
 
 
    double tInicio, // Tiempo en el que comienza la ejecucion
           Tpar, Tseq;   
 
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &numeroProcesadores);
    MPI_Comm_rank(MPI_COMM_WORLD, &id_Proceso);
 
    int n;
 
    if (argc <= 1) {// si no se pasa el size de la matriz, se coge n=10
        if (id_Proceso==0)
            cout << "The dimension N of the matrix is missing (N x N matrix)"<< endl;
        MPI_Finalize();
        return 0;
    } else 
        n = atoi(argv[1]);
 
    x = new  float[n]; //reservamos espacio para el vector x (n floats).
 
    // Proceso 0 genera matriz A y vector x
    if (id_Proceso==0)
    {
        A = new float[n*n];//reservamos espacio para la matriz (n x n floats)
        y = new float[n];//reservamos espacio para el vector resultado final y (n floats)
 
        // Rellena la matriz y el vector
        for (int i = 0; i < n; i++) {
            x[i] = (float) (1.5*(1+(5*(i))%3)/(1+(i)%5));
            for (int j = 0; j < n; j++) {
	            A[i*n+j] = (float) (1.5*(1+(5*(i+j))%3)/(1+(i+j)%5));
            }
        }
    }
 
    // Cada proceso reserva espacio para su porción de A y para el vector x 
    const int local_A_size=n*n/numeroProcesadores;
    const int local_y_size=n/numeroProcesadores;
    local_A = new float[local_A_size];//reservamos espacio para la matriz (n x n floats)
    local_y = new  float[local_y_size]; //reservamos espacio para el vector y (n/num_procs
floats).
 
     // Repartimos una bloque de filas de A a cada proceso
    MPI_Scatter(A, // Matriz que vamos a compartir
        local_A_size, // Numero de filas a entregar
        MPI_FLOAT, // Tipo de dato a enviar
        local_A, // Vector en el que almacenar los datos
        local_A_size, // Numero de filas a recibir
        MPI_FLOAT, // Tipo de dato a recibir
        0, // Proceso raiz que envia los datos
        MPI_COMM_WORLD); // Comunicador utilizado (En este caso, el global)
 
    // Difundimos el vector x entre todas los procesos
    MPI_Bcast(x, // Dato a compartir
        n, // Numero de elementos que se van a enviar y recibir
        MPI_FLOAT, // Tipo de dato que se compartira
        0, // Proceso raiz que envia los datos
        MPI_COMM_WORLD); // Comunicador utilizado (En este caso, el global)
 
 
    // Hacemos una barrera para asegurar que todas los procesos comiencen la ejecucion
    // a la vez, para tener mejor control del tiempo empleado
    MPI_Barrier(MPI_COMM_WORLD);
    // Inicio de medicion de tiempo
    tInicio = MPI_Wtime();
 
    for (int i = 0; i < local_y_size; i++) {
        local_y[i]=0.0;
        for (int j = 0; j < n; j++) {
            local_y[i] += local_A[i*n+j] * x[j];
        }
    }
 
    MPI_Barrier(MPI_COMM_WORLD);
    // fin de medicion de tiempo
    Tpar = MPI_Wtime()-tInicio;
 
    // Recogemos los datos de la multiplicacion, por cada proceso sera un escalar
    // y se recoge en un vector, Gather se asegura de que la recolecci�n se haga
    // en el mismo orden en el que se hace el Scatter, con lo que cada escalar
    // acaba en su posicion correspondiente del vector.
    MPI_Gather(local_y, // Dato que envia cada proceso
            local_y_size, // Numero de elementos que se envian
            MPI_FLOAT, // Tipo del dato que se envia
            y, // Vector en el que se recolectan los datos
            local_y_size, // Numero de datos que se esperan recibir por cada proceso
            MPI_FLOAT, // Tipo del dato que se recibira
            0, // proceso que va a recibir los datos
            MPI_COMM_WORLD); // Canal de comunicacion (Comunicador Global)
 
    // Terminamos la ejecucion de los procesos, despues de esto solo existira
    // el proceso 0
    // Ojo! Esto no significa que los demas procesos no ejecuten el resto
    // de codigo despues de "Finalize", es conveniente asegurarnos con una
    // condicion si vamos a ejecutar mas codigo (Por ejemplo, con "if(rank==0)".
    MPI_Finalize();
 
 
    if (id_Proceso == 0) {
        float * comprueba = new float [n];
        //Calculamos la multiplicacion secuencial para 
        //despues comprobar que es correcta la solucion.
 
        tInicio = MPI_Wtime();
        for (int i = 0; i < n; i++) {
	        comprueba[i] = 0;
	        for (int j = 0; j < n; j++) {
	            comprueba[i] += A[i*n+j] * x[j];
	        }
        }
        Tseq = MPI_Wtime()-tInicio;
 
 
        int errores = 0;
        for (unsigned int i = 0; i < n; i++) {   
            cout << "\t" << y[i] << "\t|\t" << comprueba[i] << endl;
            if (comprueba[i] != y[i])
                errores++;
        }
         cout << ".......Obtained and expected result can be seen above......." << endl;
 
        delete [] y;
        delete [] comprueba;
        delete [] A;
 
        if (errores) {
            cout << "Found " << errores << " Errors!!!" << endl;
        } else {
            cout << "No Errors!" << endl<<endl;
            cout << "...Parallel time (without initial distribution and final gathering)= "
<< Tpar << " seconds." << endl<<endl;
            cout << "...Sequential time= " << Tseq << " seconds." << endl<<endl;
 
        }
 
    }
 
 
    delete [] local_A;
    delete [] local_y;
    delete [] x;
 
    return 0;
 
}

Salida

> mpiCC 5_Matriz_X_Vector.cpp -o mxv
> mpirun -np 5 mxv
La matriz y el vector generados son
[520 105 331 20 659] [61]
[783 378 624 743 716] [83]
[707 751 373 950 919] [86]
[996 720 457 176 709] [57]
[432 508 470 191 743] [53]

El resultado obtenido y el esperado son:
104968 | 104968
213100 | 213100
240395 | 240395
207427 | 207427
159202 | 159202
No hubo errores
El tiempo tardado ha sido 0.000153065 segundos.
Creado por: Daniel Guerrero Martínez y Sergio Rodríguez Lumley 2010

Valid HTML 4.01 Transitional