En la primera publicación de este Blog os explique como construir un cluster de alto rendimiento y en esta vamos a ver los conceptos básicos sobre la programación paralela con MPI para incrementar el rendimiento de nuestras aplicaciones.
Introducción
En muchas ocasiones nos encontramos con el problema de que la ejecución de determinados algoritmos suponen un coste de tiempo demasiado elevado y las soluciones como utilizar hardware mas potente para reducir este tiempo tienen limitaciones técnicas y son muy caras. Antes estos problemas tenemos como solución la programación paralela empleando clusters HPC.
La programación en paralelo implica el desarrollo de software que se ejecuta simultáneamente en varios procesadores. Existe intercambio de información entre los procesos que forman la aplicación mediante la utilización de librerias MPI. Este intercambio de información se lleva a cabo entre procesadores que se encuentran en la misma maquina y/o en procesadores que se encuentran en la misma red, por lo que es importante que estas comunicaciones se hagan en una red con la menor latencia posible.La situación optima es que haya mucho computo por parte de los procesadores y poco intercambio de información.
Para diseñar el algoritmo del software paralelizado se precisa intercambiar mediante alguno de estos esquemas:
- Master-slave, master-worker o client-server: Todo el control lo lleva un procesador, y él se encarga de asignar tareas a los otros procesadores. Solo hay comunicación entre el servidor y el cliente, pero nunca entre clientes.
- Completamente distribuido: La tarea se hace de forma conjunta y todos los procesos tienen el mismo peso. Las comunicaciones fluyen en cualquier sentido.
MPI (Message Passing Interface)
La primera versión de MPI (“Message Passing Interface”, Interfaz de Paso de Mensajes) fué desarrollada en 1993-1994 por un grupo de investigadores al servicio de la industria, el gobierno y el sector académico. Es un estándar que define la sintaxis y la semántica de las funciones contenidas en una librería de paso de mensajes, diseñada para ser usada en programas que exploten la existencia de múltiples procesadores.
Aunque la interfaz no cambie, puede implementarse tanto en sistemas que utilicen paso de mensajes, como sistemas de memoria compartida.Existen implementaciones de software libre de MPI tales como OpenMPI, LAM, MPICH,... y estan disponibles para numerosos lenguajes de programación tales como C, C++,Fortran, JAVA, Python, ...
Estructura básica de un programa MPI, compilación y ejecución
- En toda aplicación paralizada con MPÎ hay 3 fase:
- Inicio del sistema paralelo
- Computación y comunicación
- Finalización del sistema paralelo
- Las funciones están definidas en mpi.h
- Hay un compilador específico para crear programas paralelos: mpicc, lamcc, mpic++,...
- Hay un lanzador de aplicaciones paralelas: mpirun, lamrun,...
1.Inicio del sistema paralelo
- Con MPI el procesador 0 es el que interactúa con el usuario, así que es el único que conoce los parámetros de entrada
- MPI_Init(int *argc, char ***argv)
- A partir de ese momento, la aplicación está sincronizada y todos los procesos conocen los parámetros de entrada
Hello World
#include <mpi.h>
int main(int argc, char *argv[])
{
MPI_Init(&argc,&argv);
......
}
2.Computación y comunicación
- Hay dos funciones muy interesantes en MPI para que los procesos puedan organizarse
- MPI_Comm_size número total de procesos
- MPI_Comm_rank posición del proceso actual
- Haremos que todos los procesos envíen un mensaje al primero indicando cada uno que proceso es y el primero lo pinte por pantalla
Hello World
#include <stdio.h>
#include <stdio.h>
#include <mpi.h>
int main(int argc, char *argv[]){
int size, rank , i;
char mensaje[100];
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if(rank==0){
for(i=1; i<size; i++){
MPI_Recv(mensaje, 100, MPI_CHAR, i, 0, MPI_COMM_WORLD, 0);
printf(mensaje);
}
}else{
sprintf(mensaje,"Hola mundo paralelo, soy el proceso %d de %d\n",rank,size);
MPI_Send(mensaje, 100, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
}
}
3 Finalización del sistema paralelo
- Todos los procesos deben ejecutar MPI_Finalize antes de terminar
- Esa función cierra las comunicaciones entre los procesos y da por finalizada la aplicación paralela.
Hello World
#include <stdio.h>
#include <mpi.h>
int main(int argc, char *argv[])
{
int size, rank , i;
char mensaje[100];
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if(rank==0){
for(i=1; i<size; i++){
MPI_Recv(mensaje, 100, MPI_CHAR, i, 0, MPI_COMM_WORLD, 0);
printf(mensaje);
}
}else{
sprintf(mensaje,"Hola mundo paralelo, soy el proceso %d de %d\n",rank,size);
MPI_Send(mensaje, 100, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
}
MPI_Finalize();
return EXIT_SUCCESS;
}
Funciones básicas de MPI
Para desarrollar aplicaciones paralelas contamos con numerosas funciones pero en este apartado veremos funciones mas básicas de MPI:
int MPI_Init (int *argc, char ***argv)
– La primera función MPI que hay que ejecutar
int MPI_Finalize ( )
– La última función MPI que hay que ejecutar MPI (Message Passing Interface)
int MPI_Comm_size(MPI_Comm comm, int *size)
– MPI_Comm_size(MPI_COMM_WORLD, &num_procs)
int MPI_Comm_rank(MPI_Comm comm, int *rank)
– MPI_Comm_rank(MPI_COMM_WORLD, &pos)
int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
– MPI_Send(&num, 1, MPI_INT, 2, 0, MI_COMM)
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
– MPI_Recv(&num, 1, MPI_INT, 1, 0, MI_COMM, &status)
Funciones básicas de MPI
Para desarrollar aplicaciones paralelas contamos con numerosas funciones pero en este apartado veremos funciones mas básicas de MPI:
int MPI_Init (int *argc, char ***argv)
– La primera función MPI que hay que ejecutar
int MPI_Finalize ( )
– La última función MPI que hay que ejecutar MPI (Message Passing Interface)
int MPI_Comm_size(MPI_Comm comm, int *size)
– MPI_Comm_size(MPI_COMM_WORLD, &num_procs)
int MPI_Comm_rank(MPI_Comm comm, int *rank)
– MPI_Comm_rank(MPI_COMM_WORLD, &pos)
int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
– MPI_Send(&num, 1, MPI_INT, 2, 0, MI_COMM)
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
– MPI_Recv(&num, 1, MPI_INT, 1, 0, MI_COMM, &status)
Compilación de una aplicación paralela
Para compilar un programa paralelo debemos usar un compilador especial. Dependiendo de si usamos C o C++, y dependiendo de la librería instalada: mpicc, mpic++, lamcc, lamc++, lamc++-64,...
> mpicc -o holamundo holamundo.c
Ejecución de una aplicación paralela
Ejecución de una aplicación paralela
Ahora podemos ejecutar el programa en la máquina paralela. Dependiendo de la instalación que tengamos, usamos mpirun, mpiexec, lamrun,...
> mpirun –np 4 ./holamundo
Hola mundo paralelo, soy el proceso 1 de 4
Hola mundo paralelo, soy el proceso 2 de 4
Hola mundo paralelo, soy el proceso 3 de 4
Las aplicaciones pueden ser enviadas a un gestor de trabajos p.e. SGE (Sun Grid Engine),
#!/bin/bash
#$ -cwd
#$ -pe hpmpi 4
#$ -N mi_identificador_de_tarea
#$ -M correo@correo.com
#$ -m be
#$ -S /bin/bash
#$ -q MPI_Pmas
mpirun -np $NSLOTS ./holamundo
> qsub tarea
Conclusión:
Hemos hecho un acercamiento a la programación en paralelo y espero que os sirva para entender de que se trata y que os permita desarrollar vuestras primeras aplicaciones. Al tratarse de un area extensa espero en futuros artículos volver a hablar de la programación paralela pero ya en aspectos mas avanzados
.
Hola, primero queria felicitarlo por el articulo y aprovechando la comunicacion, queria consultarle si ha realizando pruebas de ABC Linux con fortran MP y como le funciono? Le pregunto porque estoy necesitando reducir los tiempos de compilacion para una tesis y montar un cluster con ABC me seria de mucha utilidad.
ResponderEliminarMuchas Gracias