Master SIA Facultad de Informática Programación de Sistemas Paralelos MPI: Ejemplos > Máquinas y directorios Vamos a trabajar con un cluster sencillo de 32 nodos (Intel Core 2 6320 - 1,8 GHz - 2 GB RAM - 4 kB cache) más un nodo similar para desarrollo, conectados todos 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. El nodo de desarrollo hace las veces de servidor de ficheros y de punto de entrada al cluster; su dirección externa es g002624.gi.ehu.es, y su dirección en el cluster es servidornodo00. El resto de los nodos, nodo01, nodo02... nodo32 sólo son accesibles desde el nodo00 (no tienen conexión al exterior). Cada grupo tiene abierta una cuenta en el cluster, de nombre csiaxx, donde xx es el mismo número de cuenta que en el caso de las cuentas en la máquina SMP. Para conectarte (linux): > ssh –l csia01 g002624.gi.ehu.es El directorio templates contiene los ejemplos y las prácticas que vamos a utilizar. Haz una copia a tu directorio de trabajo. Para poder ejecutar aplicaciones utilizando diferentes máquinas 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 máquina, para que nos reconozca como "usuarios autorizados". Hay 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 continuación, entrar y salir, una por una, en las máquinas del cluster: > ssh nodoxx (yes) > exit (xx = 01 ... 32) > MPICH2 Vamos a utilizar la versión MPICH2 de MPI (es de libre distribución; si queréis la podéis instalar en vuestro PC; otra implementación de gran difusión es LAM / Open MPI). Previo a ejecutar programas en paralelo, hay que: - Crear un fichero de nombre .mpd.conf que contenga una línea con el siguiente contenido: secretword=palabra (cualquier palabra) y cambiarle las protecciones: > chmod 600 .mpd.conf - Poner en marcha el "entorno" MPICH2 (unos daemons mpd que van a correr en cada máquina del cluster): > mpdboot -v -n num_proc -f fichero_maquinas fichero_maquinas contiene los "nombres" de las máquinas del cluster con las que se quiere trabajar (por defecto se utiliza el fichero mpd.hosts). En el caso máximo, lanzaremos 33 (1+32) daemons, aunque trabajaremos con 32 como máximo. Otros comandos útiles: > mpdtrace > mpdlistjobs > mpdringtest 2 > mpd & > mpd –help devuelve las máquinas "activas" lista los trabajos en ejecución en el anillo de daemons recorre el anillo de daemons (2 veces) y devuelve el tiempo lanza un solo daemon en el procesador local información del comando Si ha habido algún problema con la generación de daemons, etc., ejecutar mpdcleanup y repetir el proceso. - A continuación podemos compilar y ejecutar programas: > mpicc [-Ox] -o pr1 pr1.c [x=1-3 nivel de optimiz.; por defecto, 2] > mpiexec -n xx pr1 xx = número de procesos ejecuta el programa pr1 en xx procesadores (el flag -1 hace que no se lance el proceso en el nodo desde el que se han lanzado los daemons). > 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 sesión de trabajo ejecutamos: > mpdallexit (para más información sobre estos comandos comando --help) > JUMPSHOT Jumpshot es la herramienta de análisis de la ejecución de programas paralelos que se distribuye junto con MPICH. Es una herramienta compleja, que permite analizar gráficamente ficheros de trazas (tipo "log") obtenidos de la ejecución de programas MPI en los que previamente se han añadido una serie de llamadas a MPE para recoger información. Para generar el fichero log podemos añadir puntos de muestreo en lugares concretos (añadiendo funciones de MPE), o, en casos sencillos, tomar datos de todas las funciones MPI. > mpicc -mpe=mpilog -o prog prog.c > mpiexec –n xx prog 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 hay que efectuar una conversión a dicho formato. Esa conversión puede hacerse ya dentro de la aplicación o ejecutando: > clog2TOslog2 prog.clog2 La ventana inicial de jumpshot nos permite escoger el fichero que queremos visualizar. En una nueva ventana aparece un esquema gráfico de la ejecución, en el que las diferentes funciones de MPI se representan con diferentes colores. En el caso de las comunicaciones punto a punto, una flecha une la emisión y recepción de cada mensaje. Con el botón derecho del ratón podemos obtener datos de las funciones ejecutadas y de los mensajes transmitidos. NOTA: para poder ejecutar esta aplicación gráfica (u otras) de manera remota: -- desde windows: ejecutar previamente X-Win32 (o una aplicación similar) -- desde linux: entrar en la máquina ejecutando ssh –X -l cuenta máquina /********************************************************************************** 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]; 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; } /********************************************************************************** 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[15],v[14],v[13], v[12],v[11],v[10],v[9],v[8],v[7],v[6],v[5],v[4],v[3],v[2],v[1],v[0], 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); /********************************************************************************** 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[0], 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[0], 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(); } EJERCICIOS >> envio1 Modifica el programa envio para que devuelva a P0 la suma de los elementos del vector recibido y éste último imprima el resultado. >> circu1 Modifica el programa circu para que P0 imprima el número de soluciones obtenidas. /**************************************************************** probe.c ejemplo de uso de la funcion probe de MPI ****************************************************************/ #include <mpi.h> #include <stdio.h> int main (int argc, char **argv) { int pid, npr, origen, destino, ndat, tag; int i, longitud, tam; int *VA, *VB; MPI_Status info; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &pid); MPI_Comm_size(MPI_COMM_WORLD, &npr); if (pid == 0) { srand(time(NULL)); longitud = rand() % 100; VA = (int *) malloc (longitud*sizeof(int)); for (i=0; i<longitud; i++) VA[i] = i; printf("\n valor de VA en P0 antes de enviar los datos\n\n"); for (i=0; i<longitud; i++) printf("%4d", VA[i]); printf("\n\n"); destino = 1; tag = 0; MPI_Send(VA, longitud, MPI_INT, destino, tag, MPI_COMM_WORLD); free(VA); } else if (pid == 1) { origen = 0; tag = 0; MPI_Probe(origen, tag, MPI_COMM_WORLD, &info); MPI_Get_count(&info, MPI_INT, &tam); if(tam != MPI_UNDEFINED) { VB = (int *) malloc(tam*sizeof(int)); MPI_Recv(VB, tam, MPI_INT, origen, tag, MPI_COMM_WORLD, &info); } printf("\n valor de VB en P1 tras recibir los datos\n\n"); for (i=0; i<tam; i++) printf("%4d", VB[i]); printf("\n\n"); free(VB); } MPI_Finalize(); } /********************************************************************************** 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 C es %d en proc 0 \n\n", C); } 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 C es %d en proc 1 \n\n", C); } MPI_Finalize(); } /********************************************************************************** send-dead.c prueba para ver el tamaino del buffer de la funcion send el programa se bloquea 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; // Identificador del proceso 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[0], kont, MPI_INT, 1, 0, MPI_COMM_WORLD); MPI_Recv(&b[0], kont, MPI_INT, 1, 0, MPI_COMM_WORLD, &status); printf("emisor %d \n", kont); } else { MPI_Send(&c[0], kont, MPI_INT, 0, 0, MPI_COMM_WORLD); MPI_Recv(&d[0], kont, MPI_INT, 0, 0, MPI_COMM_WORLD, &status); printf(" receptor %d \n", kont); } } MPI_Finalize(); return 0; } /* main */ /********************************************************************************** ssend.c Ping-pong entre dos procesadores Comunicacion sincrona ssend **********************************************************************************/ #include #include #include #include <stdio.h> <mpi.h> <math.h> <unistd.h> #define VUELTAS 4 double calculo() { double aux; sleep(1); aux = rand() % 100; 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 */ /********************************************************************************** isend.c Ping-pong entre dos procesadores Comunicacion inmediata isend **********************************************************************************/ #include <stdio.h> #include <mpi.h> #include <unistd.h> #define VUELTAS 4 double calculo() { double aux; sleep(1); aux = rand() % 100; 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 */ /********************************************************************************** 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[0], 2, MPI_INT, &B[0], 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[0], &C[0], 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[0], &C[0], 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[0], 2, MPI_INT, &A[0], 2, MPI_INT, 0, MPI_COMM_WORLD); printf(" 5: GATHER en P%d, A = %d %d %d %d %d %d %d A[0],A[1],A[2],A[3],A[4],A[5],A[6],A[7]); %d\n", pid, MPI_Barrier(MPI_COMM_WORLD); /* Recoleccion de los vectores B en el vector A en todos los P */ MPI_Allgather(&B[0], 2, MPI_INT, &A[0], 2, MPI_INT, MPI_COMM_WORLD); printf(" 6: ALLGATHER en P%d, A = %d %d %d %d %d %d %d %d\n", pid, A[0],A[1],A[2],A[3],A[4],A[5],A[6],A[7]); MPI_Finalize(); return 0; } /* main */ EJERCICIO /********************************************************************************* 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; a=0.0, b=100.0; n; w; resul, x; i; // Limites de la integral // Numero de trapecios a sumar // Anchura de los trapecios 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; // anchura de los trapecios 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; 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 / (x + 1.0) + 1.0 / (x*x + 1.0) ; return y; } >> Completa el programa intepar0, que calcula la integral de la funcion f = 1/(x+1) + 1/(x2+1) mediante sumas de areas de trapecios bajo la curva que representa la función. Utiliza funciones de comunicación en grupo /********************************************************************************** intepar0.c Integral de una funcion mediante sumas de areas de trapecios -- para completar **********************************************************************************/ #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, resto, kont; double resul, resul_loc; int MPI_Status // Identificador y numero de proc. // Resultado de la integral origen, destino, tag; status; 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_loc = n resto = n if (pid < / n; // cada uno calcula el trozo que tiene que sumar / npr; % npr; resto) n_loc = n_loc + 1; a_loc = a + pid * n_loc * w; if (pid >= resto) a_loc = a_loc + resto * w; b_loc = a_loc + n_loc * w; // Calculo de la integral local resul_loc = Integrar(a_loc, b_loc, n_loc, w); // Sumar resultados parciales // (POR COMPLETAR) // 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 pr.) = %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.<= 10e8) scanf("%f %f %d", &aux_a, &aux_b, n_ptr); \n"); t0 = MPI_Wtime(); (*a_ptr) = (double)aux_a; (*b_ptr) = (double)aux_b; // a_ptr, b_ptr, n_ptr: punteros a a, b y n // distribuir datos a todos los procesos // (POR COMPLETAR) } } /* Leer_datos */ // FUNCION Integrar: calculo local de la integral double Integrar(double a_loc, double b_loc, int n_loc, double h) { 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; } /********************************************************************************** intepar3.c // 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){ ... // lectura pos = 0; MPI_Pack(a_ptr, MPI_Pack(b_ptr, MPI_Pack(n_ptr, de los parametros de la integral 1, MPI_DOUBLE, buf, 100, &pos, MPI_COMM_WORLD); 1, MPI_DOUBLE, buf, 100, &pos, MPI_COMM_WORLD); 1, MPI_INT, buf, 100, &pos, MPI_COMM_WORLD); MPI_Bcast(buf, pos, 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 */ /********************************************************************************** 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 pid, pid_malla, pid_m, pid_fila, pid_col; int npr, k, longi_dim[2], anillo[2], reordenar = 1; int coord[2], coord_libre[2], pruebaf, pruebac; MPI_Comm 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 */