Multiplicación de matrices incremental. Versión MPI+OpenMP.

Sobre una matriz de números reales, A de dimensión NxN, se realizan las multiplicaciones: de la submatriz 1x1 de la parte superior-izquierda por sí misma, de la submatriz 2x2 de la parte superior-izquierda por sí misma..., y así sucesivamente hasta la matriz completa NxN. Las multiplicaciones son independientes, sin sustituir las entradas de la matriz original A tras cada multiplicación. Finalmente se suman todas las matrices obtenidas, acumulando los resultados de cada matriz en sus posiciones correspondientes de la matriz A. El código secuencial que se proporciona no está optimizado (se podría reutilizar información de una multiplicación para la siguiente) y no se pide optimizarlo en secuencial, sino simplemente paralelizarlo, en este caso con MPI+OpenMP. La soluci\'on puede explotar paralelismo de grano grueso o fino o una combinaci\'on de los dos.


Se resuelven varios problemas. Para cada problema la función a paralelizar tiene parámetros:

Input parameters:

-int N: número de filas y columnas

Input/Output parameter:

-int *A: matriz de dimensión NxN

Parallelism parameters:

-int nodo: identificador de proceso

-int np: número de procesos


La entrada tiene en la primera línea el número de problemas. Para cada problema hay una línea con cinco valores: N, la semilla para la generación aleatoria, los valores mínimo y máximo para la generación, y un valor que indica cada cuantos elementos se escribe uno a la salida.

Ficheros

3
10  1 -1 1 1
400  2 -1 1 130
430  3 -1 1 210
/*
  CPP_CONTEST=2018Enero
  CPP_PROBLEM=F
  CPP_LANG=CPP+OPENMP+MPI
  CPP_PROCESSES_PER_NODE=marte 1
*/

#include < stdlib.h>
#include < omp.h>
#include < mpi.h>

extern void escribir(double *,int,int);

void multiplicar(int n,int ld,double *a,double *r)
{
  for(int i=0;i < n;i++)
  {
    for(int j=0;j < n;j++)
    {
      double s=r[i*ld+j];
      for(int k=0;k < n;k++)
      {
        s+=a[i*ld+k]*a[k*ld+j];
      }
      r[i*ld+j]=s;
    }
  }
}

void copiar(double *vo,int n,double *vd)
{
  for(int i=0;i < n;i++)
    vd[i]=vo[i];
}

void  sec(int n,double *A,int nodo,int np)
{
  double *R=new double[n*n];
  for(int i=0;i < n*n;i++)
    R[i]=0.;
  for (int i = 0; i < n; i++)
  {
    multiplicar(i+1,n,A,R);
  }
  copiar(R,n*n,A);
#ifdef DEBUG
  escribir(R,n,n);
#endif
  delete[] R;
}
#include < stdlib.h>
#include < stdio.h>
#include < sys/time.h>
#include < mpi.h>
#include < signal.h>
#include < unistd.h>

//Prueba de Metodología de la Programación Paralela
//enero 2018
//Multiplicación de matrices incremental, versión MPI+OpenMP
//Esquema para entrada/salida, validación y toma de tiempos

void generar(double *m, int t,int lv,int uv) {
  int i;

  for (i = 0; i < t; i++) {
      m[i] =(((1. * rand()) / RAND_MAX)*(uv-lv)+lv);
  }
}

void escribir(double *m, int N,int M) {
  int i, j;

  for (i = 0; i < N; i++) {
    for (j = 0; j < M; j++)
      printf("%.3lf ", m[i * M + j]);
    printf("\n");
  }
  printf("\n");
}

void escribirresult(double *a,int N,int salida)
{
  int i;

  for(i=0;i < N;i++)
  {
        if((i%salida)==0)
        {
          printf("%.3lf \n",a[i]);
        }
  }
  printf("\n");
}

/*
c
c     mseconds - returns elapsed milliseconds since Jan 1st, 1970.
c
*/
long long mseconds(){
  struct timeval t;
  gettimeofday(&t, NULL);
  return t.tv_sec*1000 + t.tv_usec/1000;
}

static void alarm_handler(int sig) {
  fprintf(stderr, "Time Limit Exceeded\n");
  abort();
}

extern void sec(int n,double *A,int nodo,int np);

int main(int argc,char *argv[]) {
  int N; //tamaño matriz
  double *A;
  int cuantos; //número de problemas
  int semilla; //semilla para generación aleatoria
  int lv,uv; //valores inferior y superior para generación aleatoria
  int salida; //indica cada cuantos elementos se escribe uno a la salida
  long long ti,tf,tt=0;
  int nodo,np;

  FILE *stats_file = fopen("stats", "w");

  struct sigaction sact;
  sigemptyset(&sact.sa_mask);
  sact.sa_flags = 0;
  sact.sa_handler = alarm_handler;
  sigaction(SIGALRM, &sact, NULL);
  alarm(60);  /* time limit */
 
   MPI_Init(&argc,&argv);
  MPI_Comm_size(MPI_COMM_WORLD,&np);
  MPI_Comm_rank(MPI_COMM_WORLD,&nodo);
 
// The number of test cases is read
// and sent to all the processes
  if(nodo==0)
  {
    scanf("%d",&cuantos);
    MPI_Bcast(&cuantos,1,MPI_INT,0,MPI_COMM_WORLD);
  }
  else
  {
    MPI_Bcast(&cuantos,1,MPI_INT,0,MPI_COMM_WORLD);
  }

  for(int i=0;i < cuantos;i++)
  {
    if(nodo==0)
    {
      scanf("%d",&N);                   
      scanf("%d",&semilla);  
      scanf("%d",&lv); 
      scanf("%d",&uv); 
      scanf("%d",&salida);  

      A = (double *) calloc(sizeof(double),N*N);

      srand(semilla);
      generar(A,N*N,lv,uv);
#ifdef DEBUG
  escribir(A,N,N);
#endif
    }
    MPI_Barrier(MPI_COMM_WORLD);
    ti=mseconds(); 
    sec(N,A,nodo,np);
    MPI_Barrier(MPI_COMM_WORLD);
    tf=mseconds(); 
    if(nodo==0)
    {
#ifdef DEBUG
  escribir(A,N,N);
#endif
      if(i!=0) tt+=tf-ti;
      escribirresult(A,N*N,salida);
      free(A);
    }
  }
  if(nodo==0)
  {   
    fprintf(stats_file, "%Ld\n", tt);
    fclose(stats_file);
  }
  MPI_Finalize();
  return 0;
}