You are on page 1of 35

Programacin de Sistemas Paralelos 07 Ejercicios MPI

> Mquinas y directorios


Vamos a trabajar con un cluster sencillo de 34 nodos: 32 nodos uniprocesador (Pentium IV, 3 GHz, 512 MB RAM) y dos nodos SMP de 4 procesadores cada uno, conectados mediante Gigabit Ethernet y un switch en una red local. Aunque no es un sistema de grandes prestaciones, nos permite ejecutar todo tipo de aplicaciones paralelas mediante paso de mensajes, analizar su comportamiento, etc. Uno de los nodos del cluster hace las veces de servidor de ficheros y de punto de entrada al cluster; su direccin externa es g000002.gi.ehu.es, y su direccin en el cluster es nodo00. El resto de los nodos, nodo01, nodo02... slo son accesibles desde el nodo00 (no tienen conexin al exterior, salvo los nodos SMP). Para los ejercicios del curso hemos dividido la mquina en dos: un cluster con 28 nodos uniprocesador, de nombres nodo00 ... nodo27 (ms los dos nodos SMP de 4 procesadores), y un pequeo cluster de 4 nodos (nodo31 ... nodo28) que utilizaremos para alguna prueba concreta. En cada cuenta hay un directorio (PSP) en el que hemos dejando unos cuantos ejemplos, ejercicios, etc. que vamos a utilizar. Para poder ejecutar aplicaciones utilizando diferentes mquinas necesitamos que el sistema pueda entrar en las mismas usando ssh pero sin que se pida password. Para ello, hay que haber entrado una primera vez en cada mquina, para que nos reconozca como "usuarios autorizados". Tenemos que ejecutar los siguientes comandos: - En el directorio de entrada, ejecutar:
> ssh-keygen -t rsa (responder con return las tres veces)

con lo que se genera el directorio .ssh con los ficheros con claves id.rsa e id_rsa.pub - Pasar al directorio .ssh y ejecutar:
> cp id_rsa.pub authorized_keys > chmod go-rw authorized_keys

- A continuacin, entrar y salir, una por una, en las mquinas del cluster:
> ssh nodoxx (yes) > exit (xx = 01 ... 27)

> MPICH2
Vamos a utilizar la versin MPICH2 de MPI (es de libre distribucin; si queris la podis instalar en vuestro PC; otra implementacin de gran difusin es LAM / Open MPI). Previo a ejecutar programas en paralelo, hay que:

- Crear un fichero de nombre .mpd.conf que contenga una lnea con el siguiente contenido:
secretword=palabra_secreta (cualquier palabra)

y cambiarle las protecciones:


> chmod 600 .mpd.conf

- Poner en marcha el "entorno" MPICH2 (unos daemons mpd en cada mquina del cluster):
> mpdboot -v -n num_proc -f fichero_maquinas fichero_maquinas contiene los "nombres" de las mquinas del cluster con las que se quiere trabajar (por defecto se utiliza el fichero mpd.hosts).

Otros comandos tiles:


> > > > > mpdtrace mpdjoblist mpdringtest 2 mpd & mpd help devuelve las mquinas "activas" devuelve las mquinas "activas" recorre el anillo de daemons (2 veces) y devuelve el tiempo lanza un solo daemon en el procesador local informacin del comando

Si ha habido algn problema con la generacin de daemons, etc., ejecutar mpdcleanup y repetir el proceso. - A continuacin podemos compilar y ejecutar programas:
> mpicc [-Ox] -o pr1 pr1.c > mpiexec -n xx pr1 [x=1-3 nivel de optimiz.] xx = nmero de procesos

ejecuta el programa pr1 en xx procesadores. > mpiexec -n 1 -host nodo00 p1 : -n 1 -host nodo01 p2 > mpiexec -configfile procesos lanza dos programas, p1 y p2, en los nodos 00 y 01, o bien tal como se especifica en el fichero procesos.

- Finalmente, para terminar una sesin de trabajo ejecutamos:


> mpdallexit

(para ms informacin sobre estos comandos comando --help)

> JUMPSHOT
Jumpshot es la herramienta de anlisis de la ejecucin de programas paralelos que se distribuye junto con MPICH. Es una herramienta compleja, que permite analizar grficamente ficheros de trazas obtenidos de la ejecucin de programas MPI en los que previamente se han aadido una serie de llamadas a MPE para recoger informacin. Se puede obtener informacin estndar de la ejecucin de las funciones MPI de un programa, sin aadirle nada, si se compila con la siguiente opcin:
> mpicc -mpe=mpilog -o prog prog.c

Una vez compilado, lo ejecutamos y obtenemos un fichero de trazas, de tipo clog2. Ahora podemos ejecutar jumpshot para analizar el fichero de trazas obtenido. Jumphot4 trabaja con un formato de tipo slog2, por lo que previamente har una conversin a dicho formato. Si se desea, se puede hacer esa conversin ejecutando clog2TOslog2.

EJEMPLOS
/********************************************************************************** MPI: hola.c programa MPI: activacion de procesos **********************************************************************************/ #include <mpi.h> #include <stdio.h> int main(int argc, char *argv[]) { int lnom; char nombrepr[MPI_MAX_PROCESSOR_NAME];cat circu int pid, npr; int A = 2; // identificador y numero de proc. MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &npr); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Get_processor_name(nombrepr, &lnom); A = A + 1; printf(" >> Proceso %2d de %2d activado en %s, A = %d\n", pid, npr, nombrepr, A); MPI_Finalize(); return 0; }

/********************************************************************************** MPI: circu.c paralelizacion MPI de un bucle **********************************************************************************/ #include <mpi.h> #include <stdio.h> #define DECBIN(n,i) ((n&(1<<i))?1:0) void test (int pid, int z) { int v[16], i; for (i=0; i<16; i++) v[i] = DECBIN(z,i); if ((v[0] || v[1]) && (!v[1] || !v[3]) && (v[2] || v[3]) && (!v[3] || !v[4]) && (v[4] || !v[5]) && (v[5] || !v[6]) && (v[5] || v[6]) && (v[6] || !v[15]) && (v[7] || !v[8]) && (!v[7] || !v[13]) && (v[8] || v[9]) && (v[8] || !v[9]) && (!v[9] || !v[10]) && (v[9] || v[11]) && (v[10] || v[11]) && (v[12] || v[13]) && (v[13] || !v[14]) && (v[14] || v[15])) { printf(" %d) %d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d (%d)\n", pid, v[0],v[1],v[2], v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15], z); fflush(stdout); } } int main (int argc, char *argv[]) { int i, pid, npr; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &npr); MPI_Comm_rank(MPI_COMM_WORLD, &pid); for (i=pid; i<65536; i += npr) MPI_Finalize(); } test(pid, i);

/*************************************************************************************

MPI: envio.c
se envia un vector desde el procesador 0 al 1 **************************************************************************************/ #include <mpi.h> #include <stdio.h> #define N 10 int main (int argc, char **argv) { int pid, npr, origen, destino, ndat, tag; int VA[N], i; MPI_Status info; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr); for (i=0; i<N; i++) VA[i] = 0; if (pid == 0) { for (i=0; i<N; i++) VA[i] = i; destino = 1; tag = 0; MPI_Send(VA, N, MPI_INT, destino, tag, MPI_COMM_WORLD); } else if (pid == 1) { printf("\nvalor de VA en P1 antes de recibir datos\n\n"); for (i=0; i<N; i++) printf("%4d", VA[i]); printf("\n\n"); origen = 0; tag = 0; MPI_Recv(VA, N, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); MPI_Get_count (&info, MPI_INT, &ndat); printf("Pr 1 recibe VA de Pr %d: tag %d, ndat %d \n\n", info.MPI_SOURCE, info.MPI_TAG, ndat); for (i=0; i<ndat; i++) printf("%4d", VA[i]); printf("\n\n"); } MPI_Finalize(); }

/************************************************************************************

MPI: envio1.c
se envia un vector desde el procesador 0 al 1 devuelve de 1 a 0 la suma de los elementos del vector. **************************************************************************************/ #include <mpi.h> #include <stdio.h> #define N 10

int main (int argc, char **argv) { int pid, npr, origen, destino, ndat, tag; int VA[N], sumVA, i; MPI_Status info; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr); for (i=0; i<N; i++) VA[i] = 0; if (pid == 0) { for (i=0; i<N; i++) VA[i] = i; destino = 1; tag = 0; MPI_Send(VA, N, MPI_INT, destino, tag, MPI_COMM_WORLD); origen = 1; MPI_Recv(&sumVA, 1, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); printf(" La suma de VA es %d \n\n", sumVA); } else if (pid == 1) { printf("\nvalor de VA en P1 antes de recibir datos\n\n"); for (i=0; i<N; i++) printf("%4d", VA[i]); printf("\n\n"); origen = 0; tag = 0; MPI_Recv(VA, N, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); MPI_Get_count (&info, MPI_INT, &ndat); printf("Pr 1 recibe VA de Pr %d: tag %d, ndat %d \n\n", info.MPI_SOURCE, info.MPI_TAG, ndat); for (i=0; i<ndat; i++) printf("%4d", VA[i]); printf("\n\n"); sumVA = 0; for (i=0; i<N; i++) sumVA += VA[i]; destino = 0; MPI_Send(&sumVA, 1, MPI_INT, destino, tag, MPI_COMM_WORLD); } MPI_Finalize(); }

/*************************************************************************************

MPI: dlock1.c
intercambio de dos variables **************************************************************************************/ #include <mpi.h> #include <stdio.h> int main (int argc, char **argv) { int pid, origen, destino, tag; int A, B, C; MPI_Status info; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); if (pid == 0) { A = 5; printf("\n >> recibiendo datos de P1 \n"); origen = 1; tag = 1; MPI_Recv(&B, 1, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); printf("\n >> enviando datos a P1 \n"); destino = 1; tag = 0; MPI_Send(&A, 1, MPI_INT, destino, tag, MPI_COMM_WORLD); C = A + B; printf("\n } else if (pid == 1) { B = 6; printf("\n >> recibiendo datos de P0 \n"); origen = 0; tag = 1; MPI_Recv(&A, 1, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); printf("\n >> enviando datos a P0 \n"); destino = 0; tag = 0; MPI_Send(&B, 1, MPI_INT, destino, tag, MPI_COMM_WORLD); C = A + B; printf("\n } MPI_Finalize(); } C es %d en proc 1 \n\n", C); C es %d en proc 0 \n\n", C);

/*************************************************************************************

dlock2.c
*************************************************************************************/ if (pid == 0) { ... destino = 1; tag = 0; MPI_Send(&A, 1, MPI_INT, destino, tag, MPI_COMM_WORLD); origen = 1; tag = 1; MPI_Recv(&B, 1, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); ... } else if (pid == 1) { ... destino = 0; tag = 0; MPI_Send(&B, 1, MPI_INT, destino, tag, MPI_COMM_WORLD); origen = 0; tag = 1; MPI_Recv(&A, 1, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); ... }

dlock3.c
*************************************************************************************/ ... if (pid == 0) { ... destino = 1; tag = 0; MPI_Send(&A, 1, MPI_INT, destino, tag, MPI_COMM_WORLD); origen = 1; tag = 1; MPI_Recv(&B, 1, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); ... } else if (pid == 1) { ... destino = 0; tag = 1; MPI_Send(&B, 1, MPI_INT, destino, tag, MPI_COMM_WORLD); origen = 0; tag = 0; MPI_Recv(&A, 1, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); ... } ...

dlock4.c
************************************************************************************/ ... if (pid == 0) { ... destino = 1; tag = 0; MPI_Send(A, N, MPI_INT, destino, tag, MPI_COMM_WORLD); origen = 1; tag = 1; MPI_Recv(B, N, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); ... } else if (pid == 1) { ... destino = 0; tag = 1; MPI_Send(B, N, MPI_INT, destino, tag, MPI_COMM_WORLD); origen = 0; tag = 0; MPI_Recv(A, N, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); ... } ...

dlock5.c
*************************************************************************************/ ... if (pid == 0) { ... destino = 1; tag = 0; MPI_Send(A, N, MPI_INT, destino, tag, MPI_COMM_WORLD); origen = 1; tag = 1; MPI_Recv(B, N, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); ... } else if (pid == 1) { origen = 0; tag = 0; MPI_Recv(A, N, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); destino = 0; tag = 1; MPI_Send(B, N, MPI_INT, destino, tag, MPI_COMM_WORLD); ... } ...

/*************************************************************************************

MPI: send-dead.c
prueba para ver el tamaino dl buffer de la funcion send el programa se bloquera al enviar un paquete mas grande **************************************************************************************/ #include <stdio.h> #include "mpi.h" #define N 100000 int main(int argc, char** argv) { int pid, kont; int a[N], b[N], c[N], d[N]; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); for (kont=100; kont<=N; kont=kont+100) { if (pid == 0) { MPI_Send(&a, kont, MPI_INT, 1, 0, MPI_COMM_WORLD); MPI_Recv(&b, kont, MPI_INT, 1, 0, MPI_COMM_WORLD, &status); printf("emisor %d \n", kont); } else { MPI_Send(&c, kont, MPI_INT, 0, 0, MPI_COMM_WORLD); MPI_Recv(&d, kont, MPI_INT, 0, 0, MPI_COMM_WORLD, &status); printf(" receptor %d \n", kont); } } MPI_Finalize(); return 0; } /* main */

// Identificador del proceso

/*************************************************************************************

MPI: ssend.c
Ping-pong entre dos procesadores Comunicacion sincrona ssend **************************************************************************************/ #include <stdio.h> #include <mpi.h> #include <math.h> #define VUELTAS 4 double calculo() { #define N 5000 int i, j; double aux = 2.25; for(i=0; i<N; i++) for(j=0; j<N; j++) return(aux); } int main(int argc, char** argv) { double t0, t1, dat = 0.0, dat1, dat_rec; int pid, i; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); if (pid == 0) t0 = MPI_Wtime(); for(i=0; i<VUELTAS; i++) { if (pid == 0) { MPI_Ssend(&dat, 1, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD); dat1 = calculo(); MPI_Recv(&dat_rec, 1, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &status); dat = dat1 + dat_rec; } else { dat1 = calculo(); MPI_Recv(&dat_rec, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &status); dat = dat1 + dat_rec; MPI_Ssend(&dat, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); } } if (pid == 0) { t1 = MPI_Wtime(); printf("\n Tiempo de ejecucion = %f s \n", t1-t0); printf("\n Dat = %1.3f \n\n", dat); } MPI_Finalize(); return 0; } /* main */

aux = aux + rand() % 2;

/**************************************************************************************

MPI: isend.c
Ping-pong entre dos procesadores Comunicacion inmediata isend **************************************************************************************/ #include <stdio.h> #include <mpi.h> #include <math.h> #define VUELTAS 4 double calculo() { #define N 5000 int i, j; double aux = 2.25; for(i=0; i<N; i++) for(j=0; j<N; j++) return(aux); } int main(int argc, char** argv) { double t0, t1, dat = 0.0, dat1, dat_rec; int pid, i; MPI_Status status; MPI_Request request; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); if (pid == 0) t0 = MPI_Wtime(); for(i=0; i<VUELTAS; i++) { if (pid == 0) { MPI_Isend(&dat, 1, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD,&request); dat1 = calculo(); MPI_Wait(&request, &status); MPI_Recv(&dat_rec, 1, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &status); dat = dat1 + dat_rec; } else { dat1 = calculo(); if (i!=0) MPI_Wait(&request, &status); MPI_Recv(&dat_rec, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &status); dat = dat1 + dat_rec; MPI_Isend(&dat, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD,&request); } } if (pid == 0) { t1 = MPI_Wtime(); printf("\n Tiempo de ejecucion = %f s \n", t1-t0); printf("\n Dat = %1.3f \n\n", dat); MPI_Finalize(); return 0; } /* main */

aux = aux + rand() % 2;

/*************************************************************************************

inteser.c
Integral de una funcion mediante sumas de areas de trapecios **************************************************************************************/

#include <stdio.h> #include <sys/time.h> #include <math.h> double f(double x);

int main() { struct timeval double float int double double int

t0, t1; tej; // Limites de la integral // Numero de trapecios a sumar // Anchura de los trapecios

a=0.0, b=100.0; n; w; resul, x; i;

printf("\n Introduce a, b (limites) y n (num. de trap.) \n"); scanf("%f %f %d", &a, &b, &n); a = (double) a; b = (double) b; gettimeofday(&t0,0); // CALCULO DE LA INTEGRAL w = (b-a) / (double) n; resul = (f(a) + f(b)) / 2.0; x = a; for (i=1; i<n; i++) { x = x + w; resul = resul + f(x); } resul = resul * w; // anchura de los trapecios

gettimeofday(&t1,0); tej = (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec)/1e6;


printf("\n Integral (= ln x+1 + arctg x), de %1.1f a %1.1f, %d trap. = %1.12f\n",a,b,n,resul);

printf("

Tej (serie) = %1.3f ms \n\n", tej*1000);

return 0; } /* main */

// Funcion f = funcion a integrar double f(double x) { double y; y = 1.0 / (sin(x) + 2.0) + 1.0 / (sin(x)*cos(x) + 2.0); // y = 1.0 / (x + 1.0) + 1.0 / (x*x + 1.0); return y; }

/**************************************************************************************

MPI: intepar.c
Integral de una funcion medinate sumas de areas de trapecios Programa Paralelo MPI **************************************************************************************/ #include <mpi.h> #include <stdio.h> double t0, t1;

void Leer_datos(double* a_ptr, double* b_ptr, int* n_ptr, int pid, int npr); double Integrar(double a_loc, double b_loc, int n_loc, double w); double f(double x); int main(int argc, char** argv) { int pid, npr; double a, b, w, a_loc, b_loc; int n, n_loc; double resul, resul_loc; int MPI_Status origen, destino, tag; status;

// Identificador y numero de proc.

// Resultado de la integral

MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr); // Lectura y distribucion de parametros a todos los procesadores Leer_datos(&a, &b, &n, pid, npr); w = (b-a) / n; n_loc = n / npr; a_loc = a + pid * n_loc * w; b_loc = a_loc + n_loc * w; // Calculo de la integral local resul_loc = Integrar(a_loc, b_loc, n_loc, w); // Suma de resultados parciales if (pid == 0) { resul = resul_loc; tag = 0; for (origen=1; origen<npr; origen++) { MPI_Recv(&resul_loc, 1, MPI_DOUBLE, origen, tag, MPI_COMM_WORLD, &status); resul = resul + resul_loc; } } else { destino = 0; tag = 0; MPI_Send(&resul_loc, 1, MPI_DOUBLE, destino, tag, MPI_COMM_WORLD); } // Impresion de resultados if (pid == 0) { t1 = MPI_Wtime();
printf("\n Integral (= ln x+1 + atan x), de %1.1f a %1.1f, %d trap. = %1.12f\n", a,b,n,resul);

printf(" }

Tiempo ejecucion (%d proc.) = %1.3f ms \n\n", npr, (t1-t0)*1000);

MPI_Finalize(); return 0; } /* main */

// FUNCION Leer_datos void Leer_datos(double* a_ptr, double* b_ptr, int* n_ptr, int pid, int npr) { int origen, destino, tag; float aux_a, aux_b; MPI_Status status; if (pid == 0) { printf("\n Introduce a, b (limites) y n (num. de trap.) scanf("%f %f %d", &aux_a, &aux_b, n_ptr); t0 = MPI_Wtime(); (*a_ptr) = (double)aux_a; (*b_ptr) = (double)aux_b; for (destino=1; destino<npr; destino++) { tag = 0; MPI_Send(a_ptr, 1, MPI_DOUBLE, destino, tag, MPI_COMM_WORLD); tag = 1; MPI_Send(b_ptr, 1, MPI_DOUBLE, destino, tag, MPI_COMM_WORLD); tag = 2; MPI_Send(n_ptr, 1, MPI_INT, destino, tag, MPI_COMM_WORLD); } } else { origen = 0; tag MPI_Recv(a_ptr, tag = 1; MPI_Recv(b_ptr, tag = 2; MPI_Recv(n_ptr, } } /* Leer_datos */

\n");

= 0; 1, MPI_DOUBLE, origen, tag, MPI_COMM_WORLD, &status); 1, MPI_DOUBLE, origen, tag, MPI_COMM_WORLD, &status); 1, MPI_INT, origen, tag, MPI_COMM_WORLD, &status);

// FUNCION Integrar: calculo local de la integral double Integrar(double a_loc, double b_loc, int n_loc, double w) { double resul_loc, x; int i; // calculo de la integral resul_loc = (f(a_loc) + f(b_loc)) / 2.0; x = a_loc; for (i=1; i<n_loc; i++) { x = x + w; resul_loc = resul_loc + f(x); } resul_loc = resul_loc * w; return resul_loc; } /* Integrar */

// FUNCION f: funcion a integrar double f(double x) { double y; y = 1.0 / (x + 1.0) + 1.0 / (x*x + 1.0); return y; }

/************************************************************************************

MPI: intepar2.c
Integral de una funcion mediante sumas de areas de trapecios Broadcast para el envio de datos y Reduce para la recepcion **************************************************************************************/

... // Lectura y distribucion de parametros a todos los procesadores Leer_datos(&a, &b, &n, pid, npr); w = (b-a) / n; n_loc = n / npr; a_loc = a + pid * n_loc * w; b_loc = a_loc + n_loc * w;

// Calculo de la integral local resul_loc = Integrar(a_loc, b_loc, n_loc, w);

// Suma de resultados parciales resul = 0.0; root = 0; MPI_Reduce(&resul_loc, &resul, 1, MPI_DOUBLE, MPI_SUM, root, MPI_COMM_WORLD); ... } /* main */

// FUNCION Leer_datos void Leer_datos(double* a_ptr, double* b_ptr, int* n_ptr, int pid, int npr) { float aux_a, aux_b; int root; if (pid == 0) { printf("\n Introduce a, b (limites) y n (num. de trap.) scanf("%f %f %d", &aux_a, &aux_b, n_ptr); t0 = MPI_Wtime(); (*a_ptr) = (double)aux_a; (*b_ptr) = (double)aux_b; } root = 0; MPI_Bcast(a_ptr, 1, MPI_DOUBLE, root, MPI_COMM_WORLD); MPI_Bcast(b_ptr, 1, MPI_DOUBLE, root, MPI_COMM_WORLD); MPI_Bcast(n_ptr, 1, MPI_INT, root, MPI_COMM_WORLD); } /* Leer_datos */

\n");

/***********************************************************************************+

MPI: intepar3.c
Integral de una funcion mediante sumas de areas de trapecios Uso de pack/unpack y broadcast para la comunicacion USo de REDUCE para recoger los datos ************************************************************************************/

...

// FUNCION Leer_datos: pack / unpack y broadcast void Leer_datos(double* a_ptr, double* b_ptr, int* n_ptr, int pid, int npr) { float aux_a, aux_b; char buf[100]; int pos, root=0; if (pid == 0) { printf("Introduce a, b (limites) y n (num. de trap.<= 2*10e8 y mult. npr.)) scanf("%f %f %d", &aux_a, &aux_b, n_ptr); t0 = MPI_Wtime(); (*a_ptr) = (double)aux_a; (*b_ptr) = (double)aux_b; pos = 0; MPI_Pack(a_ptr, 1, MPI_DOUBLE, buf, 100, &pos, MPI_COMM_WORLD); MPI_Pack(b_ptr, 1, MPI_DOUBLE, buf, 100, &pos, MPI_COMM_WORLD); MPI_Pack(n_ptr, 1, MPI_INT, buf, 100, &pos, MPI_COMM_WORLD); MPI_Bcast(buf, 100, MPI_PACKED, root, MPI_COMM_WORLD); } else { MPI_Bcast(buf, 100, MPI_PACKED, root, MPI_COMM_WORLD); pos = 0; MPI_Unpack(buf, 100, &pos, a_ptr, 1, MPI_DOUBLE, MPI_COMM_WORLD); MPI_Unpack(buf, 100, &pos, b_ptr, 1, MPI_DOUBLE, MPI_COMM_WORLD); MPI_Unpack(buf, 100, &pos, n_ptr, 1, MPI_INT, MPI_COMM_WORLD); } } /* Leer_datos */

\n");

/**************************************************************************************

MPI: colec.c
Pruebas de comunicaciones colectivas ejecutar con 4 procesos **************************************************************************************/ #include <stdio.h> #include <mpi.h> int main(int argc, char** argv) { int pid, npr; int i, X; int A[8], B[2], C[2]; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr); /* Inicializaciones en todos los procesos */ for(i=0; i<8; i++) A[i] = -1; for(i=0; i<2; i++) B[i] = -1; for(i=0; i<2; i++) C[i] = -1; X = -1; if (pid == 0) { for(i=0; i<8; i++) A[i] = i; X = 6; } /* Broadcast de X desde P0 */ MPI_Bcast(&X, 1, MPI_INT, 0, MPI_COMM_WORLD); printf("1: BC de P0 --> P%d y X = %d \n", pid, X); MPI_Barrier(MPI_COMM_WORLD); /* Reparto de A (8 elementos) desde P0 en trozos de tamaino 2, en B */ MPI_Scatter(A, 2, MPI_INT, B, 2, MPI_INT, 0, MPI_COMM_WORLD); printf(" 2: SCATTER soy P%d y B = %d %d \n", pid, B[0], B[1]); MPI_Barrier(MPI_COMM_WORLD); /* Suma de los 4 vectores B (2 elementos) en el vector C en P0 */ MPI_Reduce(B, C, 2, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); printf(" 3: REDUCE en P%d C = %d %d \n", pid, C[0], C[1]); MPI_Barrier(MPI_COMM_WORLD); /* Suma de los 4 vectores B (2 elementos) en el vector C en todos los P */ MPI_Allreduce(B, C, 2, MPI_INT, MPI_SUM, MPI_COMM_WORLD); printf(" 4. ALLREDUCE en P%d C = %d %d \n", pid, C[0], C[1]); MPI_Barrier(MPI_COMM_WORLD); /* Recoleccion de los vectores B en el vector A en P0 */ for(i=0; i<2; i++) B[i] = B[i] + 10; MPI_Gather(B, 2, MPI_INT, A, 2, MPI_INT, 0, MPI_COMM_WORLD); printf(" 5: GATHER en P%d, A = %d %d %d %d %d A[0],A[1],A[2],A[3],A[4],A[5],A[6],A[7]); MPI_Barrier(MPI_COMM_WORLD); /* Recoleccion de los vectores B en el vector A en todos los P */ MPI_Allgather(B, 2, MPI_INT, A, 2, MPI_INT, MPI_COMM_WORLD); printf(" 6: ALLGATHER en P%d, A = %d %d %d %d A[0],A[1],A[2],A[3],A[4],A[5],A[6],A[7]); MPI_Finalize(); return 0; } /* main */ %d %d %d %d\n", pid,

%d

%d

%d\n",

pid,

/**************************************************************************************

MPI: sumvec.c
Calcula V(i) = V(i) * sumatorio(V(j)) Programa paralelo MPI **************************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <mpi.h> int main(int argc, char **argv) { int pid, npr; int N, N_loc, i; int *V, *V_loc, sum_loc, sum; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr); if (pid == 0) { printf("\nLongitud del vector (multiplo npr.): "); scanf("%d", &N); // Multiplo del numero de procesos N_loc = N / npr; V = (int *)malloc(sizeof(int) * N); for(i=0; i<N; i++) V[i] = rand() % 100 - 50; } //// Broadcast la longitud del vector local MPI_Bcast(&N_loc, 1, MPI_INT, 0, MPI_COMM_WORLD ); // inicializacion del vector

//// Reservar memoria para los vectores locales en cada proceso V_loc = (int *)malloc(sizeof(int)*N_loc);

//// Scatter o distribuir el vector a todos los procesos MPI_Scatter(V, N_loc, MPI_INT, V_loc, N_loc, MPI_INT, 0, MPI_COMM_WORLD);

//// Calcular la suma de todos los elemtos del vector local for (i=0, sum_loc=0; i<N_loc; i++) sum_loc += V_loc[i];

//// Calcular la suma global de los vectores MPI_Allreduce(&sum_loc, &sum, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);

//// Multiplicar la parte local del vector con la suma global for (i=0; i<N_loc; i++) V_loc[i] *= sum;

//// Gather o recoleccion de los vectores locales en el proceso root MPI_Gather(V_loc, N_loc, MPI_INT, V, N_loc, MPI_INT, 0, MPI_COMM_WORLD);

if (pid == 0) { printf(" sum = %d \n", sum); printf("\n\n V[0] = %d, V[N/2] = %d, V[N-1] = %d\n\n", V[0],V[N/2],V[N-1]); } MPI_Finalize(); return 0; }

/*************************************************************************************

MPI: topol_malla.c
Creacion de comunicadores en malla, filas y columnas Salida: coordenadas de los nodos en una topologia 2D; BC en filas y columnas de esa topologia 1. Crea un comunicador en malla 2D (toro) a partir del global 2. Imprime las coordenadas de los nodos en la nueva topologia 3. Usa MPI_Cart_sub para crear comunicadores para las filas 4. Efectua un BC en cada fila e imprime los resultados 3. Usa MPI_Cart_sub para crear comunicadores para las columnas 4. Efectua un BC en cada columna e imprime los resultados Nota: El numero de procesadores debe ser un cuadrado perfecto k x k

*************************************************************************************/
#include <stdio.h> #include "mpi.h" #include <math.h>

int main(int argc, char* argv[]) {


int int int MPI_Comm pid, pid_malla, pid_m, pid_fila, pid_col; npr, k, longi_dim[2], anillo[2], reordenar = 1; coord[2], coord_libre[2], pruebaf, pruebac; malla_com, fila_com, col_com;

MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &npr); MPI_Comm_rank(MPI_COMM_WORLD, &pid); // datos de la topologia en malla k = sqrt(npr); longi_dim[0] = longi_dim[1] = k; anillo[0] = anillo[1] = 1;

// numero de nodos en cada dimension // anillo (1) o cadena (0) en cada dim.

// creacion del nuevo comunicador con una malla asociada MPI_Cart_create(MPI_COMM_WORLD, 2, longi_dim, anillo, reordenar, &malla_com); //coordenadas del procesador en la nueva topologia en malla MPI_Comm_rank(malla_com, &pid_malla); //pid en la malla (reord=1!) MPI_Cart_coords(malla_com, pid_malla, 2, coord); //a la inversa, partiendo de las coordenadas, devuelve el valor del pid en la malla MPI_Cart_rank(malla_com, coord, &pid_m); if (pid==0) printf("\n ---- coordenadas en la malla \n"); printf("Proceso %d > pid_malla = %d, coords = (%d,%d), pid_m = %d\n", pid, pid_malla, coord[0], coord[1], pid_m); //creacion de comunicadores para las filas de la malla coord_libre[0] = 0; coord_libre[1] = 1; MPI_Cart_sub(malla_com, coord_libre, &fila_com); // prueba de comunicacion: el primer nodo de cada fila envia su pid al resto if (coord[1] == 0) pruebaf = pid; MPI_Bcast(&pruebaf, 1, MPI_INT, 0, fila_com); MPI_Comm_rank(fila_com, &pid_fila); if (pid==0) printf("\n ---- prueba de BC por filas \n"); printf("Proceso %d > coords = (%d,%d), pid_primero_fila = %d, pid_fila: %d\n", pid, coord[0], coord[1], pruebaf, pid_fila); //creacion de comunicadores para las columnas de la malla coord_libre[0] = 1; coord_libre[1] = 0; MPI_Cart_sub(malla_com, coord_libre, &col_com); // prueba de comunicacion: el primer nodo de cada fila envia su pid al resto if (coord[0] == 0) pruebac = pid; MPI_Bcast(&pruebac, 1, MPI_INT, 0, col_com); MPI_Comm_rank(col_com, &pid_col); if (pid==0) printf("\n ---- prueba de BC por columnas \n"); printf("Proceso %d > coords = (%d,%d), pid_primero_col = %d, pid_col: %d\n", pid, coord[0], coord[1], pruebac, pid_col); MPI_Finalize(); return 0; } /* main */

/**************************************************************************************

MPI: iopar1.c
P0 escribe en el fichero dat1 4 enteros por cada proceso Luego todos leen los 4 enteros que les corresponden *************************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <mpi.h> #define tambuf 4*32 int main(int argc, char **argv) { int pid, npr; int i, numdat; int buf[tambuf], buf2[tambuf], modoacceso; MPI_File dat1; MPI_Status info; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr); numdat = 4; if (pid == 0) for(i=0; i<npr*numdat; i++) buf[i] = i*i; if (pid == 0) { modoacceso = (MPI_MODE_CREATE | MPI_MODE_WRONLY); MPI_File_open(MPI_COMM_SELF, "dat1", modoacceso, MPI_INFO_NULL, &dat1); MPI_File_seek(dat1, 0, MPI_SEEK_SET); MPI_File_write(dat1, buf, npr*numdat, MPI_INT, &info); MPI_File_close(&dat1);
printf("\n P0 ha escrito %d datos, desde 0 hasta %d \n\n", npr*numdat, buf[npr*numdat-1]);

} sleep(3); modoacceso = MPI_MODE_RDONLY; MPI_File_open(MPI_COMM_WORLD, "dat1", modoacceso, MPI_INFO_NULL, &dat1); MPI_File_seek(dat1, pid*numdat*sizeof(int), MPI_SEEK_SET); MPI_File_read(dat1, buf2, numdat, MPI_INT, &info); MPI_File_close(&dat1);
printf(" > P%d ha leido %5d %5d %5d %5d \n", pid, buf2[0], buf2[1], buf2[2], buf2[3]);

MPI_Finalize(); return 0; }

/**************************************************************************************

MPI: iopar2.c
Cada proceso escribe en el fichero dat1 4 enteros, cada uno a continuacion del siguiente Luego, cada proceso lee los 4 entreos escritos por el proceso pid+1

*************************************************************************************/

#include <stdio.h> #include <stdlib.h> #include <mpi.h> #define tambuf 4*32 int main(int argc, char **argv) { int pid, npr; int i, numdat, numint; int buf[tambuf], buf2[tambuf], modoacceso; MPI_File dat1; MPI_Status info; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr); numdat = 4; numint = numdat * sizeof(int); for(i=0; i<numdat; i++) buf[i] = (i+pid*numdat)*(i+pid*numdat);

modoacceso = (MPI_MODE_CREATE | MPI_MODE_RDWR); MPI_File_open(MPI_COMM_SELF, "dat1", modoacceso, MPI_INFO_NULL, &dat1); MPI_File_seek(dat1, pid*numint, MPI_SEEK_SET); MPI_File_write(dat1, buf, numdat, MPI_INT, &info); MPI_File_sync(dat1); MPI_Barrier(MPI_COMM_WORLD); sleep(3); MPI_File_seek(dat1, ((pid+1)%npr)*numint, MPI_SEEK_SET); MPI_File_read(dat1, buf2, numdat, MPI_INT, &info); MPI_File_close(&dat1);
printf("\n > Pid %d ha leido %5d %5d %5d %5d \n", pid, buf2[0], buf2[1], buf2[2], buf2[3]);

MPI_Finalize(); return 0; }

/**************************************************************************************

MPI: iopar3.c
P0 escribe en un fichero en formato ascii ujna matriz de 8x8 enteros (es una prueba: el resto no hace nada) *************************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <mpi.h> #define tambuf 100 #define N 64 int main(int argc, char **argv) { int pid, npr; int i; int buf[tambuf], buf2[tambuf], modoacceso; char aux[20]; char linea[2000]; MPI_File dat2; MPI_Status info; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr);

if (pid == 0) for(i=0; i<N; i++) buf[i] = i*i; // Los 64 elementos de buf se convierten a ascci, 8 lineas de 8 elementos linea[0] = '\0'; for(i=0; i<N; i++) { if((i%8==0) && (i>0)) strcat(linea,"\n"); sprintf (aux, " %6d", buf[i]); strcat (linea, aux); } strcat(linea,"\n"); // ahora se escribe linea if (pid == 0) { modoacceso = (MPI_MODE_CREATE | MPI_MODE_WRONLY); MPI_File_open(MPI_COMM_SELF, "dat2", modoacceso, MPI_INFO_NULL, &dat2); MPI_File_seek(dat2, 0, MPI_SEEK_SET); MPI_File_write(dat2, linea, strlen(linea), MPI_CHAR, &info); MPI_File_close(&dat2); } MPI_Finalize(); return 0; }

/**************************************************************************************

MPI: iopar4.c
Leen entre todos los procesos el fichero ascci creado por iopar3.c y lo convierten a enteros. El fichero es de 8 lineas de 8 enteros. (ejecutar con 2, 4 u 8 procesos) **************************************************************************************/

#include <stdio.h> #include <stdlib.h> #include <mpi.h> #define N #define M 64 8*(8*7+1)

// numero total de caracteres del fichero dat2

int main(int argc, char **argv) { int pid, npr; int i, k; int buf[N], modoacceso; char aux[M]; MPI_File dat2; MPI_Status info; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr);

modoacceso =

MPI_MODE_RDONLY;

MPI_File_open(MPI_COMM_WORLD, "dat2", modoacceso, MPI_INFO_NULL, &dat2); MPI_File_seek(dat2, pid*M/npr, MPI_SEEK_SET); MPI_File_read(dat2, aux, M/npr, MPI_CHAR, &info); MPI_File_close(&dat2); // convertimos de ascci a enteros k = 0; for(i=0; i<N/npr; i++) { sscanf(&aux[k], "%d", &buf[i]); k = k+7; if ((i%8==0) && (i>0)) k++; } // cada proceso imprime los dos primeros y los dos ultimos datos que ha leido printf("\n P%d ha leido %5d %5d ... %5d %5d \n", pid, buf[0], buf[1], buf[N/npr-2], buf[N/npr-1]); MPI_Finalize(); return 0; }

Ejercicios a realizar
Te proponemos realizar 3 ejercicios. Al final, y junto con los ejercidos sobre OpenMP, hay que redactar un informe con los resultados obtenidos (numricos y grficos, programas), explicando de manera razonada el comportamiento que se observa. Los programas incluyen variables, funciones, etc. como ayuda, pero puedes modificarlos en lo que quieras (manteniendo la semntica, claro). No olvides aadir #include <mpi.h> al principio de los programas. Recuerda que el compilador icc (mpicc) tiene puesta por defecto la optimizacin O2. Para evitar problemas de interferencias con otras tareas y para obtener tiempos de ejecucin "fiables" vamos a realizar uno de estos dos procedimientos: - ejecutar el programa unas cuantas veces (5 por ejemplo) y quedarse con el menor tiempo de ejecucin. - o bien aadir un bucle al programa que repita la prueba N veces, medir los tiempos de ejecucin en cada repeticin y quedarse con el menor. Estos son los ejercicios a trabajar:

Ejercicio 1

integral_s.c / integral_p.c
f(x)

Tal como hemos visto, el programa calcula la integral de una funcin, en este caso f(x) = 1.0 / (sin(x) + 2.0) + 1.0 / (sin(x)*cos(x) + 2.0), mediante la suma del rea de un cierto nmero de trapecios que cubren la curva de la funcin entre dos puntos. El programa solicita los dos extremos de la funcin y el nmero de trapecios. integral_s.c es el programa serie e integral_p.c la versin paralela MPI (broadcast para el reparto de datos y reduce para la recogida de resultados).

El objetivo del ejercicio es medir el tiempo de ejecucin de ambos programas, usando los siguientes parmetros: - lmites de la integral: 0.0 y 100.0 (si quieres, los puedes aadir al programa como constantes) - nmero de trapecios: 1.120 - 11.200 - 112.000 - 1.120.000 - 11.200.000 - 112.000.000 - nmero de procesadores (caso paralelo): 2, 4, 8, 16 y 28 Tras ello, representa en un grfico los tiempos de ejecucin serie y paralelo en funcin del nmero de trapecios sumados, y en otro grfico el factor de aceleracin (speed-up) obtenido en relacin al caso serie. Utiliza las escalas ms adecuadas para los grficos, de manera que se observe correctamente el comportamiento del programa. Interpreta y justifica los resultados obtenidos.

Ejercicio 2

anillo.c

n procesos forman un anillo en el que la comunicacin es i (i+1) mod num_proc. Cada proceso enva al siguiente proceso en el anillo un vector. Al comenzar el programa, se pide la longitud del vector que se va a transmitir y el nmero de vueltas que debe dar al anillo de procesos. El objetivo del ejercicio es completar el programa y obtener el tiempo de ejecucin, en un anillo de 16 procesadores, de acuerdo a los siguientes parmetros: - nmero de vueltas al anillo: 4 - tamao del vector: 20, 21, 22, ..., 220 enteros Se recomienda hacer un bucle para todos los experimentos, empezando con un mensaje de 20 elementos e incrementando la longitud del vector que se enva multiplicndola por 2. Para obtener tiempos "fiables" repite el experimento varias veces (incluye un bucle en el programa) y qudate con el tiempo menor.

A partir de los resultados obtenidos, calcula el tiempo de transmisin i i+1 y el ancho de banda conseguido en la transmisin (MB/s), en funcin de la longitud del vector transmitido. Dibuja ambas curvas de manera adecuada e interpreta los resultados.

A escoger uno de estos dos ejercicios, 3 o 4.


Ejercicio 3
pladin.c

Se quiere repartir la ejecucin de una determinada cola de tareas de manera dinmica entre n procesos. Uno de los procesos (manager) hace la veces de gestor de la cola de tareas; su trabajo consiste en ir distribuyendo las tareas a peticin de los otros procesos. El resto de los procesos (workers) ejecuta la tarea encomendada, y, al finalizar la misma, solicita una nueva tarea, hasta terminar de esta manera con todas las tareas. Se trata de una estructura estndar de gestin dinmica de tareas y, como ejemplo de la misma, vamos a considerar el caso de procesamiento de una determinada matriz. El programa pladin_ser.c recoge la versin serie del trabajo que hay que realizar en paralelo. As pues, escribe el programa pladin.c, que procesa en paralelo la matriz M repartiendo las filas de la matriz de manera dinmica, tipo self scheduling, es decir, fila a fila. Recuerda: uno de los procesos, P0, es el encargado de ir repartiendo los datos bajo demanda, y el resto, n1 procesos, procesan la matriz fila a fila. Confirma los resultados de la versin paralela con los que obtienes en la versin serie. Finalmente, obtn los tiempos de ejecucin y el speed-up para los casos de 1+1, 1+2, 1+4, 1+8, 1+16 y 1+27 procesos. Si quieres, prueba con otro tipo de scheduling y compara los resultados que obtengas.

Ejercicio 4

imagen.c

El procesado de imgenes es una tarea tpica que se puede efectuar en paralelo y que ofrece buenos resultados. Una imagen es simplemente una matriz de pxeles, y sobre ella se efectan diferentes operaciones matemticas. Como ejemplo de este tipo de tareas, te proponemos paralelizar el programa imagen.c, que efecta sobre una imagen de entrada en formato pgm (portable grey map), escala de grises [0 .. 255] unas cuantas operaciones tpicas. En concreto, aplica dos veces a los pxeles de la imagen las siguientes operaciones: 1. Mscara para extraer contornos:
imagen[i][j] = 8*imagen[i][j] imagen[i-1][j-1] imagen[i-1][j] imagen[i-1][j+1] - imagen[i][j-1] imagen[i][j+1] imagen[i+1][j-1] imagen[i+1][j] imagen[i+1][j+1]

La mscara corresponde a una pequea matriz de 33 elementos con contenido: -1 -1 -1 -1 8 -1 -1 -1 -1 que se aplica a los elementos de la imagen original. Esta mscara se conoce como mscara de Laplace y tiene como resultado resaltar los contornos o siluetas de la imagen. 2. Proceso de "umbralizacin": Tras "filtrar" la imagen con la mscara anterior, los pxeles de valor menor que uno dado se convierten al valor mnimo (0, negro), y los de mayor valor que uno dado, al valor mximo (255, blanco).

Tras efectuar el proceso dos (NUM_ITER) veces, se obtienen dos "proyecciones" de la imagen final: proyeccin vertical y proyeccin horizontal, vectores que contabilizan, respectivamente, el nmero de pxeles de cada columna y de cada fila de la imagen final que son distintos de 0 (negro). Como resultado final del programa se genera una imagen que agrupa a la imagen original procesada y a las imgenes correspondientes a las dos proyecciones. Si el fichero imagen de partida es pz.pgm, el resultado queda en el fichero pz_imagconproy.pgm, adems de otros cuatro ficheros pgm con las imgenes de salida de cada iteracin del bucle (pz_lp0 y pz_lp1) y las dos proyecciones (pz_proy_hori y pz_proy_vert). El programa serie imagen.c contiene unas cuantas rutinas ya programadas que permiten efectuar estas operaciones. El fichero pixmap.c (y pixmap.h) contiene las rutinas necesarias para poder leer y almacenar imgenes en el formato estndar pgm. Para visualizar las imgenes se puede utilizar cualquier visor compatible con el formato pgm. Por ejemplo, est instalada la aplicacin ImageMagick que permite ver las imgenes y efectuar transformaciones sencillas de las mismas. Los dos comandos principales son display y convert. Por ejemplo: > display imagen.pgm Presenta en pantalla la imagen contenida en el fichero imagen.pgm. [ display -help da informacin sobre el comando ] > convert imagen.jpg imagen.pgm Convierte la imagen en formato jpg al formato pgm. [ convert -help da informacin sobre el comando ] El programa imagen.c utiliza un struct para guardar las imgenes, con la siguiente estructura:
typedef struct { int w; int h; unsigned char *dat; unsigned char **im; } imagen; imagen i1;
/* /* /* /* numero numero imagen vector de columnas */ de filas */ almacenada en bytes [0..255] */ de punteros a filas - acceso bidimensional */

/* declaracion de la variable i1 de tipo imagen */

Los dos primeros enteros indican el tamao de la imagen; dat almacena todos los bytes de la imagen en un vector de longitud w*h; im es un vector de punteros que apuntan al comienzo de cada fila de la imagen, para poder referenciar la imagen como imagen.im[i][j]. Para compilar tu programa, ejecuta > cc o imagen imagen.c pixmap.c > mpicc o imagen imagen.c pixmap.c (versin serie) (versin paralela MPI)

Sugerencias para la versin paralela. El procesado de la imagen se puede paralelizar de muchas maneras. Por ejemplo, se puede repartir la imagen por filas entre los procesos. En todo caso, probablemente el tamao de la imagen no ser mltiplo del nmero de procesos, por lo que habr que repartir trozos de tamao diferente. Una solucin sencilla es asignar al "ltimo" proceso el trozo ms grande si la divisin no es exacta. Recuerda que hay funciones de reparto y de recogida de datos en las que los trozos que se mueven son de tamao variable (son las funciones tipo MPI_Gatherv, MPI_Scatterv...), que son adecuadas para este caso. Previo a escribir cdigo paralelo, te recomendamos que, tras entender cmo funciona el proceso en serie, hagas un esquema bsico de cmo quieres repartir y procesar los datos, para tener claro el tipo de funciones MPI que debes utilizar. Finalmente, prueba tu programa con diferente nmero de procesos y compara los resultados (la imagen final) con los que obtienes del caso serie.

Ejercicio 2: anillo.c
/***************************************************************************** MPI: anillo.c Se envia un vector de proceso a proceso en un anillo de npr procesos Se calcula el tiempo de transmision y el ancho de banda. Pide la longitud del vector y el numero de vueltas a dar. Repite el proceso nrep veces y una mas previa de calentamiento Repite el experimento para diferentes longitudes del vector, desde 1 hasta 2**longi OJO compilar con lm --por completar *********************************************************************************/ #include <stdio.h> #include <mpi.h> #define nrep 10 // numero de repeticiones del experimento //

int main(int argc, char *argv[]) { int pid, npr; int siguiente, anterior; int *vector, longi, lgmax, nvueltas, tam; int i;

MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr); // direcciones de los vecinos en el anillo // siguiente = (pid + 1) % npr; anterior = (pid + npr - 1) % npr; // lectura de parametros // if (pid == 0) { printf("\n Longitud maxima del vector a enviar (potencia de 2, maximo 22): scanf("%d", &lgmax); printf(" Numero de vueltas en el anillo: "); scanf("%d", &nvueltas); } // Distribucion de los datos a todos los procesos // /* Reserva de memoria, inicializacion del vector */ tam = pow(2,lgmax); vector = (int *) mallococ(sizeof(int)*tam); if (pid == 0) for(i=0; i<tam; i++) vector[i] = nvueltas; // COMIENZO DE LA TRANSMISION //

");

// desde longi = 1 hasta el valor maximo solicitado (en potencias de 2) // repetir el proceso nrep veces (para hacer media de los tiempos de transmision)
// transmitir vector de tamao longi del 0 al 1, del 1 al 2, etc, dando nvueltas al anillo

// Finalmente, obtener como resultado el tiempo de una vuelta en el anillo, // el tiempo de transmision i --> i+1 y el ancho de banda de la transmision // i-->i+1 = num_bytes / tiempo (en MB/s) MPI_Finalize(); return 0; }

Ejercicio 3: pladin_ser.c
/************************************************************************ pladin_ser.c version serie de un sistema de gestion de una cola de tareas (filas de una matriz) mediante palnificacion dinamica **************************************************************************/ #include <stdio.h> #include <sys/time.h> #define NF 3000 #define NC 500 int main(int argc, char **argv) { double tej; struct timeval t0,t1; int int i, j, k, x; M[NF][NC], sum;

// inicializacion de la matriz for(i=0; i<NF; i++) for(j=0; j<NC; j++) M[i][j] = rand() % 1000 - 200; gettimeofday(&t0,0);

// Procesamiento de la matriz. CODIGO A PARALELIZAR for (i=0; i<NF; for (j=0; j<NC; { if (M[i][j] > { x = 1; for (k=2; k M[i][j] = x } } i++) j++) 0)

<= M[i][j]; k++) x = (x * k) % (M[i][j]+1); % (M[i][j]+1);

sum = 0; for (i=0; i<NF; i++) for (j=0; j<NC; j++) sum = (sum + M[i][j]) % 1000;

gettimeofday(&t1,0); // Imprimir resultados printf("\n sum = %d\n", sum); tej = (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec)/1000000.0; printf(" \n Tej = %1.3f s\n\n", tej); return 0; }

Ejercicio 4: imagen.c
/********************************************************************************** Fichero: imagen.c Se aplica a una imagen en formato PGM (P5: binario) el siguiente tratamiento para resaltar los contornos de la imagen: -- una mascara de laplace de 3 x 3 -- unos umbrales maximo y minimo El proceso se repite NUM_ITER (2) veces. De la imagen final se calcula un histograma por filas y otro por columnas, para contar el numero de pixeles distintos de 0 (negro). Ficheros de entrada: Ficheros de salida: xx.pgm (imagen en formato pgm) xx_imagconproy.pgm imagen final e histogramas xx_lp1 / xx_lp2 ... imagenes parciales de cada iteracion xx_proy_hori.pgm histograma por filas xx_proy_vert.pgm histograma por columnas

Compilar el programa junto con pixmap.c **********************************************************************************/

#include #include #include #include #define #define #define #define #define

<stdio.h> <stdlib.h> <sys/time.h> "pixmap.h" NEGRO 0 BLANCO 255 MAX_VAL 256 NUM_ITER 2 TAM_PROY 150

/* Rango de posibles valores de grises */ /* Numero de repeticiones del proceso de la imagen */ /* Los histogramas son de 150 * h/w pixeles */

struct timeval t0,t1; double tej;

/*

FUNCIONES DE GESTION DE IMAGENES

*/

/* 1: Copia la imagen iimagen en oimagen. Reserva memoria */ /*********************************************************************************/

void copiar_imagen (imagen *oimagen, imagen iimagen) { int i, h, w; h = iimagen.h; w = iimagen.w; if ((oimagen->dat = (unsigned char *) malloc(w*h * sizeof(unsigned char))) == NULL) { fprintf(stderr, "Funcion copiar_imagen: malloc error\n"); exit(1); } oimagen->h = h; oimagen->w = w; oimagen->im = (unsigned char **) malloc(h * sizeof(unsigned char *)); for (i=0; i<h; i++) oimagen->im[i] = &oimagen->dat[i*w]; memcpy(oimagen->dat, iimagen.dat, h*w); return; }

/* 2: Crea una imagen y la inicializa a un valor constante */ /***********************************************************************************/

void generar_imagen(imagen *oimagen, int h, int w, unsigned char val) { int i, j; oimagen->h = h; oimagen->w = w; // asignar memoria a la imagen if ((oimagen->dat = (unsigned char *) malloc(w*h * sizeof(unsigned char))) == NULL) { fprintf(stderr, "Funcion generar_imagen: malloc error\n"); exit(1); } // crear la estructura de punteros a las filas if ((oimagen->im = (unsigned char **) malloc(h * sizeof(unsigned char *))) == NULL) { fprintf(stderr, "Funcion generar_imagen: malloc error\n"); exit(1); } for(i=0; i<h; i++) oimagen->im[i] = &oimagen->dat[i*w]; // inicializar los pixeles de la imagen memset(oimagen->dat, val, h*w); return; }

/* 3: Libera memoria de una imagen */ /***********************************************************************************/

void liberar_imagen(imagen iimagen) { free(iimagen.dat); free(iimagen.im); return; }

/*

FUNCIONES DE TRATAMIENTO DE IMAGENES

*/

/*4: Calcula un vector con la proyeccion vertical (reserva memoria) */ /***********************************************************************************/

void calcular_proyeccion_vertical(int **proy, imagen iimagen) { int i, j, h, w; h = iimagen.h; w = iimagen.w; // Reservar memoria para el vector proyeccion if ( (*proy = (int *) malloc(w * sizeof(int))) == NULL ) { fprintf(stderr, "Funcion calcular_proyeccion_vertical: malloc error\n"); exit(1); } for (j=0; j<w; j++) (*proy)[j] = 0; for (i=0; i<h; i++) for (j=0; j<w; j++) if (iimagen.im[i][j] != NEGRO) (*proy)[j]++; return; }

/* 5: Crea una imagen con la proyeccion vertical (reserva memoria) */ /***********************************************************************************/

void crear_imagen_proyeccion_vertical(imagen *proyimagen,int *proy, int longi) { int maximo, valor; int i, j; // Crea una imagen para la proyeccion vertical, toda en negro generar_imagen(proyimagen, TAM_PROY, longi, NEGRO); // calcula el valor maximo para escalar la imagen del histo a 150 pixeles maximo = -1; for (i=0; i<longi; i++) if(proy[i] > maximo) maximo = proy[i]; for (j=0; j<longi; j++) { if (maximo == 0) valor = 0; else valor = proy[j] * TAM_PROY/maximo;

// por si max fuera 0 // escalar al valor maximo

// deja una linea en negro, de tamaino proporcional al valor correspondiente for (i=0; i < TAM_PROY-valor; i++) proyimagen->im[i][j] = BLANCO; } return; }

/* 6: Calcula un vector con la proyeccion horizontal (reserva memoria) */ /***********************************************************************************/

void calcular_proyeccion_horizontal(int **proy, imagen iimagen) { int i, j, h, w; h = iimagen.h; w = iimagen.w; // Reservar memoria para el vector proyeccion if ( (*proy = (int *) malloc(h * sizeof(int))) == NULL ) { fprintf(stderr, "Funcion calcular_proyeccion_horizontal: malloc error\n"); exit(1); } for (i=0; i<h; i++) (*proy)[i] = 0; for (i=0; i<h; i++) for (j=0; j<w; j++) if (iimagen.im[i][j] != NEGRO) (*proy)[i]++; return; }

/* 7: Crea una imagen con la proyec. horizontal (reserva memoria) */ /***********************************************************************************/

void crear_imagen_proyeccion_horizontal(imagen *proyimagen,int *proy, int longi) { int maximo, valor; int i, j; // Crea una imagen para la proyeccion vertical, toda en negro generar_imagen(proyimagen, longi, TAM_PROY, NEGRO); // calcula el valor maximo para escalar la imagen del histo a 150 pixeles maximo = -1; for (i=0; i<longi; i++) if(proy[i] > maximo) maximo = proy[i]; for (i=0; i<longi; i++) { if (maximo == 0) valor = 0; else valor = proy[i]* TAM_PROY/maximo;

// por si max fuera 0 // escalar al valor maximo

// deja una linea en negro, de tamaino proporcional al valor correspondiente for (j=0; j < TAM_PROY-valor; j++) proyimagen->im[i][j] = BLANCO; } return; }

/* 8: Crea una unica imagen con iimagen y sus dos proyecciones */ /***********************************************************************************/

void {

crear_imagen_con_proyecciones(imagen *oimagen, imagen iimagen, imagen proyvert, imagen proyhori)

int i, j, w, h; h = oimagen->h = iimagen.h + proyvert.h; w = oimagen->w = iimagen.w + proyhori.w; // Crea la imagen total, inicializada a negro generar_imagen(oimagen, h, w, NEGRO); // Copia iimagen for (i=0; i<iimagen.h; i++) for (j=0; j<iimagen.w; j++) oimagen->im[i][j] = iimagen.im[i][j]; // copia a la derecha la proyeccion horizontal for (i=0; i<proyhori.h; i++) for (j=0; j<proyhori.w; j++) oimagen->im[i][iimagen.w + j] = proyhori.im[i][j]; // copia debajo la proyeccion vertical for (i=0; i<proyvert.h; i++) for (j=0; j<proyvert.w; j++) oimagen->im[i + iimagen.h][j] = proyvert.im[i][j]; //printf ("\nCreada imagen con proyecciones vertical y horizontal\n"); return; }

/* 9: Filtro de laplace (3x3): detecta ejes (iimagen -> oimagen) */ /***********************************************************************************/

void aplicar_laplace_contorno(imagen *oimagen, imagen iimagen) { int suma; int i, j, k, l, h, w; int mask_laplace[3][3]; // Mascara de laplace mask_laplace[0][0] = -1; mask_laplace[0][1] = -1; mask_laplace[0][2] = -1; mask_laplace[1][0] = -1; mask_laplace[1][1] = 8; mask_laplace[1][2] = -1; mask_laplace[2][0] = -1; mask_laplace[2][1] = -1; mask_laplace[2][2] = -1; h = iimagen.h; w = iimagen.w; generar_imagen(oimagen, h, w, NEGRO); // Aplicar mascara y dejar resultado en oimagen for (i=0; i<=h-1; i++) for (j=0; j<=w-1; j++) { if (i==0 || i==h-1) suma = 0; // los bordes de la imagen se dejan en negro (0) else if(j==0 || j==w-1) suma = 0; else { suma = 0; for (k=-1; k<=1; k++) for (l=-1; l<=1; l++) suma = suma + ((int)iimagen.im[i+k][j+l] * mask_laplace[k+1][l+1]); } if (suma<0) suma = 0; if (suma>255) suma = 255; (oimagen->im)[i][j] = (unsigned char)suma; } //printf ("\nAplicado laplace_contorno\t\t(w: %d h: %d)\n",w,h); return; }

/* 10: Aplica umbrales minimo y maximo a la imagen */ /***********************************************************************************/

void aplicar_umbrales(imagen *oimagen) { int i, j, h, w; // Valores umbral: por debajo o por encima, se convierten al minimo o al maximo unsigned char umbral_min = 40; unsigned char valor_min = 0; unsigned char umbral_max = 215; unsigned char valor_max = 255; h = oimagen->h; w = oimagen->w; for (i=0; i<=h-1; i++) for (j=0; j<=w-1; j++) { if (oimagen->im[i][j] <= umbral_min) oimagen->im[i][j] = valor_min; else if (oimagen->im[i][j] >= umbral_max) oimagen->im[i][j] = valor_max; } return; }

/************************* MAIN **********************/ /***********************************************************************************/

int main(int argc, char **argv) { int i, sum=0; char *name; name = malloc (sizeof(char)*100); // Imagenes de salida de cada iteracion; 0 = entrada imagen lpimagen[NUM_ITER+1]; // Vectores de proyecciones e imagenes correspondientes int *proy_vert, *proy_hori; imagen proyimagevert, proyimagehori, imagenconproyecciones; if (argc != 2) { printf ("\nUSO: programa imagen\n"); printf (" [extension .pgm implicita]\n"); exit (0); } // Lectura de la imagen de entrada: solo imagenes graylevel en formato .pgm strcpy(name, argv[1]); strcat(name,".pgm"); if ( load_pixmap(name, &lpimagen[0]) == 0 ) { printf ("\nError en lectura del fichero de entrada: %s\n\n",name); exit (0); }
printf("\n --> Procesando imagen de hxw = %dx%d pix.\n", lpimagen[0].h, lpimagen[0].w);

gettimeofday(&t0, 0);

/* Proceso imagen: NUM_ITER veces (laplace + umbral) */ /*********************************************************************************/

for (i=1; i<=NUM_ITER; i++) { aplicar_laplace_contorno(&lpimagen[i], lpimagen[i-1]); aplicar_umbrales(&lpimagen[i]); }

/* Calculo de las proyecciones de la imagen final */ /*********************************************************************************/

calcular_proyeccion_vertical(&proy_vert, lpimagen[NUM_ITER]); calcular_proyeccion_horizontal(&proy_hori, lpimagen[NUM_ITER]);

/* test de prueba y toma de de tiempos */ /*********************************************************************************/

gettimeofday(&t1, 0);
// test de prueba: sumar el valor de todos los pixeles de la imagen final, modulo 255

for (i=0; i<lpimagen[NUM_ITER].h * lpimagen[NUM_ITER].w; i++) sum = (sum + lpimagen[NUM_ITER].dat[i]) % 255; printf("\n Test de prueba sum = %d \n", sum); tej = (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) / 1e6; printf("\n Tej. serie = %1.1f ms\n\n", tej*1000);

/* Escritura de resultados en disco; liberar memoria */ /*********************************************************************************/

// Guardar imagenes de salida lpimagen[i], proyecciones e imagen final conjunta for (i=1; i<=NUM_ITER; i++) { sprintf(name, "%s_lp%d.pgm", argv[1], i); store_pixmap(name, lpimagen[i]); } strcpy(name, argv[1]); name = strcat(name, "_proy_vert.pgm");
crear_imagen_proyeccion_vertical(&proyimagevert, proy_vert, lpimagen[NUM_ITER].w);

store_pixmap(name, proyimagevert); strcpy(name, argv[1]); name = strcat(name, "_proy_hori.pgm");


crear_imagen_proyeccion_horizontal(&proyimagehori, proy_hori, lpimagen[NUM_ITER].h);

store_pixmap(name, proyimagehori); strcpy(name, argv[1]); name=strcat(name, "_imagconproy.pgm"); crear_imagen_con_proyecciones(&imagenconproyecciones, lpimagen[NUM_ITER], proyimagevert, proyimagehori); store_pixmap(name, imagenconproyecciones);

// Liberar memoria de las imagenes y proyecciones for (i=0; i<=NUM_ITER; i++) liberar_imagen(lpimagen[i]); liberar_imagen(imagenconproyecciones); liberar_imagen(proyimagevert); liberar_imagen(proyimagehori); free(proy_hori); free(proy_vert); free(name); return 0; }

You might also like