MPI Tutorial

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 12

INTRODUCCION A MESSAGE PASSING INTERFACE (MPI)

Dr. Pablo Guilln

CeCalCULA INTRODUCCION Qu es MPI?


La interface de pases de mensajes MPI, por sus siglas en Ingls, (Message Passing Interface), es una biblioteca de funciones y subrutinas que pueden ser usadas en programas C, FORTRAN y C++. Con el uso de MPI en programas que modelan algn fenmeno o proceso de Ciencias e Ingeniera, se intenta explotar la existencia de mltiples procesadores a travs del pase de mensajes. MPI fue desarrollado en los aos 1993-1994 por un grupo de investigadores de la Industria y la comunidad acadmica. Hoy en da MPI es una biblioteca estndar en la programacin paralela basada en el pase de mensajes.

COMUNICACION PUNTO A PUNTO Un Primer Programa


Posiblemente el programa de ms uso e introductorio de mltiples procesos, es el que cada proceso escribe el mensaje Hola Mundo. En el siguiente programa cada proceso escribe en pantalla el mensaje Hola Mundo. #include <iostream.h> #include <mpi.h> int main(int argc, char **argv) { MPI_Init(&argc,&argv); cout << Hola Mundo << endl; MPI_Finalize(); } Qu observamos en este programa? mpi.h. Nos provee de las declaraciones de funciones para todas las funciones de MPI.

Tenemos un comienzo y un final. El comienzo est en la forma de una llamada a MPI_Init(), lo cual le indica al sistema operativo que este es un programa MPI y permite al sistema operativo a realizar cualquier inicializacin necesaria. El final est en la forma de una llamada a MPI_Finalize(), lo cual le indica al sistema operativo que el ambiente de programacin MPI ha culminado.

Cuando se compila y ejecuta este programa, se obtiene una coleccin de mensajes impresos Hola Mundo por pantalla. El nmero de mensajes es igual al nmero de procesos los cuales ha ejecutado el programa. Las dos funciones MPI que hemos usado en el programa tienen la siguiente forma: MPI_Init y MPI_Finalize MPI_Init #include <mpi.h> int MPI_Init(int *argc, char **argv) argc argv puntero al nmero de argumentos puntero al vector de argumentos Inicializar el entorno de MPI

MPI_Finalize #include <mpi.h> int MPI_Finalize()

Terminar el entorno de MPI

Nota: Todos los procesos deben llamar esta rutina antes de terminar. Despus de haber llamado esta rutina el nmero de procesos no est definido. Debe llamarse poco antes de terminar el programa en cada proceso. En MPI, los procesos involucrados en la ejecucin de un programa paralelo son identificados por una secuencia de nmeros enteros no negativos. Si existen p procesos ejecutando un programa, cada proceso tendr como identificador un nmero: 0, 1,..., p - 1. Nos surge la siguiente pregunta: Cmo un proceso conoce su identificador? Existen dos comandos en MPI: MPI_Comm_size y MPI_Comm_rank MPI_Comm_size #include <mpi.h> int MPI_Comm_size ( MPI_Comm comm, int *size ) Input: comm comunicador (handle) Determina el tamao del grupo asociado con un comunicador

Output: size puntero a un nmero entero que recoge el nmero de procesos en el grupo de comm

MPI_Comm_rank #include <mpi.h>

Determina el rango (identificador) del proceso actual dentro del comunicador

int MPI_Comm_rank ( MPI_Comm comm, int *rank ) Input: comm Output: rank puntero a un nmero entero que recoge el rango del proceso actual en el grupo de comm comunicador (handle)

En ambas funciones el argumento comm es llamado el comunicador, y ste esencialmente es una designacin para una coleccin de procesos que pueden comunicarse uno con el otro. MPI tiene la funcionalidad de permitir la especificacin de varios comunicadores (diferenciar la coleccin de procesos); sin embargo en los ejemplos que se presentan en estas notas, siempre se usar el comunicador MPI_COMM_WORLD, el cual est predefinido dentro de MPI y consiste de todos los procesos inicializados cuando se ejecuta el programa paralelo. Cmo usamos esta informacin? Modifiquemos nuestro primer programa para que no slo cada proceso imprima el mensaje Hola Mundo, sino que tambin imprima de cual proceso el mensaje proviene y el nmero total de procesos. #include <iostream.h> #include <math.h> #include <mpi.h> int main(int argc, char ** argv){ int mynode, totalnodes; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD, &totalnodes); MPI_Comm_rank(MPI_COMM_WORLD, &mynode);

cout << "Hello world from processor " << mynode; cout << " of " << totalnodes << endl; MPI_Finalize(); } A este punto hacemos la siguiente observacin: Cuando se ejecuta un programa con MPI, todos los procesos usan el mismo objeto compilado, y por lo tanto, todos los procesos estn ejecutando exactamente el mismo cdigo. Nos surge la siguiente pregunta: Qu es lo que en MPI distingue un programa paralelo ejecutandose en P procesadores de la versin serial del cdigo ejecutandose en P procesadores? Dos cosas distinguen el programa paralelo: 1. Cada proceso usa su identificador de proceso para determinar que parte de las instrucciones del algoritmo le corresponden. 2. Los procesos se comunican uno con el otro para llevar a cabo la tarea final. Aunque cada proceso recibe una copia idntica de las instrucciones a ser ejecutadas, sto no implica que todos los procesos ejecutaran las mismas instrucciones. Debido a que cada proceso es capaz de obtener su identificador de proceso (usando MPI_Comm_rank), ste puede determinar que parte del cdigo le es suministrado para ejecutar. Esto es llevado a cabo a trvez del uso de la sentencia if. La seccin del cdigo que va a ser ejecutado por un proceso particular debe estar encerrado dentro de una sentencia if, lo cual verifica el nmero de identificacin del proceso. Si el cdigo no est situado entre sentencias if especficas a un identificador particular, entonces el cdigo sera ejecutado por todos los procesos (como en el caso del cdigo mostrado anteriormente). Ahora con respecto al segundo punto, comunicacin entre los procesos, recordemos que MPI es una biblioteca de pase (envo y recepcin) de mensajes, el envo y la recepcin de mensajes es hecho a travs de las siguientes dos funciones: MPI_Send y MPI_Recv: MPI_Send MPI_Send #include <mpi.h" int MPI_Send( void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm ) Input: buf count datatype dest tag comm direccin del primer elemento del buffer nmero de elementos en el buffer tipo de datos de cada elemento en buffer identificador del destinatario bandera del mensaje comunicador Envia datos en un mensaje

El tipo de datos de cada elemento puede ser de los siguientes:

(C/C++) MPI_CHAR (char), MPI_SHORT (short), MPI_INT (int), MPI_LONG (long), MPI_FLOAT (float), MPI_DOUBLE (double), MPI_UNSIGNED_CHAR (unsigned char), MPI_UNSIGNED_SHORT (unsigned short), MPI_UNSIGNED (unsigned int), MPI_UNSIGNED_LONG (unsigned long), MPI_LONG_DOUBLE (long double) (FORTRAN) MPI_REAL (REAL), MPI_INTEGER (INTEGER), MPI_LOGICAL (LOGICAL), MPI_DOUBLE_PRECISION (DOUBLE PRECISION), MPI_COMPLEX (COMPLEX), MPI_DOUBLE_COMPLEX (complex*16 o complex*32 si existe)

MPI_Recv MPI_Recv #include <mpi.h> int MPI_Recv( void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status ) Output: buf status Input: count datatype source tag comm nmero mximo de elementos para el buffer tipo de datos de cada elemento en buffer identificador del remitente bandera del mensaje comunicador direccin del primer elemento del buffer que recibe una estructura que indica el estatus Recibe datos de un mensaje

Nota: se utilizan los mismos tipos de datos como en MPI_Send. El parmetro count indica el nmero mximo de elementos que pueden recibirse, el nmero de elementos recibidos puede determinarse con MPI_Get_count. Para mostrar el uso de MPI_Send y MPI_Recv mediante un ejemplo (programa), consideremos el siguiente ejemplo numrico (cdigo secuencial): #include<iostream.h> int main(int argc, char **argv){ int sum;

sum = 0; for(int i=1;i<=1000;i=i+1) sum = sum + i; cout << "The sum from 1 to 1000 is: " << sum << endl; } que realiza la suma de todos los nmeros de 1 a 1000. Qu estrategia debemos seguir para que la suma se realice en mltiples procesos? La estrategia se enfoca en particionar los clculos (la suma) a travs de los procesos. Supongamos que usamos slo dos procesos, entonces el proceso 0 suma los nmeros de 1 a 500, y el proceso 1 suma los nmeros de 501 a 1000, y luego al final, los dos valores son sumados para obtener la suma total de todos los nmeros de 1 a 1000. Una frmula para particionar las sumas a travs de los procesos est dada por: startval =1000*mynode/totalnodes+1 endval = 1000*(mynode+1)/totalnodes Si un solo proceso es usado, entonces totalnodes = 1 y mynode = 0, por tanto startval = 1 y endval = 1000. Ahora, si usamos dos procesos, entonces totalnodes = 2 y mynode toma los valores 0 y 1. Para mynode = 0, startval = 1 y endval = 500, y para mynode = 1, startval = 501 y endval = 1000. Una vez que se tienen los valores de comienzo y final donde se realizarn las sumas, cada proceso puede ejecutar un lazo (for loop) para sumar los valores entre su startval y su endval, seguidamente que la acumulacin local es hecha (por cada proceso), cada proceso (diferente del proceso 0) enva su suma al proceso 0. El siguiente cdigo lleva a cabo lo anteriormente descrito: #include<iostream.h> #include<mpi.h> int main(int argc, char ** argv){ int mynode, totalnodes; int sum,startval,endval,accum; MPI_Status status; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD, &totalnodes); MPI_Comm_rank(MPI_COMM_WORLD, &mynode); sum = 0; startval = 1000*mynode/totalnodes+1; endval = 1000*(mynode+1)/totalnodes; for(int i=startval;i<=endval;i=i+1) sum = sum + i; if(mynode!=0) MPI_Send(&sum,1,MPI_INT,0,1,MPI_COMM_WORLD); else for(int j=1;j<totalnodes;j=j+1){ MPI_Recv(&accum,1,MPI_INT,j,1,MPI_COMM_WORLD, &status); sum = sum + accum;

} if(mynode == 0) cout << "The sum from 1 to 1000 is: " << sum << endl; MPI_Finalize(); }

COMUNICACION PUNTO A PUNTO Resumen


Las llamadas bsicas a MPI: Las llamadas imprescindibles: MPI_INIT - iniciar el sistema MPI MPI_FINALIZE - terminar la cmputos con MPI MPI_COMM_SIZE - determinar el nmero de procesos MPI_COMM_RANK - determinar el identificador del propio proceso MPI_SEND - mandar un mensaje MPI_RECV - recibir un mensaje Otras funciones tiles: MPI_Barrier #include "mpi.h" int MPI_Barrier (MPI_Comm comm ) Input: comm comunicador (handle) Bloquea hasta que todos los procesos han alcanzado esta rutina

Nota: Esta funcin es til para asegurar que todos los procesos se encuentran en un cierto estado antes de seguir en el clculo.

MPI_Wtime #include "mpi.h" double MPI_Wtime()

devuelve los segundos desde un momento dado no especificado

Devuelve: tiempo en segundos desde un instante arbitrario


Nota: Esta funcin puede emplearse para medir el tiempo de clculo transcurrido:

t1=MPI_Wtime(); ... clculos ...; t2=MPI_Wtime(); printf("%e\n",t2-t1);

MPI_Get_processor_name #include "mpi.h"

devuelve el nombre del procesador actual

int MPI_Get_processor_name( char *name, int *resultlen) Output: name


resultlen

cadena de caracteres que recibe el nombre del nodo. Longitud mnima MPI_MAX_PROCESSOR_NAME
longitud del nombre devuelto

Nota: para asegurar que los procesos se ejecutan en las mquinas que hemos especificado, resulta til incluir una llamada a esta funcin cuando empezamos con MPI.

COMUNICACION COLECTIVA EN MPI


Comunicaciones colectivas en MPI En esta seccin explicaremos algunas comunicaciones colectivas (comunicaciones que involucran todos los procesos de un grupo). MPI ofrece una grn variedad de este tipo de comunicaciones, aqu trataremos slo las versiones bsicas de estas comunicaciones. MPI_Barrier - Sincronizacin mediante barrera MPI_Bcast - mandar datos a todos los procesos MPI_Gather - obtener datos de todos los procesos MPI_Scatter - repartir datos sobre todos los procesos MPI_Reduce - realizar operacin (suma, mximo, ..) sobre todos los procesos

MPI_Barrier MPI_Barrier #include "mpi.h" int MPI_Barrier (MPI_Comm comm ) Input: comm comunicador (handle) Bloquea hasta que todos los procesos han alcanzado esta rutina

Nota: Esta funcin es util para asegurar que todos los procesos se encuentran en un cierto estado antes de seguir en el clculo.

MPI_Bcast MPI_Bcast #include "mpi.h" int MPI_Bcast ( void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm ) Input/Output: buffer Input: count datatype root nmero de elementos en buffer tipo de datos rango del proceso que contiene los datos que sern direccion de los datos mandar datos a todos los procesos

replicados comm comunicador

Nota: Una vez terminada la llamada todos los procesos disponen de los mismos datos que root en buffer. Esta funcin se suele utilizar para comunicar valores iniciales de un clculo a todos los procesos si el procesos root se encarga del I/O.

MPI_Gather MPI_Gather #include "mpi.h" int MPI_Gather ( void *sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm ) Input: sendbuf sendcount sendtype Output: recvbuf Input: recvcount recvtype root comm nmero de elementos receive(=sendcount) tipo de datos a recibir rango del proceso root comunicador en cada aqui root recibe los datos datos que manda cada proceso nmero de elementos en sendbuf tipo de datos en sendbuf Obtener datos de todos los procesos

Notas: Todos los procesos (root incluido) mandan su sendbuf a root. En recvbuf de root se guardan estos datos ordenados por el rango del proceso que los ha mandado. recvbuf se ignora en todos los procesos menos root. En root, recvbuf debe ser lo suficientemente grande para guardar np*recvcount datos, si np es el nmero de procesos.

MPI_Scatter MPI_Scatter #include "mpi.h" int MPI_Scatter ( void *sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, int recvcnt, MPI_Datatype recvtype, int root, MPI_Comm comm ) Input: Repartir datos sobre todos los procesos

sendbuf sendcnt sendtype Output: recvbuf Input: recvcnt recvtype root comm

datos que manda root nmero de elementos en sendbuf tipo de datos en sendbuf

aqui cada proceso recibe los datos

nmero de elementos receive(=sendcount) tipo de datos a recibir rango del proceso root comunicador

en

cada

Nota: Esta funcin es la "inversa" de MPI_Gather. sendbuf es repartido en np segmentos iguales y es mandado a todos los procesos (root incluido) por orden de rango. sendbuf es ignorado en todos los procesos menos root.

MPI_Reduce MPI_Reduce #include "mpi.h" int MPI_Reduce ( void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm ) Input: sendbuf Output: recvbuf Input: count datatype op root comm nmero de elementos en sendbuf tipo de datos en sendbuf operacin a realizar sobre elementos de sendbuf rango del proceso root comunicador resultado de la operacin en root datos a los que se aplicar la reduccin Repartir datos sobre todos los procesos

Nota: Esta funcin puede utilizarse por ejemplo para calcular la suma de un nmero que se tiene en cada proceso y se requiere la suma en root. Las siguiente operaciones estn previstos: MPI_MAX (mximo), MPI_MIN (mnimo), MPI_SUM (suma), MPI_PROD (producto) MPI_LAND (AND lgico), MPI_LOR

(OR lgico), MPI_LXOR (XOR lgico), MPI_BAND (AND binario), MPI_BOR (OR binario) MPI_BXOR (XOR binario), MPI_MAXLOC y MPI_MINLOC. Si sendbuf es un vector, la operacin se realiza para cada elemento de sendbuf.

También podría gustarte