Programación con MPI
Cluster Beowulf
• Un cluster Beowulf es una colección de computadoras personales
(PC's) interconectadas por medio de una red privada de alta
velocidad, corriendo como sistema operativo alguna distribución
Linux.
• Los nodos en el cluster son dedicados exclusivamente a ejecutar
tareas asignadas al cluster.
• Por lo general el cluster se encuentra comunicado al mundo exterior
a través de un solo nodo, llamado nodo “Principal", el cual también
esta reservado para accesar, compilar y manejar las aplicaciones a
ejecutar.
Arquitectura del Cluster
nodo-01
Servidor de Archivos
nfs.pci.uas.edu.mx
alfa.miservidor.mx
nodo-02
Internet
Nodo Principal
colhuacan.pci.uas.edu.mx
maestro.miservidor.mx
Nodo-03
beta.miservidor.mx
nodo-04
¿Qué es MPI?
MPI (iniciales de Message Passing Interface) es:
– una especificación para programación de paso de
mensajes.
– Proporciona una biblioteca de funciones para C, C++ o
Fortran que son empleadas en los programas para
comunicar datos entre procesos.
¿Quién define MPI?
– MPI es la biblioteca de paso de mensajes estándar y
portable, especificada por consenso por el MPI Forum,
con unas 40 organizaciones participantes, como
modelo que permita desarrollar programas que puedan
ser migrados a diferentes computadoras paralelos.
– http://www.mpi-forum.org
– El primer estándar MPI 1.0 fue acabado y publicado en
mayo 1994. El estándar ha sido actualizado desde
entonces, estando actualmente en desarrollo el MPI 2.
Características de MPI
• Estándar actual de programación de los sistemas de
memoria distribuida
• Portabilidad: multiprocesadores y multicomputadoras.
• Se basa en el envío y recepción de mensajes entre
procesos.
• Las implementaciones de MPI esta orientadas a Fortran
y C.
Implementaciones
• MPICH (MPI/Chameleon). Producida por la
Universidad del Estado de Mississippi.
• LAM (Local Area Multicomputer) es un ambiente de
programación y un sistema de desarrollo sobre redes
de computadores heterogéneas. Esta implementación
es del Centro de Supercomputación de Ohio.
• OpenMPI es el sucesor de la herramienta LAM
Funciones básicas de MPI
• Cualquier programa paralelo con MPI puede
implementarse con tan sólo 6 funciones, aunque hay
más de 120 para aspectos avanzados.
• Todas las funciones empiezan por MPI_ y obligan a
que los programas MPI tengan #include <mpi.h>
• Los programas MPI deben ser obligatoriamente
inicializados y finalizados en MPI (MPI_Init,
MPI_Finalize).
Funciones de Inicio y Terminación
• MPI_Init()
• int MPI_Init(int *argc, char ***argv)
• MPI_Finalize()
• int MPI_Finalize(void)
Ingreso
Conexión remota con ssh
$ ssh
[email protected]//12AB34cd*
Crear una carpeta personal
$ mkdir nombre
• Ingresar a la carpeta
$ cd nombre
Primer Programa
#include <stdio.h>
#include <mpi.h>
int main (int argc, char *argv[]) {
MPI_Init(&argc, &argv);
printf("Hola Mundo!!!\n");
MPI_Finalize();
}
Compilación y Ejecución con OpenMPI
Compilación
• $ mpicc prog01.c -o prog01
• Ejecutar programa
• $ mpirun -np 4 prog01
• Crear un archivo de nodos (ejem. nodos.lst) con
• alfa
• beta
• Ejecutar programa
• $ mpirun -hostfile nodos.lst -np 4 prog01
Funciones de Información de Ambiente
• Algunas de las preguntas que nos podemos hacer
son:
– ¿Cuántos procesos hay?
– ¿Qué proceso soy?
– ¿En dónde estoy corriendo?
Comunicadores
• Un comunicador corresponde a un grupo de procesos
sobre el que se realiza la comunicación.
• Dentro de un comunicador cada proceso tiene un
rango que lo identifica.
• Existe un comunicador básico MPI_COMM_WORLD
que contiene a todos los procesos.
• Existen funciones que nos permiten saber el rango y
el número de procesos dentro de un comunicador.
Numero de procesos
El valor para el número de procesos se obtiene con
int MPI_Comm_size(MPI_Comm comm, int *nproc)
Retorna el número de procesos involucrados en una ejecución
Entrada comm comunicador
Salida nproc número de procesos en el grupo de comm
Identificador de Proceso
Para obtener el identificador o rango del proceso se utiliza
int MPI_Comm_rank(MPI_Comm comm, int *idproc)
Retorna el identificador o rango de un proceso
Entrada comm comunicador
Salida idproc rango del proceso en el grupo de comm
El identificador o rango es un número entre cero (0) y el total de
procesos menos uno (procesos -1).
En múltiples
En un solo nodo nodo (CLUSTER)
Nombre del procesador
Para saber donde se esta corriendo el proceso
int MPI_Get_processor_name(char *name, int *resultlen)
Retorna el nombre del procesador sobre el cual se esta
corriendo al momento de hacer la llamada
Salida name - nombre del procesador
Salida resultlen - longitud en número de caracteres de name
Segundo Ejemplo
#include <stdio.h>
#include <mpi.h>
int idproc, nproc, resultlen;
char nombre[30];
int main (int argc, char** argv)
{
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &idproc);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);
MPI_Get_processor_name(nombre, &resultlen);
printf("Hola Mundo! Yo soy %d de %d corriendo en %s\n",idproc, nproc,
nombre);
MPI_Finalize();
}
Ejercicio
• Desarrolle un programa que lea en paralelo 4 archivos que contienen
una serie de números de tipo flotante, es necesario saber cual es el
valor menor y mayor de cada archivo así como su promedio, se
deben generar archivos para cada salida de información.
• Procese los archivos en paralelo con 4 nodos con MPI.
Tarea
• Modifique el ejercicio anterior para que pueda realizar los cálculos
para N archivos en N nodos.
Funciones Básicas de Comunicación
• La forma de comunicación en MPI es a través de
mensajes que contienen datos.
• La forma más simple es la comunicación punto a
punto, donde se envía un mensaje de un proceso a
otro.
• Esto se realiza usando las funciones MPI_Send y
MPI_ Recv.
Funciones Básicas de Comunicación
Envío de Datos
int MPI Send(void *Datos, int cont, MPI_Datatype dtype, int
destino, int tag, MPI_Comm comunicador)
Recepción de datos
int MPI Recv(void *Datos, int cont, MPI_Datatype dtype,
int fuente, int tag, MPI_Comm comunicador,
MPI_ Status *estado)
Funciones Básicas de Comunicación
void *Datos Datos de envío o recepción
int cont Número de datos
MPI_Datatype dtype Tipo de datos
int destino/fuente rango del nodo al que se
envía/recibe
int tag Etiqueta que diferencia al
mensaje, para ignorarla en
puede pasar MPI_ANY_TAG
en MPI_Recv
Funciones Básicas de Comunicación
MPI Comm comm Comunicador
MPI_Status *estado Estado de la recepción (solo
MPI_Recv) para ignorarlo
pasar
MPI_STATUS_IGNORE
Tipos de Datos
• Al enviar un dato es necesario especificar su tipo.
• Cada tipo tiene una equivalencia con un instancia del
tipo MPI_Datatype.
• Esto permite el uso de MPI en ambientes
heterogéneos.
• Es posible generar tipos de datos más complejos,
Tipos de Datos
Tipo de dato MPI Tipo de dato C
MPI_CHAR signed char
MPI_SHORT signed short int
MPI_INT signed int
MPI_LONG signed long int
MPI_UNSIGNED_CHAR unsigned char
MPI_UNSIGNED unsigned int
MPI_UNSIGNED_LONG unsigned long int
MPI_FLOAT float
MPI_DOUBLE double
MPI_LONG_DOUBLE long double
MPI_BYTE
Comunicación con bloqueo
#include <stdio.h> else // Esclavos
#include <stdlib.h> {
#include <mpi.h> int length;
printf("SLAVE (rank = %i)\n", rank);
#define TAG_LENGTH 1
#define TAG_DATA 2 MPI_Recv(&length, 1, MPI_INT, 0, TAG_LENGTH,
→ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
int main(int argc, char** argv)
{ double* data = (double*)malloc(sizeof(double)*length);
int size;
int rank; MPI_Recv(data, length, MPI_DOUBLE, 0, TAG_DATA,
→ MPI_COMM_WORLD, MPI_STATUS_IGNORE);
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size); free(data);
MPI_Comm_rank(MPI_COMM_WORLD, &rank); }
if (rank == 0) // Maestro MPI_Finalize();
{ return 0;
printf("MASTER (size = %i)\n", size); }
int length = 20;
double* data = (double*)malloc(sizeof(double)*length);
for (int r = 1; r < size; ++r)
{
MPI_Send(&length, 1, MPI_INT, r, TAG_LENGTH,
→ MPI_COMM_WORLD);
MPI_Send(data, length, MPI_DOUBLE, r, TAG_DATA,
→ MPI_COMM_WORLD);
}
free(data);
}
14/10/10
Funciones colectivas
• MPI_Bcast difunde un mensaje del proceso identificado
como root a todos los procesos del grupo
• int MPI_Bcast(void *Datos, int cont, MPI_Datatype tipo,
int root, MPI_Comm comm)
• Difunde datos desde root a todos los procesos
• Entrada/Salida Datos dirección del buffer
• Entrada cont número de elementos en el
buffer de envio
• Entrada tipo tipo de dato de los elementos
Entrada root rango del proceso root
• Entrada comm comunicador
#include <mpi.h>
#include <stdio.h>
int main(int argc, char** argv)
{
int rank;
int buf;
const int root=0;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if(rank == root)
buf = 777;
printf("[%d]: Before Bcast, buf is %d\n", rank, buf);
/* everyone calls bcast, data is taken from root and ends up in everyone's buf
*/
MPI_Bcast(&buf, 1, MPI_INT, root, MPI_COMM_WORLD);
printf("[%d]: After Bcast, buf is %d\n", rank, buf);
MPI_Finalize();
return 0;
}
Funciones colectivas
MPI_Reduce combina los elementos provisto en el
buffer de entrada (inbuf) de cada proceso en el grupo,
usando la operación operador (puede ser una
operación predefinida o definida por el usuario) y
retorna el valor combinado en el buffer de salida
(outbuf) del proceso root
• int MPI_Reduce(void *DatoEnt, void *DatoSal, int
cont, MPI_Datatype tipo, MPI_Op operador, int
root, MPI_Comm comm)
Funciones colectivas
Entrada DatoEnt dirección del buffer de
entrada
Salida DatoSal dirección del buffer de salida
Entrada cont número de elementos en el
buffer de entrada
Entrada tipo tipo de dato de los elementos
del buffer de entrada
Entrada operador operación (ver Tabla).
Entrada root rango del proceso root
Entrada comm comunicador
Funciones colectivas
• Operadores de reducción
Operador Descripción
MPI_SUM Suma
MPI_MAXLOC Máximo y localización
MPI_MINLOC Mínimo y localización
Funciones colectivas
#include <mpi.h>
#include <stdio.h>
int main(int argc, char** argv)
{
int rank, size, local_value, global_sum;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
local_value = rank;
MPI_Reduce(&local_value, &global_sum, 1, MPI_INT, MPI_SUM, 0,
MPI_COMM_WORLD);
if (rank == 0)
printf("The sum of all ranks is: %d\n", global_sum);
MPI_Finalize();
return 0;
}