mpi.pdf

download mpi.pdf

of 19

Transcript of mpi.pdf

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 1

    Programacin de aplicaciones paralelascon MPI (Message Passing Interface)

    Jos Miguel AlonsoFacultad de Informtica UPV/EHU

    [email protected]/1/97

    1. Introduccin a MPIMPI (Message Passing Interface) es un Interfaz estandarizado para larealizacin de aplicaciones paralelas basadas en paso de mensajes. Elmodelo de programacin que subyace tras MPI es MIMD ( Multiple Instructionstreams, Multiple Data streams) aunque se dan especiales facilidades para lautilizacin del modelo SPMD (Single Program Multiple Data), un casoparticular de MIMD en el que todos los procesos ejecutan el mismoprograma, aunque no necesariamente la misma instruccin al mismo tiempo.

    MPI es, como su nombre indica, un interfaz, lo que quiere decir que elestndar no exige una determinada implementacin del mismo. Loimportante es dar al programador una coleccin de funciones para que stedisee su aplicacin, sin que tenga necesariamente que conocer el hardwareconcreto sobre el que se va a ejecutar, ni la forma en la que se hanimplementado las funciones que emplea.

    Hw.

    Sw. paso de mensajesMPI

    Aplicacin

    Figura 1. Ubicacin de MPI en el proceso de programacin de aplicaciones paralelas.

    MPI ha sido desarrollado por el MPI Forum, un grupo formado porinvestigadores de universidades, laboratorios y empresas involucrados en lacomputacin de altas prestaciones. Los objetivos fundamentales del MPIForum son los siguientes:

    1. Definir un entorno de programacin nico que garantice la portabilidad delas aplicaciones paralelas.

    2. Definir totalmente el interfaz de programacin, sin especificar cmo debeser la implementacin del mismo

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 2

    3. Ofrecer implementaciones de calidad, de dominio pblico, para favorecerla extensin del estndar.

    4. Convencer a los fabricantes de computadores paralelos para que ofrezcanversiones de MPI optimizadas para sus mquinas ( lo que ya han hechofabricantes como IBM y Silicon Graphics).

    Los elementos bsicos de MPI son una definicin de un interfaz deprogramacin independiente de lenguajes, ms una coleccin de bindings oconcreciones de ese interfaz para los lenguajes de programacin msextendidos en la comunidad usuaria de computadores paralelos: C yFORTRAN.

    Un programador que quiera emplear MPI para sus proyectos trabajar conuna implementacin concreta de MPI, que constar de , al menos, estoselementos:

    Una biblioteca de funciones para C, ms el fichero de cabecera mpi.h conlas definiciones de esas funciones y de una coleccin de constantes ymacros.

    Una biblioteca de funciones para FORTRAN + mpif.h. Comandos para compilacin, tpicamente mpicc, mpif77, que son

    versiones de los comandos de compilacin habituales (cc, f77) queincorporan automticamente las bibliotecas MPI.

    Comandos para la ejecucin de aplicaciones paralelas, tpicamentempirun.

    Herramientas para monitorizacin y depuracin.

    MPI no es, evidentemente, el nico entorno disponible para la elaboracin deaplicaciones paralelas. Existen muchas alternativas, entre las quedestacamos las siguientes:

    Utilizar las bibliotecas de programacin propias del computador paralelodisponible: NX en el Intel Paragon, MPL en el IBM SP2, etc.

    PVM (Parallel Virtual Machine): de caractersticas similares a MPI, sedesarroll con la idea de hacer que una red de estaciones de trabajofuncionase como un multicomputador. Funciona tambin enmulticomputadores, normalmente como una capa de software encima delmecanismo de comunicaciones nativo.

    Usar, si es posible, lenguajes de programacin paralelos (FORTRAN 90) osecuenciales (C, FORTRAN 77) con directivas de paralelismo.

    Usar lenguajes secuenciales junto con compiladores que paralelicenautomticamente.

    MPI est an en sus comienzos, y aunque se est haciendo un huecocreciente en la comunidad de programadores de aplicaciones cientficasparalelas, no es probable que desplace a corto plazo a los entornos deprogramacin ya existentes (como los anteriormente citados) o impida laaparicin de otros nuevos. El MPI Forum es consciente de que MPI todavaadolece de algunas limitaciones, e incluso ha identificado bastantes de ellas:

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 3

    Entrada/salida: no se establece un mecanismo estandarizado de E/Sparalela.

    Creacin dinmica de procesos. MPI asume un nmero de procesosconstante, establecido al arrancar la aplicacin.

    Variables compartidas. El modelo de comunicacin estandarizado por MPIslo tiene en cuente el paso de mensajes.

    Bindings para otros lenguajes, adems de C y FORTRAN. Se piensa, enconcreto, en C++ y Ada.

    Soporte para aplicaciones de tiempo real. MPI no recoge en ningn puntorestricciones de tiempo real.

    Interfaces grficos. No se define ningn aspecto relacionado con lainteraccin mediante GUIs con una aplicacin paralela.

    Etc.

    Como ya hemos comentado, MPI est especialmente diseado paradesarrollar aplicaciones SPMD. Al arrancar una aplicacin se lanzan enparalelo N copias del mismo programa * (procesos). Estos procesos noavanzan sincronizados instruccin a instruccin sino que la sincronizacin,cuando sea necesaria, tiene que ser explcita. Los procesos tienen unespacio de memoria completamente separado. El intercambio deinformacin, as como la sincronizacin, se hacen mediante paso demensajes.Se dispone de funciones de comunicacin punto a punto (que involucran sloa dos procesos), y de funciones u operaciones colectivas (que involucran amtiples procesos). Los procesos pueden agruparse y formarcomunicadores, lo que permite una definicin del mbito de las operacionescolectivas, as como un diseo modular.

    La estructura tpica de un programa MPI, usando el binding para C, es lasiguiente:

    # include "mpi.h"main (int argc, char **argv) {

    int nproc; /* Nmero de procesos */int yo; /* Mi direccin: 0

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 4

    utilizadas de MPI: MPI_Init() para iniciar la aplicacin paralela,MPI_Comm_size() para averiguar el nmero de procesos que participan enla aplicacin, MPI_Comm_rank(), para que cada proceso averigue sudireccin (identificador) dentro de la coleccin de procesos que componen laaplicacin, y MPI_Finalize() para dar por finalizada la aplicacin.int MPI_Init(int *argc, char ***argv);int MPI_Comm_size (MPI_Comm comm, int *size);int MPI_Comm_rank (MPI_Comm comm, int *rank);int MPI_Finalize(void);

    El ejemplo nos sirve tambin para que prestemos atencin a algunasconvenciones de MPI. Los nombres de todas las funciones empiezan conMPI_, la primera letra que sigue siempre es mayscula, y el resto sonminsculas.

    La mayor parte de las funciones MPI devuelven un entero, que es undiagnstico. Si el valor devuelto es MPI_SUCCESS, la funcin se harealizado con xito. No se han estandarizado otros posibles valores.

    La palabra clave MPI_COMM_WORLD hace referencia a l comunicadoruniversal, un comunicador predefinido por MPI que incluye a todos losprocesos de la aplicacin. Ms adelante veremos cmo definir otroscomunicadores. Todas las funciones de comunicacin de MPI necesitancomo argumento un comunicador.

    En el resto de este tutorial vamos a ir presentando las diferentes funcionesque MPI ofrece para la comunicacin y sincronizacin entre procesos. En laseccin 2 presentamos los mecanismos para la comunicacin entre pares deprocesos (Comunicacin punto a punto). En la seccin 3 presentamos lasfunciones para comunicacin entre grupos de procesos ( Operacionescolectivas). La seccin 4 discute aspectos de MPI relacionados con laModularidad. En la seccin 5 se describen las funciones disponibles paradefinir Tipos de datos derivados. Por ltimo, la seccin 6 (Bibliografa) aportareferencias para la localizacin de informacin adicional sobre MPI.

    2. Comunicacin punto a puntoUn buen nmero de funciones de MPI estn dedicadas a la comunicacinentre pares de procesos. Existen mltiples formas distintas de intercambiarun mensaje entre dos procesos, en funcin de l modelo y el modo decomunicacin elegido.2.1 Modelos y modos de comunicacinMPI define dos modelos de comunicacin: bloqueante (blocking) y nobloqueante (nonblocking). El modelo de comunicacin tiene que ver con eltiempo que un proceso pasa bloqueado tras llamar a una funcin decomunicacin, sea sta de envo o de recepcin. Una funcin bloqueantemantiene a un proceso bloqueado hasta que la operacin solicitada finalice.Una no bloqueante supone simplemente encargar al sistema la realizacinde una operacin, recuperando el control inmediatamente. El proceso tiene

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 5

    que preocuparse, ms adelante, de averiguar si la operacin ha finalizado ono.

    Queda una duda, sin embargo: cundo damos una operacin porfinalizada? En el caso de la recepcin est claro: cuando tengamos unmensaje nuevo, completo, en el buffer asignado al efecto. En el caso de laemisin la cosa es ms compleja: se puede entender que la emisin haterminado cuando el mensaje ha sido recibido en destino, o se puede sermenos restrictivo y dar por terminada la operacin en cuanto se ha hechouna copia del mensaje en un buffer del sistema en el lado emisor. MPI defineun envo como finalizado cuando el emisor puede reutilizar, sin problemas decausar interferencias, el buffer de emisin que tena el mensaje. Dicho esto,podemos entender que tras hacer un send (envo) bloqueante podemosreutilizar el buffer asociado sin problemas, pero tras hacer un send nobloqueante tenemos que ser muy cuidadosos con las manipulaciones que serealizan sobre el buffer, bajo el riesgo de alterar inadvertidamente lainformacin que se est enviando.

    Al margen de si la funcin invocada es bloqueante o no, el programadorpuede tener un cierto control sobre la forma en la que se realiza y completaun envo. MPI define, en relacin a este aspecto, 4 modos de envo: bsico(basic), con buffer (buffered), sncrono (synchronous) y listo (ready).Cuando se hace un envo con buffer se guarda inmediatamente, en unbuffer al efecto en el emisor, una copia del mensaje. La operacin se da porcompleta en cuanto se ha efectuado esta copia. Si no hay espacio en elbuffer, el envo fracasa.

    Si se hace un envo sncrono, la operacin se da por terminada slo cuandoel mensaje ha sido recibido en destino. Este es el modo de comunicacinhabitual en los sistemas basados en Transputers. En funcin de laimplementacin elegida, puede exigir menos copias de la informacinconforme sta circula del buffer del emisor al buffer del receptor.

    El modo de envo bsico no especifica la forma en la que se completa laoperacin: es algo dependiente de la implementacin. Normalmente equivalea un envo con buffer para mensajes cortos y a un envo sncrono paramensajes largos. Se intenta as agilizar el envo de mensajes cortos a la vezque se procura no perder demasiado tiempo realizando copias de lainformacin.

    En cuanto al envo en modo listo, slo se puede hacer si antes el otroextremo est preparado para una recepcin inmediata. No hay copiasadicionales del mensaje (como en el caso del modo con buffer), y tampocopodemos confiar en bloquearnos hasta que el receptor est preparado.2.2 Comunicacin bsicaEl resultado de la combinacin de dos modelos y cuatro modos decomunicacin nos da 8 diferentes funciones de envo. Funciones derecepcin slo hay dos, una por modelo. Presentamos a continuacin los

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 6

    prototipos de las funciones ms habituales. Empezamos con MPI_Send() yMPI_Recv que son, respectivamente, las funciones de envo y recepcinbsicas bloqueantes.

    int MPI_Send(void* buf, int count, MPI_Datatype datatype,int dest, int tag, MPI_Comm comm);int MPI_Recv(void* buf, int count, MPI_Datatype datatype,int source, int tag, MPI_Comm comm, MPI_Status *status);

    El significado de los parmetros es como sigue. Buf, count y datatypeforman el mensaje a enviar o recibir: count copias de un dato del tipodatatype que se encuentran (o se van a dejar) en memoria a partir de ladireccin indicada por buf. Dest es, en las funciones de envo, el identificadordel proceso destinatario del mensaje. Source es, en las funciones derecepcin, el identificador del emisor del cual esperamos un mensaje. Si nonos importa el origen del mensaje, podemos poner MPI_ANY_SOURCE. Tages una etiqueta que se puede poner al mensaje. El significado de la etiquetalo asigna el programador. Suele emplearse para distinguir entre diferentesclases de mensajes. El emisor pone siempre una etiqueta a los mensajes, yel receptor puede elegir entre recibir slo los mensajes que tengan unaetiqueta dada, o aceptar cualquier etiqueta, poniendo MPI_ANY_TAG comovalor de este parmetro. Comm es un comunicador; en muchas ocasionesse emplea el comunicador universal MPI_COMM_WORLD. Status es unresultado que se obtiene cada vez que se completa una recepcin, y nosinforma de aspectos tales como el tamao del mensaje recibido, la etiquetadel mensaje y el emisor del mismo. La definicin de la estructura MPI_Statuses la siguiente:

    typedef struct { int MPI_SOURCE; int MPI_TAG; /* otros campos no accesibles */} MPI_Status;

    Podemos acceder directamente a los campos .MPI_SOURCE y .MPI_TAG,pero a ningn otro ms. Si queremos saber el tamao de un mensaje, loharemos con la funcin MPI_Get_count():int MPI_Get_count(MPI_Status *status, MPI_Datatypedatatype, int *count);

    MPI_Isend() y MPI_Irecv() son las funciones de emisin/recepcin bsicas nobloqueantes.

    int MPI_Isend(void* buf, int count, MPI_Datatypedatatype, int dest, int tag, MPI_Comm comm, MPI_Request*request);int MPI_Irecv(void* buf, int count, MPI_Datatypedatatype, int source, int tag, MPI_Comm comm, MPI_Request*request);

    int MPI_Wait(MPI_Request *request, MPI_Status *status);int MPI_Test(MPI_Request *request, int *flag, MPI_Status

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 7

    *status);int MPI_Cancel(MPI_Request *request);

    Las funciones no bloqueantes manejan un objeto request del tipoMPI_Request. Este objeto es una especie de recibo de la operacinsolicitada. Ms adelante se podr utilizar este recibo para saber si laoperacin ha terminado o no.

    La funcin MPI_Wait() toma como entrada un recibo, y bloquea al procesohasta que la operacin correspondiente termina. Por lo tanto, hacer unMPI_Isend() seguido de un MPI_Wait() equivale a hacer un envobloqueante. Sin embargo, entre la llamada a la funcin de envo y la llamadaa la funcin de espera el proceso puede haber estado haciendo cosas tiles,es decir, consigue solapar parte de clculo de la aplicacin con lacomunicacin.

    Cuando no interesa bloquearse, sino simplemente saber si la operacin haterminado o no, podemos usar MPI_Test(). Esta funcin actualiza un flagque se le pasa como segundo parmetro. Si la funcin ha terminado, esteflag toma el valor 1, y si no ha terminado pasa a valer 0.

    Por ltimo, MPI_Cancel() nos sirve para cancelar una operacin decomunicacin pendiente, siempre que sta an no se haya completado.2.3 Comunicacin con bufferUna de los problemas que tiene el envo bsico es que el programador notiene control sobre cunto tiempo va a tardar en completarse la operacin.Puede que apenas tarde, si es que el sistema se limita a hacer una copia delmensaje en un buffer, que saldr ms tarde hacia su destino; pero tambinpuede que mantenga al proceso bloqueado un largo tiempo, esperando aque el receptor acepte el mensaje.Para evitar el riesgo de un bloqueo no deseado se puede solicitarexplcitamente que la comunicacin se complete copiando el mensaje en unbuffer, que tendr que asignar al efecto el propio proceso. As, se elimina elriesgo de bloqueo mientras se espera a que el interlocutor est preparado.La funcin correspondiente es MPI_Bsend(), que tiene los mismosargumentos que MPI_Send().int MPI_Bsend(void* buf, int count, MPI_Datatypedatatype, int dest, int tag, MPI_Comm comm);

    Para que se pueda usar el envo con buffer es necesario que el programadorasigne un buffer de salida. Para ello hay reservar previamente una zona dememoria (de forma esttica o con malloc()) y luego indicarle al sistema que laemplee como buffer. Esta ltima operacin la hace MPI_Buffer_attach(). Esebuffer se puede recuperar usando MPI_Buffer_detach().int MPI_Buffer_attach(void* buffer, int size);int MPI_Buffer_detach(void* buffer, int* size);

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 8

    Recordemos que una peculiaridad de los envos con buffer es que fracasanen el caso de que el buffer no tenga suficiente espacio como para contenerun mensaje, y el resultado es que el programa aborta. El programador puededecidir entonces que el buffer asignado es muy pequeo, y asignar uno msgrande para una ejecucin posterior, o bien cambiar el modo decomunicacin a otro con menos requerimientos de buffer pero con msriesgo de bloqueo.2.4 Recepcin por encuestaLas funciones de recepcin de mensajes engloban en una operacin lasincronizacin con el emisor (esperar a que haya un mensaje disponible) conla de comunicacin (copiar ese mensaje). A veces, sin embargo, convieneseparar ambos conceptos. Por ejemplo, podemos estar a la espera demensajes de tres clases, cada una asociada a un tipo de datos diferente, y laclase nos viene dada por el valor de la etiqueta. Por lo tanto, nos gustarasaber el valor de la etiqueta antes de leer el mensaje. Tambin puede ocurrirque nos llegue un mensaje de longitud desconocida, y resulte necesarioaveriguar primero el tamao para as asignar dinmicamente el espacio dememoria requerido por el mensaje.

    Las funciones MPI_Probe() y MPI_Iprobe() nos permiten saber si tenemos unmensaje recibido y listo para leer, pero sin leerlo. A partir de la informacinde estado obtenida con cualquiera de estas sondas, podemos averiguar laidentidad del emisor del mensaje, la etiqueta del mismo y su longitud. Unavez hecho esto, podemos proceder a la lectura real del mensaje con lacorrespondiente llamada a MPI_Recv() o MPI_Irecv().int MPI_Iprobe(int source, int tag, MPI_Comm comm, int*flag, MPI_Status *status);int MPI_Probe(int source, int tag, MPI_Comm comm,MPI_Status *status);

    MPI_Probe() es bloqueante: slo devuelve el control al proceso cuando hayun mensaje listo. MPI_Iprobe() es no bloqueante: nos indica en el argumentoflag si hay un mensaje listo o noes decir, realiza una encuesta.2.5 Tipos de datosLos mensajes gestionados por MPI son secuencias de count elementos deltipo datatype. MPI define una coleccin de tipos de datos primitivos,correspondientes a los tipos de datos existentes en C. Hay otra coleccin,distinta, para FORTRAN.

    Tabla 1. Tipos de datos MPI

    Tipos MPI Tipos C equivalentesMPI_CHAR signed charMPI_SHORT signed short intMPI_INT signed intMPI_LONG signed long intMPI_UNSIGNED_CHAR unsigned charMPI_UNSIGNED_SHORT unsigned short intMPI_UNSIGNED unsigned int

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 9

    MPI_UNSIGNED_LONG unsigned long intMPI_FLOAT floatMPI_DOUBLE doubleMPI_LONG_DOUBLE long doubleMPI_BYTE Sin equivalente

    Aunque una aplicacin desarrollada en C trabaja con los tipos de datoshabituales, cuando se realice un paso de mensajes habr que facilitar a MPIun descriptor del tipo equivalente, tal como lo define MPI. La idea de fondoes la siguiente: los procesadores que participan en una aplicacin MPI notienen por qu ser todos iguales. Es posible que existan diferencias en larepresentacin de los datos en las distintas mquinas. Para eliminar losproblemas que puedan surgir, MPI realiza, si son necesarias,transformaciones de sintaxis que posibilitan la comunicacin en entornosheterogneos. La excepcin la constituyen los datos de tipo MPI_BYTE, quese copian sin ms de una mquina a otra.

    Aparte de los tipos simples definidos de forma primitiva por MPI, se permitela definicin de tipos de usuario, ms complejos. Esto lo estudiaremos msadelante (5. Tipos de datos derivados, pg. 17)2.6 Etiquetas y comunicadoresTodo mensaje que se enva con MPI va etiquetado: parmetro tag. Elproceso receptor puede elegir entre aceptar mensajes con una cierta etiqueta(parmetro tag con un valor concreto), o decir que acepta un mensajecualquiera (MPI_ANY_TAG). Tras la recepcin, se puede saber la etiquetaconcreta accediendo a status.MPI_TAG. Las etiquetas permiten diferenciarlas diferentes clases de informacin que pueden intercambiarse losprocesos.

    La comunicacin se produce dentro de un comunicador (entorno decomunicacin), que es bsicamente un conjunto de procesos. Elcomunicador universal MPI_COMM_WORLD est siempre definido, e incluyea todos los procesos que forman parte de la aplicacin. Los comunicadorespermiten que diferentes grupos de procesos acten sin interferir, as comodividir la aplicacin en fases no solapadas. MPI garantiza la entregaordenada de mensajes dentro de un mismo comunicador.3. Operaciones colectivasMuchas aplicaciones requieren de la comunicacin entre ms de dosprocesos. Esto se puede hacer combinando operaciones punto a punto, peropara que le resulte ms cmodo al programador, y para posibilitarimplementaciones optimizadas, MPI incluye una serie de operacionescolectivas:

    Barreras de sincronizacin Broadcast (difusin) Gather (recoleccin) Scatter (distribucin)

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 10

    Operaciones de reduccin (suma, multiplicacin, mnimo, etc.) Combinaciones de todas ellas

    Una operacin colectiva tiene que ser invocada por todos los participantes,aunque los roles que juegen no sean los mismos. La mayor parte de lasoperaciones requieren la designacin de un proceso como root, o raz de laoperacin.3.1 Barreras y broadcastDos de las operaciones colectivas ms comunes son las barreras desincronizacin (MPI_Barrier()) y el envo de informacin en modo difusin(MPI_Broadcast()). La primera de estas operaciones no exige ninguna clasede intercambio de informacin. Es una operacin puramente desincronizacin, que bloquea a los procesos de un comunicador hasta quetodos ellos han pasado por la barrera. Suele emplearse para dar porfinalizada una etapa del programa, asegurndose de que todos hanterminado antes de dar comienzo a la siguiente.

    int MPI_Barrier(MPI_Comm comm);

    MPI_Broadcast() sirve para que un proceso, el raz, enve un mensaje atodos los miembros del comunicador. Esta funcin ya muestra unacaracterstica peculiar de las operaciones colectivas de MPI: todos losparticipantes invocan la misma funcin, designando al mismo proceso comoraz de la misma. Una implementacin alternativa sera tener una funcin deenvo especial, en modo broadcast, y usar las funciones de recepcinnormales para leer los mensajes.int MPI_Bcast(void* buffer, int count, MPI_Datatypedatatype, int root, MPI_Comm comm);

    Este esquema representa el significado de un broadcast. En las filasrepresentamos los procesos de un comunicador, y en las columnas los datosque posee cada proceso. Antes del broadcast, el procesador raz(suponemos que es el 0) tiene el dato A0. Tras realizar el broadcast, todoslos procesos (incluyendo el raz) tienen una copia de A0.

    A A

    A

    A

    Datos

    Procesos

    buffer envo buffer recepcinFigura 2. Esquema de la operacin colectiva MPI_Broadcast(). Buffer envo indica loscontenidos de los bufferes de envo antes de proceder a la operacin colectiva. Buffer

    recepcin indica los contenidos de los bufferes de recepcin tras completarse la operacin.

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 11

    3.2 Recoleccin (gather)MPI_Gather realiza una recoleccin de datos en el proceso raz. Esteproceso recopila un vector de datos, al que contribuyen todos los procesosdel comunicador con la misma cantidad de datos. El proceso raz almacenalas contribuciones de forma consecutiva.

    int MPI_Gather(void* sendbuf, int sendcount, MPI_Datatypesendtype, void* recvbuf, int recvcount, MPI_Datatyperecvtype, int root, MPI_Comm comm);

    A B CAB

    C

    Figura 3. Esquema de la operacin colectiva MPI_Gather().Slo el proceso raz tiene que preocuparse de los parmetros recbuf,recvcount y recvtype. Sin embargo, todos los procesos (raz incluido) tienenque facilitar valores vlidos para sendbuf, sendcount y sendtype.

    Existe una versin de la funcin de recoleccin, llamada MPI_Gatherv(), quepermite almacenar los datos recogidos en forma no consecutiva, y que cadaproceso contribuya con bloques de datos de diferente tamao . La tablarecvcounts establece el tamao del bloque de datos aportado por cadaproceso, y displs indica cul es el desplazamiento entre bloque y bloque

    int MPI_Gatherv(void* sendbuf, int sendcount,MPI_Datatype sendtype, void* recvbuf, int *recvcounts,int *displs, MPI_Datatype recvtype, int root, MPI_Commcomm);

    Muchas veces interesa distribuir a todos los procesos el resultado de unarecoleccin previa. Esto se puede hacer concatenando una recoleccin conun broadcast. Existen funciones que se encargan de hacer todo esto en unnico paso: MPI_Allgather() (si los bloques de datos son de tamao fijo y sealmacenan consecutivamete) y MPI_Allgatherv() (si los tamaos de los datosson variables y/o se almacenan de forma no consecutiva).int MPI_Allgather(void* sendbuf, int sendcount,MPI_Datatype sendtype, void* recvbuf, int recvcount,MPI_Datatype recvtype, MPI_Comm comm);

    int MPI_Allgatherv(void* sendbuf, int sendcount,MPI_Datatype sendtype,void* recvbuf, int *recvcounts, int*displs, MPI_Datatype recvtype, MPI_Comm comm);

    A B CA B CA B C

    A

    B

    C

    Figura 4. Esquema de la operacin colectiva MPI_Allgather().

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 12

    3.3 Distribucin (scatter)MPI_Scatter() realiza la operacin simtrica a MPI_Gather(). El proceso razposee un vector de elementos, uno por cada proceso del comunicador. Trasrealizar la distribucin, cada proceso tiene una copia del vector inicial.Recordemos que MPI permite enviar bloques de datos, no slo datosindividuales.

    int MPI_Scatter(void* sendbuf, int sendcount,MPI_Datatype sendtype, void* recvbuf, int recvcount,MPI_Datatype recvtype, int root, MPI_Comm comm);

    Si los datos a enviar no estn almacenados de forma consecutiva enmemoria, o los bloques a enviar a cada proceso no son todos del mismotamao, tenemos la funcin MPI_Scatterv(), con un interfaz ms complejo pero ms flexible.

    int MPI_Scatterv(void* sendbuf, int *sendcounts, int*displs, MPI_Datatype sendtype, void* recvbuf, intrecvcount, MPI_Datatype recvtype, int root, MPI_Commcomm);

    A0 A1 A2 A0A1A2

    Figura 5. Esquema de la operacin colectiva MPI_Scatter().

    3.4 Comunicacin todos con todos (All-to-all)La comunicacin de todos con todos supone que, inicialmente, cada procesotiene un vector con tantos elementos como procesos hay en el comunicador.Para i, j y k entre 0 y N-1 (donde N es el nmero de procesos delcomunicador), cada proceso i enva una copia de sendbuf[j] al proceso j, yrecibe del proceso k un elemento, que almacena en recvbuf[k]. MPI_Alltoall()equivale, por tanto, a una sucesin de N operaciones de distribucin, encada una de las cuales el proceso i toma el rol de raz.

    int MPI_Alltoall(void* sendbuf, int sendcount,MPI_Datatype sendtype, void* recvbuf, int recvcount,MPI_Datatype recvtype, MPI_Comm comm);

    int MPI_Alltoallv(void* sendbuf, int *sendcounts, int*sdispls, MPI_Datatype sendtype, void* recvbuf, int*recvcounts, int *rdispls, MPI_Datatype recvtype,MPI_Comm comm);

    A0 A1 A2B0 B1 B2C0 C1 C2

    A0 B0 C0B1 C1B2 C2

    A1A2

    Figura 6. Esquema de la operacin colectiva MPI_Alltoall().

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 13

    3.5 ReduccinUna reduccin es una operacin realizada de forma cooperativa entre todoslos procesos de un comunicador, de tal forma que se obtiene un resultadofinal que se almacena en el proceso raz.

    int MPI_Reduce(void* sendbuf, void* recvbuf, int count,MPI_Datatype datatype, MPI_Op op, int root, MPI_Commcomm);

    MPI define una coleccin de operaciones del tipo MPI_Op que se puedenutilizar en una reduccin: MPI_MAX (mximo), MPI_MIN (mnimo), MPI_SUM(suma), MPI_PROD (producto), MPI_LAND (and lgico), MPI_BAND (and bita bit), MPI_LOR (or lgico), MPI_BOR (or bit a bit), MPI_LXOR (xor lgico),MPI_BXOR (xor bit a bit), etc.En ocasiones el programador puede necesitar otra operacin de reduccindistinta, no predefinida. Para ello MPI ofrece la funcin MPI_Op_create(), quetoma como argumento de entrada una funcin de usuario y devuelve unobjeto de tipo MPI_Op que se puede emplear con MPI_Reduce(). Ese objetose puede destruir ms adelante con MPI_Op_free().int MPI_Op_create(MPI_User_function *function, intcommute, MPI_Op *op );

    int MPI_Op_free(MPI_Op *op);

    La operacin a realizar puede ser cualquiera. En general se empleanoperaciones conmutativas y asociativas, es decir, cuyo resultado no dependedel orden con el que se procesan los operandos. Eso se indica con el valor 1en el parmetro commute. Si la operacin no es conmutativa (commute = 0)entonces se exige que la reduccin se realice en orden de direccin (seempieza con el proceso 0, luego con el 1, con el 2, etc.).En ocasiones resulta til que el resultado de una reduccin est disponiblepara todos los procesos. Aunque se puede realizar un broadcast tras lareduccin, podemos combinar la reduccin con la difusin usandoMPI_Allreduce(). Si el resultado de la reduccin es un vector que hay quedistribuir entre los procesadores, podemos combinar la reduccin y ladistribucin usando MPI_Reduce_scatter().int MPI_Allreduce(void* sendbuf, void* recvbuf, intcount, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);

    int MPI_Reduce_scatter(void* sendbuf, void* recvbuf, int*recvcounts,MPI_Datatype datatype,MPI_Op op,MPI_Commcomm);

    Una variante ms de la reduccin nos la da MPI_Scan(). Es similar aMPI_Allreduce(), excepto que cada proceso recibe un resultado parcial de lareduccin, en vez del final: cada proceso i recibe, en vez del resultado deOP(0..N-1)siendo N el nmero total de procesosel resultado de OP(0..i).

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 14

    int MPI_Scan(void* sendbuf, void* recvbuf, int count,MPI_Datatype datatype, MPI_Op op, MPI_Comm comm );

    4. ModularidadMPI permite definir grupos de procesos. Un grupo es una coleccin deprocesos, y define un espacio de direcciones (desde 0 hasta el tamao delgrupo menos 1). Los miembros del grupo tienen asignada una direccindentro de l. Un proceso puede pertenecer simultneamente a varios grupos,y tener una direccin distinta en cada uno de ellos.

    Un comunicador es un universo de comunicacin. Bsicamente consiste enun grupo de procesos, y un contexto de comunicacin. Las comunicacionesproducidas en dos comunicadores diferentes nunca interfieren entre s.

    El concepto de comunicador se introdujo para facilitar la elaboracin debibliotecas de programas: el programa de un usuario se ejecuta en uncomunicador, y las funciones de la biblioteca en otro diferente (posiblemente,con el mismo grupo de procesos, pero con un contexto diferente). As no hayriesgo de que se mezclen mensajes del usuario con mensajes privados delas funciones de la biblioteca. Adems, as tampoco hay problemas a la horade usar etiquetas.

    Describimos ahora algunas de las funciones sobre comunicadores mscomunes. Dos de ellas ya han sido presentadas: MPI_Comm_size() paraaveriguar el tamao (nmero de procesos) de un comunicador, yMPI_Comm_rank() para que un proceso obtenga su identificacin dentro delcomunicador.

    int MPI_Comm_size(MPI_Comm comm, int *size);

    int MPI_Comm_rank(MPI_Comm comm, int *rank);

    La funcin MPI_Comm_dup() permite crear un nuevo comunicador, con elmismo grupo de procesos, pero diferente con texto. Se puede usar antes dellamar a una funcin de una biblioteca. La Figura 7 (izquierda) muestra cmopuede ocurrir que un mensaje del usuario (flecha fina) interfiera con losmensajes propios de una funcin de biblioteca (flechas gruesas). Si labiblioteca trabaja en un comunicador diferente (derecha) entonces no hayproblemas de interferencia.

    int MPI_Comm_dup(MPI_Comm comm, MPI_Comm *newcomm);

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 15

    Figura 7. Utilizacin de un comunicador para aislar una funcin de biblioteca.

    MPI_Comm_dup (MPI_COMM_WORLD, &newcomm);/* llamada a funcin, que se ejecutar dentro *//* de newcomm */MPI_Comm_free (&newcomm);

    La operacin MPI_Comm_dup(), as como todas las dems que creancomunicadores, son operaciones colectivas: tienen que ser invocadas portodos los procesos implicados, con argumentos compatibles.

    MPI_Comm_free() destruye un comunicador que ya no se va a emplear. Porsu parte, MPI_Comm_split() crea, a partir de un comunicador inicial, varioscomunicadores diferentes, cada uno con un conjunto disjunto de procesos. Elcolor determina en qu comunicador queda cada uno de los procesos.

    int MPI_Comm_split(MPI_Comm comm, int color, int key,MPI_Comm *newcomm);

    Veamos un ejemplo de creacin de comunicadores con MPI_Comm_split().Partiendo del comunicador universal, definimos dos comunicadores: uno conaquellos procesos que tienen direccin par en el comunicadorMPI_COMM_WORLD, y otro con los procesos impares. Cada procesopertenecer a dos comunicadores: al universal y al recin creado, y tendrpor lo tanto dos direcciones, una por comunicador.

    int myglobalid, myotherid, color;MPI_Comm newcom;

    MPI_Comm_rank (MPI_COMM_WORLD, &myglobalid);if (myglobalid%2 == 0) color = 0;else color = 1;MPI_Comm_split (comm, color, myglobalid, &newcom);MPI_Comm_rank (newcom, &myotherid);

    /* operaciones entre pares e impares, por separado */

    MPI_Comm_free (&newcom);

    La Figura 8 muestra una coleccin de seis procesos que forman inicialmenteparte del comunicador universal y que, tras MPI_Comm_split() pasan aformar dos nuevos comunicadores disjuntos.

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 16

    0 1 2 3 4 5

    0 2 4 1 3 50 1 2 0 1 2

    Direcciones enMPI_COMM_WORLD

    Direcciones ennewcomm

    Figura 8. Arriba: una coleccin de procesos en el comunicador MPI_COMM_WORLD. Abajo,dos comunicadores: uno con los procesos con direccin global par, y otro con los procesos

    con direccin global impar.

    Los procesos que se encuentran en dos grupos diferentes no puedencomunicarse entre s, a no ser que se cree un intercomunicador (loscomunicadores vistos hasta ahora se denominan intracomunicadores). Lafuncin MPI_Intercomm_create() nos permite crear un intercomunicador.int MPI_Intercomm_create(MPI_Comm local_comm, intlocal_leader, MPI_Comm peer_comm, int remote_leader, inttag, MPI_Comm *newintercomm);

    Todos los procesos de los dos comunicadores que quieren interconectarsedeben hacer una llamada a MPI_Intercomm_create() con argumentoscompatibles. Interpretar los argumentos de esta funcin no es trivial. Enprimer lugar, cada proceso tiene que facilitar el comunicador ( local_comm),y nombrar a un proceso local como lider del intercomunicador (local_leader).Luego hay que facilitar un comunicador que englobe a los doscomunicadores que queremos intercomunicar ( peer_comm;MPI_COMM_WORLD siempre resulta vlido), y la direccin de un procesodel otro comunicador que va a actuar como lder ( remote_leader), teniendoen cuenta que esa direccin corresponde al comunicador global. Con esto yase tiene tendido un puente entre los dos comunicadores. Tambin hay quefacilitar un nmero de etiqueta (tag) que est libre y que el programador nodebe emplear para ningn otro propsito. El intercomunicador lo obtenemosen newintercomm. Una vez que se tiene un intercomunicador, se puedeusar para acceder al comunicador que est al otro lado. Las direcciones delos procesos receptores que hay que facilitar en las funciones de envo, o lasde los procesos emisores que se obtienen tras una funcin de recepcin,hacen referencia a ese otro lado.

    El siguiente ejemplo toma como punto de partida los dos comunicadorescreados en el ejemplo anterior: uno con los procesos que enMPI_COMM_WORLD tienen direccin par, y otro con los que tienen en esecomunicador direccin impar. Cada proceso tiene almacenado en newcom elcomunicador al que pertenece, y en myotherid su direccin en esecomunicador. Tras crear, de forma colectiva, el intercomunicador, cadaproceso del comunicador par enva un mensaje a su semejante en elcomunicador impar.

    if (myglobalid%2 == 0) {

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 17

    MPI_Intercomm_create (newcom, 0, MPI_COMM_WORLD,1, 99, &intercomm);

    MPI_Send(msg, 1, type, myotherid, 0,intercomm);

    }else {

    MPI_Intercomm_create (newcom, 0, MPI_COMM_WORLD,0, 99, &intercomm);

    MPI_Recv(msg, 1, type, myotherid, 0,intercomm, &status);

    }

    1 3 5

    0 2 40 1 2

    0 1 2

    MPI_COMM_WORLD

    Lderes

    Figura 9. Creacin de un intercomunicador entre los dos comunicadores de la Figura 8.

    5. Tipos de datos derivadosMPI maneja en todas sus funciones de envo/recepcin vectores de tipossimples. En general, se asume que los elementos de esos vectores estnalmacenados consecutivamente en memoria. En ocasiones, sin embargo, esnecesario el intercambio de tipos estructurados (structs), o de vectores noalmacenados consecutivamente en memoria (p.ej: envo de una columna deun array, en vez de envo de una fila).MPI incluye la posibilidad de definir tipos ms complejos (objetos del tipoMPI_Datatype), empleando constructores. Antes de usar un tipo de usuario,hay que ejecutar MPI_Type_commit(). Cuando ya no se necesite un tipo, sepuede liberar con MPI_Type_free().int MPI_Type_commit(MPI_Datatype *datatype);

    int MPI_Type_free(MPI_Datatype *datatype);

    5.1 Definicin de tipos homogneosSon tipos homogneos aquellos tipos construidos en los que todos loselementos constituyentes son del mismo tipo. Se pueden definir tiposhomogneos con dos funciones distintas: MPI_Type_contiguous() yMPI_Type_vector(). La primera versin es la ms sencilla, y permite definirun tipo formado por una coleccin de elementos de un tipo bsico, todosellos del mismo tamao y almacenados consecutivamente en memoria.

    int MPI_Type_contiguous (int count, MPI_Datatype oldtype,

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 18

    MPI_Datatype *newtype);

    Newtype es un nuevo tipo que consiste en count copias de oldtype. Si loselementos constituyentes del nuevo tipo no estn almacenadosconsecutivamente en memoria, sino que estn espaciados a intervalosregulares, la funcin a emplear es MPI_Type_vector().int MPI_Type_vector (int count, int blocklength, intstride, MPI_Datatype oldtype, MPI_Datatype *newtype);

    Newtype es un nuevo tipo que consiste en count bloques de datos. Cadabloque consta de blocklength elementos del tipo oldtype. La distancia entrebloques, medida en mltiplos del tamao del elemento bsico, la da stride.

    Una llamada a MPI_Type_contiguous(c, o, n) equivale a una llamada aMPI_Type_vector (c, 1, 1, o, n), o a una llamada a MPI_Type_vector (1, c, x,o, n), siendo x un valor arbitrario.

    5.2 Definicin de tipos heterogneosSi nos interesa trabajar con tipos no homogneos, podemos usar una funcinms genrica: MPI_Type_struct().MPI_Type_struct (int count, int *array_of_blocklenghts,MPI_Aint *array_of_displacements, MPI_Datatype*array_of_types, MPI_Datatype *newtype);

    Count determina el nmero de bloques, as como el tama o de los arraysarray_of_blocklenghts , array_of_displacements y array_of_types. Cadabloque i tiene array_of_blocklenghts[i] elementos, est desplazadoarray_of_displacements[i] bytes del anterior, y es de tipo array_of_types[i].

    Veamos un ejemplo. Si B = {2, 1, 3}, D = {0, 16, 26} y T = {MPI_FLOAT,MPI_INT, MPI_CHAR}, entonces MPI_Type_struct(3, B, D, T, newtype)devuelve un tipo que en memoria tiene el aspecto mostrado en la Tabla 2. Laprimera columna hace referencia a la posicin de memoria que ocupa cadacampo de una instancia de este tipo de datos, relativa al origen del dato.

    Tabla 2. Tipo de datos de ejemplo.Desplazamiento Tipo0 float4 float16 int26 char27 char28 char

    6. BibliografaLa mayor fuente de informacin sobre MPI es la pgina WWW mantenida por

  • Programacin de aplicaciones paralelas con MPI (Message Passing Interface) 19

    Argonne National Laboratory en http://www. mcs.anl.gov/mpi/index.html/. Enella podemos encontrar enlaces al texto del estndar, as como diferentesrecursos para aprendizaje de MPI. Entre ellos, se encuentra un enlace a laversin electrnica de libro Designing and building parallel programs,publicado por Ian Foster (y editado por Addison-Wesley).Tambin podemos encontrar aqu enlaces con implementaciones de dominiopblico de MPI. Una de las ms conocidas es MPICH, desarrollada porArgonne National Laboratory y Mississippi State University, con versionespara mltiples sistemas paralelos (SGI, IBM, Intel, Cray, etc.) y paradiferentes redes de estaciones de trabajo (IBM, HP, Sun, etc.).