Está en la página 1de 4

Sistemas Distribuidos

Practica 4: MPI (Message Passing Interface) Interface de Paso de Mensajes.


Objetivo:

a) Implementar grupos en MPI.

Introducción:

Sabemos que todos los procesos en MPI pertenecen a un grupo global o universal y que es posible,
crear subgrupos (o simplemente grupos) a partir de este grupo global.

Un grupo es una especie de abstracción que en MPI, requiere de dos parámetros: un manejador de
grupo y un comunicador. El manejador es una especie de identificador que contiene información sobre
el grupo. El comunicador, que también es un identificador, permite establecer el envío de mensajes
entre los procesos miembros del grupo. Note que, dado que el comunicador y el manejador son
“entes” abstractos, estos están definidos como tipos de dato: MPI_Comm y MPI_Group,
respectivamente.

Por default, el comunicador del grupo global es: MPI_COMM_WORLD, y es una constante que ayudara
a la creación de grupos.

La creación de grupos la podemos resumir en los siguientes pasos:

1. Obtener el manejador del grupo global. Esto se puede hacer con la función MPI_Comm_group.
2. Con el manejador del grupo global, se crean cada uno de los manejadores de los subgrupos o
grupos. En este caso, es necesario indicar el tamaño del grupo y los identificadores de los
procesos que van a pertenecer a dicho grupo. Para lo anterior se emplea la función
MPI_Group_incl.
3. Para permitir el envío de mensajes entre los procesos del grupo recién creado, se debe crear
el comunicador del grupo, lo cual se logra con la función MPI_Comm_create.

Para indicar qué procesos van a formar parte del grupo, se debe crear un arreglo que contenga los
identificadores de dichos procesos. El orden en el que se coloquen los procesos, será el identificador
del proceso dentro del grupo creado.

A continuación, se presenta un ejemplo en el cual dado un conjunto de N nodos o procesos, este será
dividido en dos grupos, y el proceso 0 de cada grupo enviará un mensaje de broadcast a todos los
elementos del grupo. Es importante que investigue sobre el proceso de envío de mensajes por
broadcast. ¿Hay broadcast síncrono y asíncrono? ¿Qué tipo de broadcast propone el ejemplo?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mpi.h>

#define MAX_PROCESOS 20
#define MASTER 0 /*El nodo maestro será el proceso 0, al igual que en los subgrupos*/

#define TRUE 1
#define FALSE 0

int iIsInGroup (int *piGroup, int iLenGroup, int iId)


{
int i;
for (i=0; i<iLenGroup; i++)
if (iId==piGroup[i]) return (TRUE);
return (FALSE);
}

int main (int iArg, char **spcArgv)


{
int iId, iNProc, i, iIdGroup, iEnvio;
int siGrupo1[MAX_PROCESOS], iNGrupo1; /*Los procesos en el grupo 1 y su número*/
int siGrupo2[MAX_PROCESOS], iNGrupo2; /*Los procesos en el grupo 2 y su número*/
MPI_Group smpigGrupoGbl;
MPI_Group smpigGrupo1, smpigGrupo2;
MPI_Comm smpicGrupoComm1, smpicGrupoComm2;

MPI_Init (&iArg, &spcArgv);


/*Determinamos el número de procesos del grupo global*/
MPI_Comm_rank (MPI_COMM_WORLD, &iId); /*Para obtener el ID*/
MPI_Comm_size (MPI_COMM_WORLD, &iNProc); /*Para obtener el número de procesos*/
if ((iNProc<4)||(iNProc>MAX_PROCESOS))
{ /*Necesitamos más procesos, pero no tantos*/
MPI_Finalize ();
return (0);
}
for (i=0, iNGrupo1=0, iNGrupo2=0; i<iNProc; i++)
{ /*Note que los procesos 0 y 1 serán los procesos 0 de cada grupo*/
if (i%2)
{ /*1, 3, 5, 7, ...*/
siGrupo1[iNGrupo1] = i; iNGrupo1++;
}
else
{ /*0, 2, 4, 6, ...*/
siGrupo2[iNGrupo2] = i; iNGrupo2++;
}
}
/*Ademas el proceso MASTER estará en ambos grupos*/
siGrupo1[iNGrupo1] = MASTER; iNGrupo1++;
if (iId==MASTER)
{
printf ("Hay %d nodos, repartidos:\n", iNProc);
printf ("Grupo 1(%d): ", iNGrupo1);
for (i=0; i<iNGrupo1; i++) printf ("%d ", siGrupo1[i]);
printf ("\nGrupo 2(%d): ", iNGrupo2);
for (i=0; i<iNGrupo2; i++) printf ("%d ", siGrupo2[i]);
printf ("\n");
}

MPI_Barrier (MPI_COMM_WORLD); /*Esperamos a los nodos*/


/*Manejador del grupo global*/
MPI_Comm_group (MPI_COMM_WORLD, &smpigGrupoGbl);
MPI_Group_incl (smpigGrupoGbl, iNGrupo1, siGrupo1, &smpigGrupo1);
MPI_Comm_create (MPI_COMM_WORLD, smpigGrupo1, &smpicGrupoComm1);
if ((smpicGrupoComm1==MPI_COMM_NULL)&&(iIsInGroup(siGrupo1, iNGrupo1, iId)))
{ /*Si esta en el grupo y no se pudo crear*/
printf ("Error en comunicadores Grupo 1, nodo %d.\n", iId);
MPI_Finalize ();
return (0);
}
MPI_Group_incl (smpigGrupoGbl, iNGrupo2, siGrupo2, &smpigGrupo2);
MPI_Comm_create (MPI_COMM_WORLD, smpigGrupo2, &smpicGrupoComm2);
if ((smpicGrupoComm2==MPI_COMM_NULL)&&(iIsInGroup(siGrupo2, iNGrupo2, iId)))
{ /*Si esta en el grupo y no se pudo crear*/
printf ("Error en comunicadores Grupo 2, nodo %d.\n", iId);
MPI_Finalize ();
return (0);
}

if (iId%2) MPI_Comm_rank (smpicGrupoComm1, &iIdGroup);


else MPI_Comm_rank (smpicGrupoComm2, &iIdGroup);
printf ("Nodo %d, en el grupo soy %d, quedo a la espera\n", iId, iIdGroup);

if (iId%2)
{ /*Separamos los grupos*/
if (iIdGroup==MASTER)
{ /*El MASTER envia*/
iEnvio = 100;
MPI_Bcast (&iEnvio, 1, MPI_INT, MASTER, smpicGrupoComm1);
}
else
{ /*Los demás reciben*/
MPI_Bcast (&iEnvio, 1, MPI_INT, MASTER, smpicGrupoComm1);
printf ("Soy %d global, pero en el grupo 1 soy %d, recibi: %d\n", iId, iIdGroup, iEnvio);
}
}
else
{
if (iIdGroup==MASTER)
{ /*El MASTER envia*/
iEnvio = 200;
MPI_Bcast (&iEnvio, 1, MPI_INT, MASTER, smpicGrupoComm2);
}
else
{ /*Los demás reciben*/
MPI_Bcast (&iEnvio, 1, MPI_INT, MASTER, smpicGrupoComm2);
printf ("Soy %d global, pero en el grupo 2 soy %d, recibi: %d\n", iId, iIdGroup, iEnvio);
}
}

usleep ((iId+1)*100000);
MPI_Barrier (MPI_COMM_WORLD); /*Para asegurar la creacion*/

if ((iId%2)||(iId==MASTER))
{ /*Todos liberan su respectivo grupo*/
printf ("Nodo %d liberando grupo 1\n", iId);
MPI_Comm_free (&smpicGrupoComm1);
MPI_Group_free (&smpigGrupo1);
}
if ((!(iId%2))||(iId==MASTER))
{ /*Todos liberan su respectivo grupo*/
printf ("Nodo %d liberando grupo 2\n", iId);
MPI_Comm_free (&smpicGrupoComm2);
MPI_Group_free (&smpigGrupo2);
}

MPI_Barrier (MPI_COMM_WORLD); /*Para asegurar la liberación*/


MPI_Finalize ();
return (0);
}

Actividad:

Diseñe un programa que propague por inundación. Considere N > 10 procesos o nodos, con N grupos
de tal forma que cada grupo “encierre” al nodo junto con sus vecinos; es decir, cada nodo será el líder
de sus vecinos. En la Fig. 1 se muestran algunos grupos en donde, por ejemplo:

a) El grupo morado corresponde a los nodos P1, P2, P3 y P8; siendo P2 el “líder”, desde el cual se
envían los mensajes de broadcast.
b) El grupo amarillo corresponde a los nodos P2, P3, P7 y P8; siendo P8 el “líder”, desde el cual
se envían los mensajes de broadcast. Note que P2 pertenece a dicho grupo, pero en este grupo
P2 actúa como nodo vecino de P8.
c) El grupo verde corresponde a los nodos P1, P2, P3 y P10; siendo P1 el “líder”, desde el cual se
envían los mensajes de broadcast. Note que P2 y P3 son nodos en los otros dos grupos
anteriores, pero en este caso son vecinos de P1 y por lo tanto reciben mensajes de este nodo.

Usted decide la topología de la red, es decir, la manera en la que estarán conectados los nodos, pero
se recomienda use una red grande.

Una vez creados todos los grupos, a partir del nodo i, se enviará un mensaje el cual se tratará de
propagar por inundación; es decir, la idea es propagar un mensaje M a todos los nodos de la red. La
propagación comenzará en i, el cual enviará M por broadcast a todos sus vecinos siguiendo los pasos:
I. Si el nodo j recibe M de i y nunca ha recibido M, coloca a i como su padre, reenvía por broadcast
el mensaje M a sus vecinos y se coloca en estado visitado.
II. Si j recibe M de i y está en estado visitado, no hace nada.

Note que, al inicio todos los nodos están en estado no visitado y eventualmente, todos los nodos serán
visitados formando una especie de árbol generador, de tal manera que el nodo i, en donde empezó
todo, sabe cómo llegar a cualquier otro nodo.

Figura 1. Algunos grupos en 11 nodos o procesos.

Una pregunta interesante es, ¿cuándo paramos? Desde luego, una vez que todos los nodos han sido
etiquetados como visitado, pero, ¿cómo hacer saber esto a todos los nodos o al nodo i? Aquí
proponemos una solución centralizada, en donde los nodos le informan a un nodo “todo poderoso”
que han sido visitados. De esta forma, este nodo” todo poderoso”, podrá decir cuando terminar e
incluso, ir construyendo el árbol generador.

Otra pregunta interesante es, ¿cómo será una solución distribuida? En donde los nodos, “por ellos
mismos”, determinan cuando han sido visitados todos los nodos. Observe que, para el caso
centralizado, no es necesario que los nodos conozcan cuántos hay en total en la red, en cambio, para
el caso distribuido, probablemente se requerirá de saber esto.

Para lo anterior, desarrolle dos programas, para que cada uno de ellos emplee:

• Comunicación síncrona.
• Comunicación asíncrona.

El programa debe preguntar por N e i, pero también debe ser capaz de hacer que todos los nodos
“sepan” a los otros nodos. Deberá imprimir en pantalla las soluciones. Recuerde que el resultado es
un árbol.

Nota: los programas son un tanto complicados (pero no imposibles), por lo que tendrá que hacer mucha
investigación sobre MPI.

También podría gustarte