Tema 7: Programación paralela de paso de mensajes

Anuncio
Departamento de Automática
Arquitectura e Ingeniería de Computadores
Tema 7
Programación paralela de paso de mensajes
Prof.
Dr. José Antonio de Frutos Redondo
Dr. Raúl Durán Díaz
Curso 2010-2011
Arquitectura e Ingeniería de Computadores
Programación con MPI
„
„
„
„
„
„
Introducción y repaso
MPI mediante un ejemplo
Funciones MPI elementales
Comunicación colectiva
Agrupación de datos para comunicaciones
La entrada/salida
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 2
Arquitectura e Ingeniería de Computadores
Modelo de paso de mensajes
„
Recordemos:
„
Desde el punto de vista hardware, estas máquinas:
„
„
„
„
Desde el punto de vista del modelo de programación:
„
„
„
„
Están típicamente construidas por medio de computadores
completos (procesador + memoria), incluyendo E/S.
Interconexión entre ellos por medio de redes (estáticas o
dinámicas).
Comunicación por medio de operaciones explícitas de E/S.
Acceso directo sólo a direcciones privadas (memoria local).
Comunicación por intercambio de mensajes.
Existe intervención apreciable del sistema operativo.
Normalmente se programa por intermedio de librerías.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 3
Arquitectura e Ingeniería de Computadores
MPI
„
„
„
„
MPI significa “Message Passing Interface”.
Es un conjunto de funciones en C (o subrutinas en
FORTRAN) con las que se puede implementar un programa
usando paso de mensajes. Nosotros usaremos C.
MPI permite coordinar la ejecución del programa en múltiples
procesadores con memoria distribuida.
MPI está normalizada:
„
„
Cualquier programa escrito con esta librería funcionará sobre
cualquier máquina en que MPI esté instalado.
La normalización ha sido realizada por un equipo independiente
con participantes de la industria, de la universidad y laboratorios
de investigación.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 4
Arquitectura e Ingeniería de Computadores
MPI
„
En el comienzo de todo programa debemos especificar:
# include “mpi.h”
„
„
Todas las funciones y constantes de MPI comienzan con el
prefijo MPI_.
„
„
„
Con ello, disponemos de las funciones y constantes definidas
en la librería.
En el caso de las funciones sigue una letra mayúscula y las
demás minúsculas: MPI_Init.
En el caso de constantes, son todas mayúsculas: MPI_CHAR.
Todas las funciones devuelven un entero int salvo que se
diga lo contrario.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 5
Arquitectura e Ingeniería de Computadores
MPI
„
Antes de comenzar a usar la librería debemos llamar a la
función MPI_Init.
„
„
Sólo debe hacerse una vez.
Para terminar, debe llamarse a la MPI_Finalize.
# include “mpi.h”
...
main(int argc, char *argv[]) {
...
/* Antes de este punto, no llamar a ninguna función MPI */
MPI_Init(&argc, &argv);
...
MPI_Finalize();
/* Pasado este punto, no llamar a ninguna función MPI */
...
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 6
Arquitectura e Ingeniería de Computadores
MPI
„
„
„
„
Llamamos grupo de comunicación (communicator) a una
familia de procesos que tienen permitido el intercambio de
mensajes.
Existe un grupo de comunicación por defecto, llamado
MPI_COMM_WORLD. En él se encuentran todos los procesos
en el momento de arranque.
Dentro de cada grupo de comunicación, cada proceso se
identifica por su rango (un identificador numérico) que se
puede obtener con MPI_Comm_rank.
El número total de procesos en un grupo de comunicación se
puede obtener con MPI_Comm_size.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 7
Arquitectura e Ingeniería de Computadores
MPI
„
Para enviar y recibir mensajes, utilizamos las funciones
básicas:
„
„
„
„
MPI_Send
MPI_Recv
Los mensajes pueden sólo intercambiarse dentro del mismo
grupo de comunicación.
Cada mensaje enviado lleva una etiqueta identificadora (tag)
que es única dentro de cada grupo de comunicación.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 8
Arquitectura e Ingeniería de Computadores
Un ejemplo sencillo de MPI
„
Antes de poder ejecutar el ejemplo, cada máquina debe tener
acceso a una copia.
„
„
O bien, todas ellas tienen acceso a la misma copia (disco
compartido).
¿Qué ocurre cuando lanzamos una ejecución?
„
„
Cada máquina comienza la ejecución de su copia.
Cada máquina realiza una ejecución independiente.
„
Cada proceso puede ejecutar distintas zonas del programa si la
lógica de éste depende de la identificación (rango) del proceso.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 9
Arquitectura e Ingeniería de Computadores
Un ejemplo sencillo de MPI
/* saludos.c -Envía un mensaje desde todos los procesos con rango != 0 al proceso de rango
0. Éste imprime los mensajes recibidos.
*/
#include <stdio.h>
#include <string.h>
#include "mpi.h"
main(int argc, char* argv[]) {
int
mi_rango;
int
p;
int
fuente;
int
dest;
int
tag = 0;
char
mensaje[256];
MPI_Status status;
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/*
/*
/*
/*
/*
/*
/*
rango de este proceso
número total de procesos
rango del remitente
rango del destinatario
etiqueta para el mensaje
buffer para el mensaje
status de return
*/
*/
*/
*/
*/
*/
*/
7. Programación paralela de paso de mensajes 10
Un ejemplo sencillo de MPI
Arquitectura e Ingeniería de Computadores
MPI_Init(&argc, &argv); /* Arrancar MPI */
/* Hallar rango del proceso actual */
MPI_Comm_rank(MPI_COMM_WORLD, &mi_rango);
/* Número de procesos */
MPI_Comm_size(MPI_COMM_WORLD, &p);
if (mi_rango != 0) {
/* Crear mensaje */
sprintf(mensaje, “Saludos desde el proceso '%d'", mi_rango);
dest = 0;
/* Usar strlen+1 para transmitir también '\0' */
MPI_Send(mensaje, strlen(mensaje)+1, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
} else { /* mi_rango == 0 */
for (fuente = 1; fuente < p; fuente++) {
MPI_Recv(mensaje, sizeof(mensaje), MPI_CHAR, fuente, tag,
MPI_COMM_WORLD, &status);
printf("%s\n", mensaje);
}
}
MPI_Finalize(); /* Finalizar MPI */
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 11
Arquitectura e Ingeniería de Computadores
Funciones elementales MPI
„
La función de MPI_Send tiene el siguiente prototipo:
MPI_Send(void
*mensaje,
int
cuenta,
MPI_Datatype tipodato,
int
destino,
int
etiqueta,
MPI_Comm
grupo_com);
„
Í
Í
Í
Í
Í
Í
*/
*/
*/
*/
*/
*/
Los tipos de datos son, por ejemplo:
MPI_CHAR
MPI_SHORT
MPI_FLOAT
.....
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/*
/*
/*
/*
/*
/*
signed char
signed short int
float
7. Programación paralela de paso de mensajes 12
Arquitectura e Ingeniería de Computadores
Funciones elementales MPI
„
La función de MPI_Recv tiene el siguiente prototipo:
MPI_Recv(void
*mensaje,
int
cuenta,
MPI_Datatype tipodato,
int
remitente,
int
etiqueta,
MPI_Comm
grupo_com,
MPI_Status *status);
„
/*
/*
/*
/*
/*
/*
/*
Î
Í
Í
Í
Í
Í
Î
*/
*/
*/
*/
*/
*/
*/
Atención: el parámetro cuenta especifica el número máximo
de elementos de tipo tipodato que caben en el buffer
mensaje.
„
Si se envía un mensaje más largo, se produce un error de
desbordamiento.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 13
Arquitectura e Ingeniería de Computadores
Funciones elementales MPI
„
La variable de tipo MPI_Status tiene, al menos, los
siguientes campos:
MPI_SOURCE
MPI_TAG
MPI_ERROR
„
También contiene información acerca del tamaño del
mensaje recibido, que se obtiene mediante la función:
MPI_Get_count(MPI_Status
*status,
MPI_Datatype datatype,
int
*cuenta);
„
Atención: cuenta obtendrá el número de elementos de tipo
datatype (y no el número de bytes).
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/* Í */
/* Í */
/* Î */
7. Programación paralela de paso de mensajes 14
Arquitectura e Ingeniería de Computadores
Funciones elementales MPI
„
Hemos usado también las funciones MPI_Comm_rank y
MPI_Comm_size:
MPI_Comm_rank(MPI_Comm
int
„
„
Como ya veremos, el comunicador es una estructura que, dicho de
modo sencillo, agrupa los procesos que pueden intercambiar
mensajes.
comunicador,
*tamanyo);
/* Í */
/* Î */
Devuelve en la variable tamanyo el número de procesos
enganchados al comunicador comunicador.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/* Í */
/* Î */
Devuelve al proceso que la llama (en la variable rango) su rango
dentro del comunicador comunicador.
MPI_Comm_size(MPI_Comm
int
„
comunicador,
*rango);
7. Programación paralela de paso de mensajes 15
Arquitectura e Ingeniería de Computadores
Ejemplo más completo de MPI
#include <stdlib.h>
#include “mpi.h”
#define ETIQ_TRAB 1
#define ETIQ_MUERTE 2
/* Tipos de datos locales */
typedef int
unidad_de_trabajo_t;
typedef double unidad_resultado_t;
/* Funciones locales */
static
static
static
static
void maestro(void);
void esclavo(void);
unidad_de_trabajo_t siguiente_item(void);
unidad_resultado_t trabaja(unidad_de_trabajo_t trabajo);
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 16
Arquitectura e Ingeniería de Computadores
Ejemplo más completo de MPI
int main(int argc, char **argv)
{
int mirango;
/* Inicializar MPI */
MPI_Init(&argc, &argv);
/* Mi identidad en el grupo de comunicación estándar */
MPI_Comm_rank(MPI_COMM_WORLD, &mirango);
if (mirango == 0) {
maestro(); /* Soy el capataz */
} else {
esclavo(); /* Soy un obrero */
}
/* Cerrar MPI */
MPI_Finalize();
return 0;
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 17
Arquitectura e Ingeniería de Computadores
Ejemplo más completo de MPI
static void maestro(void)
{
int tareas, rango;
unidad_de_trabajo_t trabajo;
unidad_resultado_t resultado;
MPI_Status
status;
/* Ver cuántos procesos hay en el grupo de comunicación */
MPI_Comm_size(MPI_COMM_WORLD, &tareas);
/* Enviar a cada obrero una unidad de trabajo */
for (rango = 1; rango < tareas; ++rango) {
/* Buscar el siguiente item en la cola de trabajos */
trabajo = siguiente_item();
/* Enviárselo al obrero que toque */
MPI_Send(&trabajo,
/* buffer del mensaje */
1,
/* un item de datos */
MPI_INT,
/* el item es un entero */
rango,
/* rango del proceso destino */
ETIQ_TRAB,
/* etiqueta del mensaje */
MPI_COMM_WORLD);
/* grupo de trabajo (es el predefinido) */
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 18
Arquitectura e Ingeniería de Computadores
Ejemplo más completo de MPI
/* Bucle para ir recogiendo trabajo hecho y enviando
más, hasta que se finalicen las tareas */
trabajo = siguiente_item();
while (trabajo != NULL) {
/* Recoger trabajo del obrero */
MPI_Recv(&resultado,
/* buffer del mensaje */
1,
/* un item de datos */
MPI_DOUBLE,
/* el item es un real doble */
MPI_ANY_SOURCE,
/* recibir de cualquier remitente */
MPI_ANY_TAG,
/* cualquier tipo de mensaje */
MPI_COMM_WORLD,
/* grupo de trabajo (es el predefinido) */
&status);
/* info del mensaje recibido */
/* Enviar más trabajo */
MPI_Send(&trabajo,
/* buffer del mensaje */
1,
/* un item de datos */
MPI_INT,
/* el dato es un entero */
status.MPI_SOURCE,
/* devolver al que nos lo acaba de enviar */
ETIQ_TRAB,
/* etiqueta del mensaje */
MPI_COMM_WORLD);
/* grupo de trabajo (es el predefinido) */
/* Buscar el siguiente item en la cola de trabajos */
trabajo = siguiente_item();
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 19
Arquitectura e Ingeniería de Computadores
Ejemplo más completo de MPI
/* No hay más tareas. Falta sólo recibir los trabajos
que todavía estén pendientes. */
for (rango = 1; rango < tareas; ++rango) {
MPI_Recv(&resultado, 1, MPI_DOUBLE, MPI_ANY_SOURCE,
MPI_ANY_TAG, MPI_COMM_WORLD, &status);
}
/* Ordenar a los obreros que se cancelen, enviándoles un
mensaje con la etiqueta ETIQ_MUERTE. */
for (rango = 1; rango < tareas; ++rango) {
MPI_Send(0, 0, MPI_INT, rango, ETIQ_MUERTE, MPI_COMM_WORLD);
}
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 20
Arquitectura e Ingeniería de Computadores
Ejemplo más completo de MPI
static void esclavo(void)
{
unidad_de_trabajo_t trabajo;
unidad_resultado_t resultado;
MPI_Status
status;
while (1) {
/* Recibir una orden de trabajo del capataz */
MPI_Recv(&trabajo, 1, MPI_INT, 0, MPI_ANY_TAG,
MPI_COMM_WORLD, &status);
/* Comprobar tipo de orden. Terminar... */
if (status.MPI_TAG == ETIQ_MUERTE) {
return;
}
/* o realizar la tarea, y... */
resultado = trabaja(trabajo);
/* devolver el resultado. */
MPI_Send(&resultado, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 21
Arquitectura e Ingeniería de Computadores
Ejemplo más completo de MPI
static unidad_de_trabajo_t siguiente_item(void)
{
/* Código para obtener un nuevo item de la cola de trabajos */
}
static unidad_resultado_t trabaja(unidad_de_trabajo_t trabajo)
{
/* Código para realizar el trabajo y obtener el resultado */
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 22
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
En este programa ejemplo la comunicación entre los
procesos es lenta:
„
„
„
„
Todos los obreros comienzan simultáneamente, más o menos.
El capataz ha de ir distribuyendo el trabajo a los obreros, que
han de hacer cola para que les llegue el turno de recibir su
tarea. El último ha de esperar a todos los anteriores.
Si hay p procesos, el último ha de esperar un tiempo O(p).
Esto es indeseable: queremos que todos dispongan de
trabajo cuanto antes.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 23
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
„
Se puede mejorar la comunicación organizando los procesos
en forma de árbol y haciendo que todos participen en la
distribución.
Por ejemplo, podríamos subdividir así el trabajo:
0
0
1
0
0
2
4
2
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
1
6
1
3
5
3
7
7. Programación paralela de paso de mensajes 24
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
Así pues, para implementar este código necesitamos saber:
„
„
si un proceso ha de recibir y de quién;
si un proceso ha de enviar y a quién.
„
„
„
„
Esto puede ser complicado si no hay una forma canónica de
hacerlo.
El ejemplo anterior es simplemente una de las posibilidades.
Sin un conocimiento de la topología del sistema, no se puede
saber qué esquema de comunicación es mejor.
Un paso de mensajes que involucra a todos los procesos de
un grupo de comunicación se llama comunicación colectiva.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 25
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
„
„
Para resolver este problema, MPI proporciona en cada
sistema una función que permite difundir una información a
todos los procesos de una forma óptima.
MPI sólo especifica la interfaz y el resultado de llamarla. La
implementación se deja al constructor de la librería para un
entorno determinado.
La función que realiza esta misión se llama MPI_Bcast y
tiene el siguiente prototipo:
MPI_Bcast(void
*mensaje,
int
cuenta,
MPI_Datatype tipodato,
int
raiz,
MPI_Comm
grupo_com);
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/*
/*
/*
/*
/*
ÍÎ */
Í */
Í */
Í */
Í */
7. Programación paralela de paso de mensajes 26
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
La llamada a la función MPI_Bcast puede resultar en
„
„
„
„
„
recepción de datos, si el rango del proceso es distinto de raiz.
envío de datos, si el rango del proceso es igual a raiz.
El valor de tipodato y cuenta ha de ser igual para todos.
Los procesos afectados serán todos los que se encuentren
en el grupo de comunicaciones grupo_com.
El sistema MPI garantiza que si un proceso difunde varios
mensajes (varias llamadas a MPI_Bcast), éstos serán
recibidos por los demás procesos en el mismo orden en que
fueron emitidos.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 27
Arquitectura e Ingeniería de Computadores
Ejemplo de integración con trapecios
/* trap.c – Integración mediante trapecios.
Algoritmo:
1. Cada proceso se autoasigna su propio intervalo de integración.
2. Cada proceso integra f(x) en su propio intervalo con el método de los
trapecios.
3a. Cada proceso con rango != 0 envía su resultado al proceso 0.
3b. El proceso 0 suma los resultados de los cálculos realizados por los demás
e imprime el resultado.
*/
#include <stdio.h>
#include "mpi.h"
/* Prototipo de la función que integra */
float Trap(float local_a, float local_b, int local_n, float h);
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 28
Arquitectura e Ingeniería de Computadores
Ejemplo de integración con trapecios
main(int argc, char *argv[]) {
int
mi_rango; /* El rango de mi proceso
int
p;
/* Número total de procesos
float
a = 0.0;
/* Extremo izquierdo
float
b = 1.0;
/* Extremo derecho
int
n = 1024; /* Número de trapecios
float
h;
/* Base de cada trapecio
float
local_a;
/* Extremo izdo. de mi proceso
float
local_b;
/* Extermo dcho. de mi proceso
int
local_n;
/* Número de trapecios para mi cálculo
float
integral; /* Resultado de la integral en mi intervalo
float
total;
/* Integral total
int
fuente;
/* Proceso que remite el resultado
int
dest = 0; /* Todos los resultados van al proceso 0
int
tag = 0;
MPI_Status status;
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
MPI_Init(&argc, &argv);
/* Arrancamos MPI */
MPI_Comm_rank(MPI_COMM_WORLD, &mi_rango); /* Obtengo mi propio rango */
MPI_Comm_size(MPI_COMM_WORLD, &p);
/* Obtengo nº total de procesos */
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 29
Arquitectura e Ingeniería de Computadores
Ejemplo de integración con trapecios
h = (b-a)/n;
local_n = n/p;
/* h es el mismo para todos los procesos */
/* igual que el número de trapecios */
/* La longitud del intervalo de integración de cada proceso es igual a
local_n*h. Así pues, mi intervalo empieza y acaba en: */
local_a = a + mi_rango*local_n*h;
local_b = local_a + local_n*h;
/* Calculo la integral en mi intervalo */
integral = Trap(local_a, local_b, local_n, h);
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 30
Ejemplo de integración con trapecios
Arquitectura e Ingeniería de Computadores
if (mi_rango == 0) {
/* Sumo los cálculos de cada proceso, que me envían los demás */
total = integral;
for (fuente = 1; fuente < p; fuente++) {
MPI_Recv(&integral, 1, MPI_FLOAT, fuente, tag,
MPI_COMM_WORLD, &status);
total = total + integral;
}
} else { /* Envío mi resultado al proceso 0 */
MPI_Send(&integral, 1, MPI_FLOAT, dest,
tag, MPI_COMM_WORLD);
}
if (mi_rango == 0) {
/* Imprimo resultados */
printf(“Con n = %d trapecios, la integral de f(x) ", n);
printf(“desde %f a %f = %f\n", a, b, total);
}
/* Cerrar MPI */
MPI_Finalize();
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 31
Arquitectura e Ingeniería de Computadores
Ejemplo de integración con trapecios
float Trap(float
float
int
float
float
float
int
float
local_a,
local_b,
local_n,
h) {
/*
/*
/*
/*
Í
Í
Í
Í
*/
*/
*/
*/
integral;
/* Para almacenar el resultado
x;
i;
f(float x); /* Función a integrar */
*/
integral = (f(local_a) + f(local_b))/2.0;
x = local_a;
for (i = 1; i <= local_n-1; i++) {
x = x + h;
integral = integral + f(x);
}
integral = integral*h;
return integral;
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 32
Arquitectura e Ingeniería de Computadores
Ejemplo de integración con trapecios
/* Función que vamos a integrar. Metemos el código que corresponda a la función
que deseamos integrar
*/
float f(float x) {
float return_val;
/* Calculamos f(x) y lo almacenamos en return_val */
...
return return_val;
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 33
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
El programa de la integración con trapecios tiene dos fases:
„
„
„
„
Cálculo de las integrales parciales
Suma de los resultados
La primera fase está equilibradamente distribuida entre los
distintos procesos.
La fase de suma la realiza exclusivamente el proceso 0.
„
„
Podríamos redistribuir el trabajo de suma entre los distintos
procesos para equilibrar este trabajo.
Se podría usar un diagrama en árbol como antes.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 34
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
Para resolver este problema, MPI proporciona la función
MPI_Reduce con el siguiente prototipo:
MPI_Reduce(void
*operando,
void
*resultado,
int
cuenta,
MPI_Datatype tipodato,
MPI_Op
operacion,
int
raiz,
MPI_Comm
grupo_com);
„
Í
Î
Í
Í
Í
Í
Í
*/
*/
*/
*/
*/
*/
*/
MPI_Reduce combina los operandos almacenados en
operando usando la operación operacion y almacena el
resultado en resultado en el proceso raiz. Tanto operando
como resultado se refieren a cuenta elementos de tipo
tipodato.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/*
/*
/*
/*
/*
/*
/*
7. Programación paralela de paso de mensajes 35
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
„
MPI_Reduce ha de ser llamado en todos los procesos del
grupo de comunicación grupo_com y cuenta, tipodato y
operacion han de valer lo mismo en todos los procesos.
El argumento operacion puede valer
„
„
„
„
„
„
MPI_SUM
MPI_PROD
MPI_MAX
MPI_MIN
...
Obsérvese que la variable resultado sólo tiene sentido en el
proceso raiz. Aun así, los demás procesos también han de
especificarla.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 36
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
Podemos, entonces, sustituir
if (mi_rango == 0) {
/* Sumo los cálculos de cada proceso, que me envían los demás */
total = integral;
for (fuente = 1; fuente < p; fuente++) {
MPI_Recv(&integral, 1, MPI_FLOAT, fuente, tag,
MPI_COMM_WORLD, &status);
total = total + integral;
}
} else { /* Envío mi resultado al proceso 0 */
MPI_Send(&integral, 1, MPI_FLOAT, dest,
tag, MPI_COMM_WORLD);
}
por
MPI_Reduce(&integral, &total, 1, MPI_FLOAT, MPI_SUM, 0,
MPI_COMM_WORLD);
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 37
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
En algunos casos nos interesa que la reducción se efectúe en
todos los procesos. Para ello existe la función MPI_Allreduce
cuyo prototipo es:
MPI_Allreduce(void
*operando,
void
*resultado,
int
cuenta,
MPI_Datatype tipodato,
MPI_Op
operacion,
MPI_Comm
grupo_com);
„
Í
Î
Í
Í
Í
Í
*/
*/
*/
*/
*/
*/
Se usa exactamente igual que MPI_Reduce, pero el resultado
de la reducción se acumula en resultado en todos los
procesos pertenecientes al grupo de comunicación grupo_com.
Por ello no es necesario el parámetro raiz, como en el otro
caso.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/*
/*
/*
/*
/*
/*
7. Programación paralela de paso de mensajes 38
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
Las barreras están directamente implementadas en MPI:
MPI_Barrier(MPI_Comm
„
„
„
Cada proceso del grupo de comunicación grupo_com se
quedará bloqueado hasta que todos ellos hayan llamado a esta
función.
Cuando el último de ellos la llame, todos se desbloquean
simultáneamente.
Es un mecanismo de sincronización.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
grupo_com); /* Í */
7. Programación paralela de paso de mensajes 39
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
Otras funciones de comunicación colectiva son las siguientes:
MPI_Gather(void
*buffer_envio,
int
cuenta_envio,
MPI_Datatype tipo_envio,
void
*buffer_recepcion,
int
cuenta_recepcion,
MPI_Datatype tipo_recepcion,
int
raiz,
MPI_Comm
grupo_com);
„
„
Í
Í
Í
Î
Í
Í
Í
Í
*/
*/
*/
*/
*/
*/
*/
*/
Cada proceso del grupo de comunicación grupo_com envía los
contenidos de buffer_envio al proceso raiz.
El proceso raiz concatena los datos recibidos por orden de
rango en buffer_recepcion, es decir, los datos del proceso
0, a continuación los del 1, etc.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/*
/*
/*
/*
/*
/*
/*
/*
7. Programación paralela de paso de mensajes 40
Arquitectura e Ingeniería de Computadores
Comunicación colectiva
„
„
Los argumentos de recepción sólo son significativos en el
proceso raiz.
El argumento cuenta_recepcion indica el número de items
recibidos de cada proceso (no el total).
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 41
Comunicación colectiva
Arquitectura e Ingeniería de Computadores
MPI_Scatter(void
*buffer_envio,
int
cuenta_envio,
MPI_Datatype tipo_envio,
void
*buffer_recepcion,
int
cuenta_recepcion,
MPI_Datatype tipo_recepcion,
int
raiz,
MPI_Comm
grupo_com);
„
„
Í
Í
Í
Î
Í
Í
Í
Í
*/
*/
*/
*/
*/
*/
*/
*/
El proceso con rango raiz distribuye los contenidos del buffer
buffer_envio en tantos segmentos como procesos haya,
cada uno con un tamaño de cuenta_envio items.
Los argumentos de envío son significativos sólo en el proceso
raiz.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/*
/*
/*
/*
/*
/*
/*
/*
7. Programación paralela de paso de mensajes 42
Comunicación colectiva
Arquitectura e Ingeniería de Computadores
MPI_Allgather(void
*buffer_envio,
int
cuenta_envio,
MPI_Datatype tipo_envio,
void
*buffer_recepcion,
int
cuenta_recepcion,
MPI_Datatype tipo_recepcion,
MPI_Comm
grupo_com);
„
„
Í
Í
Í
Î
Í
Í
Í
*/
*/
*/
*/
*/
*/
*/
Cada proceso del grupo de comunicación grupo_com envía los
contenidos de buffer_envio a todos los demás procesos.
El efecto es equivalente a llamar a MPI_Gather tantas veces
como procesos haya, actuando sucesivamente cada uno de
ellos como raiz.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/*
/*
/*
/*
/*
/*
/*
7. Programación paralela de paso de mensajes 43
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Enviar mensajes es una operación costosa.
„
„
„
Hay que tratar de enviar el mínimo número posible.
La solución obvia es agrupar los mensajes.
Existen tres mecanismos para agrupar mensajes:
„
„
„
Parámetro cuenta.
Tipos de datos derivados.
Las rutinas MPI_Pack/MPI_Unpack.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 44
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
„
„
Recordemos que las funciones MPI_Send, MPI_Receive,
MPI_Bcast, MPI_Reduce tienen todas el parámetro cuenta
y el parámetro tipodato.
Con ellos, el usuario puede agrupar datos que sean del
mismo tipo básico en un solo mensaje.
Condición imprescindible:
„
„
los datos han de estar almacenados en memoria contigua.
Esto es útil para los vectores o matrices.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 45
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Ejemplo: enviar la segunda mitad de un vector de 100
flotantes desde el proceso 0 al 1.
float vector[100];
MPI_Status status;
int p;
int mi_rango;
...
/* Inicializar vector y enviar */
if (mi_rango == 0) {
...
MPI_Send(vector+50, 50, MPI_FLOAT, 1, 0, MPI_COMM_WORLD);
} else if (mi_rango == 1) {
MPI_Recv(vector+50, 50, MPI_FLOAT, 0, 0, MPI_COMM_WORLD,
&status);
...
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 46
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
„
„
Si los datos no son de igual tipo, hemos de construir tipos
derivados a partir de los primitivos.
Un tipo MPI derivado es una sucesión de n pares
{(t0, d0), (t1, d1), ..., (tn−1,dn−1)}
donde cada ti es un tipo básico y cada di es un
desplazamiento en bytes.
Un ejemplo podría ser:
{(MPI_FLOAT, 0),(MPI_FLOAT, 16),(MPI_INT, 24)}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 47
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
La función para construir un tipo nuevo es:
MPI_Type_struct(int
int
MPI_Aint
MPI_Datatype
MPI_Datatype
„
„
„
„
/*
/*
/*
/*
/*
Í
Í
Í
Í
Î
*/
*/
*/
*/
*/
El parámetro cuenta es el número de elementos del tipo derivado
y el tamaño de los vectores long_bloque, desplazamiento y
lista_de_tipos.
El vector lista_de_tipos contiene el tipo de dato MPI para
cada entrada.
El vector desplazamiento contiene el desplazamiento con
respecto al comienzo del mensaje de cada entrada.
El vector long_bloque indica cuántos elementos de cada tipo
hay en cada entrada.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
cuenta,
long_bloque[],
desplazamiento[],
lista_de_tipos[],
*nuevo_tipo);
7. Programación paralela de paso de mensajes 48
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Ejemplo de construir un nuevo tipo.
„
Queremos construir un mensaje con las variables
float a;
float b;
int
n;
int
long_bloque[3];
MPI_Aint
desplazamientos[3];
MPI_Datatype lista_de_tipos[3];
/* Para los cálculos de direcciones */
MPI_Aint start_address;
MPI_Aint address;
/* Nuevo tipo */
MPI_Datatype nuevo_tipo;
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 49
Arquitectura e Ingeniería de Computadores
Agrupación de datos
/* Nuestros datos son de un solo elemento cada uno */
long_bloque[0] = long_bloque[1] = long_bloque[2] = 1;
/* Nuestras variables son dos flotantes y un entero */
lista_de_tipos[0] = lista_de_tipos[1] = MPI_FLOAT;
lista_de_tipos[2] = MPI_INT;
/* El primer elemento a lo ponemos a desplazamiento 0 */
desplazamientos[0] = 0;
/* Calculamos los otros desplazamientos respecto de a */
MPI_Address(&a, &start_address);
MPI_Address(&b, &address);
desplazamientos[1] = address - start_address;
MPI_Address(&n, &address);
desplazamientos[2] = address - start_address;
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 50
Agrupación de datos
Arquitectura e Ingeniería de Computadores
/* Construimos el tipo derivado */
MPI_Type_struct(3, long_bloque, desplazamientos, lista_de_tipos,
&nuevo_tipo);
/* Informamos al sistema del nuevo tipo de datos */
MPI_Type_commit(&nuevo_tipo);
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 51
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Observemos dos funciones que hemos introducido:
MPI_Address(void
*variable,
/* Í */
MPI_Aint *direccion); /* Î */
„
En principio es equivalente a la sentencia:
direccion = &variable;
„
pero nos permite asegurar la portabilidad.
MPI_Type_commit(MPI_Datatype *nuevo_tipo); /* ÍÎ */
„
„
Mediante este mecanismo, el sistema realiza unos cambios
internos en la representación de nuevo_tipo para mejorar el
rendimiento.
Estos cambios no son necesarios si nuevo_tipo es usado solo
como paso intermedio para construir un tipo más complicado. Por
eso esta función va aparte.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 52
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Tenemos otros constructores más sencillos:
MPI_Type_contiguous(int
cuenta,
MPI_Datatype tipo_viejo,
MPI_Datatype *tipo_nuevo);
„
El nuevo tipo consiste en cuenta elementos contiguos de un
vector de elementos de tipo tipo_viejo.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
/* Í */
/* Í */
/* Î */
7. Programación paralela de paso de mensajes 53
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Ejemplo: queremos mandar una fila una matriz de flotantes
de tamaño 10×10:
float A[10][10];
MPI_Datatype tipo_fila_10;
...
MPI_Type_contiguous(10, MPI_FLOAT, &tipo_fila_10);
MPI_Type_commit(&tipo_fila_10);
if (mi_rango == 0) {
MPI_Send(&(A[2][0]), 1, tipo_fila_10, 1, 0, MPI_COMM_WORLD);
} else { /* mi_rango == 1 */
MPI_Recv(&(A[2][0]), 1, tipo_fila_10,
0, 0, MPI_COMM_WORLD, &status);
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 54
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Otro constructor:
MPI_Type_vector(int
int
int
MPI_Datatype
MPI_Datatype
„
„
/*
/*
/*
/*
/*
Í
Í
Í
Í
Î
*/
*/
*/
*/
*/
El nuevo tipo consiste en elementos igualmente espaciados de un
vector.
El parámetro cuenta es el número de elementos del nuevo tipo,
long_bloque es el número de entradas en cada elemento,
espaciado es el número de elementos de tipo tipo_elem entre
sucesivos elementos del tipo tipo_nuevo.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
cuenta,
long_bloque,
espaciado,
tipo_elem,
*tipo_nuevo);
7. Programación paralela de paso de mensajes 55
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Ejemplo: queremos mandar una columna de una matriz de
flotantes de tamaño 10×10:
float A[10][10];
MPI_Datatype tipo_columna_10;
...
MPI_Type_vector(10, 1, 10, MPI_FLOAT, &tipo_columna_10);
MPI_Type_commit(&tipo_columna_10);
if (mi_rango == 0) {
MPI_Send(&(A[0][2]), 1, tipo_columna_10, 1, 0, MPI_COMM_WORLD);
} else { /* mi_rango == 1 */
MPI_Recv(&(A[0][2]), 1, tipo_columna_10,
0, 0, MPI_COMM_WORLD, &status);
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 56
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Otro constructor más:
MPI_Type_indexed(int
int
int
MPI_Datatype
MPI_Datatype
„
„
/*
/*
/*
/*
/*
Í
Í
Í
Í
Î
*/
*/
*/
*/
*/
El nuevo tipo consiste en cuenta elementos de tipo tipo_elem.
El i-ésimo elemento consta de long_bloques[i] entradas y está
desplazado desplazamientos[i] elementos de tipo
tipo_elem respecto del principio.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
cuenta,
long_bloques[],
desplazamientos[],
tipo_elem,
*tipo_nuevo);
7. Programación paralela de paso de mensajes 57
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Ejemplo: queremos mandar la porción triangular superior de
una matriz de n×n elementos:
float
A[n][n];
/* Matriz completa */
float
T[n][n];
/* Triángulo superior */
int
desplazamientos[n];
int
long_bloques[n];
MPI_Datatype tipo_triang;
...
for (i = 0; i < n; i++) {
long_bloques[i]
= n - i;
desplazamientos[i] = (n + 1)*i;
}
MPI_Type_indexed(n, long_bloques, desplazamientos,
MPI_FLOAT, &tipo_triang);
MPI_Type_commit(&tipo_triang);
/* ... Se envía como antes ... */
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 58
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
„
„
¿Cómo se realiza la correspondencia entre un tipo de dato
enviado y uno recibido?
Recordemos que un tipo derivado se define como una
sucesión de n pares
{(t0, d0), (t1, d1), ..., (tn−1,dn−1)}.
La sucesión de tipos {t0, t1,..., tn−1} es la firma del tipo. La regla
es que la firma de los tipos que se envía y se recibe sea
compatible.
„
Si la firma del tipo enviado es {t0, t1,..., tn−1} y la del recibido {u0,
u1,..., um−1} se debe verificar que
„
„
n≤m
ti = ui, para i = 0, …, n − 1
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 59
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Por último, podemos empaquetar los datos explícitamente y
desempaquetarlos al recibir. Tenemos:
MPI_Pack(void
/* Í */
*paquete,
int
cuenta,
MPI_Datatype tipo_dato,
void
*buffer,
int
tamanyo_buffer,
int
*posicion,
MPI_Comm
grupo_com);
MPI_Unpack(void
V1.4
Í */
Í */
Î */
Í */
ÍÎ */
Í */
/* Í */
*buffer,
int
tamanyo_buffer,
int
*posicion,
void
*desempaquetado,
int
cuenta,
MPI_Datatype tipo_dato,
MPI_Comm
grupo_com);
© J. A. de Frutos Redondo, R. Durán 2005
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Í */
ÍÎ */
Î */
Í */
Í */
Í */
7. Programación paralela de paso de mensajes 60
Arquitectura e Ingeniería de Computadores
Agrupación de datos
„
Veamos con un ejemplo su uso:
float a;
float b;
int
n;
char buffer[100];
int
posicion = 0;
if (mi_rango == 0) {
MPI_Pack(&a, 1, MPI_FLOAT, buffer, 100,
&posicion, MPI_COMM_WORLD);
MPI_Pack(&b, 1, MPI_FLOAT, buffer, 100,
&posicion, MPI_COMM_WORLD);
/*
/*
/*
/*
/*
/*
posicion se incrementa hasta la siguiente posición
libre del buffer.
posicion se incrementa de nuevo.
MPI_Pack(n, 1, MPI_INT, buffer, 100,
&posicion, MPI_COMM_WORLD);
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 61
*/
*/
*/
*/
*/
*/
Arquitectura e Ingeniería de Computadores
Agrupación de datos
...
/* Ahora difundimos el contenido del buffer */
MPI_Bcast(buffer, 100, MPI_PACKED, 0, MPI_COMM_WORLD);
} else {
MPI_Bcast(buffer, 100, MPI_PACKED, 0, MPI_COMM_WORLD);
/* Desempaquetamos */
MPI_Unpack(buffer, 100, &posicion, &a, 1, /* posicion se in*/
MPI_FLOAT, MPI_COMM_WORLD);
/* menta también de */
/* manera automática */
MPI_Unpack(buffer, 100, &posicion, &b, 1,
MPI_FLOAT, MPI_COMM_WORLD);
MPI_Unpack(buffer, 100, &position, &n, 1,
MPI_INT, MPI_COMM_WORLD);
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 62
Arquitectura e Ingeniería de Computadores
La entrada/salida
„
Muchos procesos se están ejecutando simultáneamente,
„
„
„
es necesario decidir quién efectúa la escritura en la salida
estándar
y quién la lectura de la entrada estándar.
La solución más típica es que sólo un proceso se ocupe de
ello y
„
„
reciba de los demás lo que debe ser escrito
y envíe a los demás lo que ha leído.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 63
Arquitectura e Ingeniería de Computadores
La entrada/salida
„
„
„
Las funciones normales de C para entrada/salida (printf,
scanf) no están pensadas para el paralelismo.
No existe un consenso claro acerca de cómo debe funcionar
la entrada/salida en sistemas multiproceso.
MPI no normaliza nada acerca de la entrada/salida.
„
No obstante las implementaciones de MPI suelen proporcionar
algún tipo de soporte.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 64
Arquitectura e Ingeniería de Computadores
La entrada/salida
„
Normalmente, todas las implementaciones de MPI:
„
„
En el caso más sencillo, convertimos las funciones de E/S en
operaciones colectivas, utilizando un proceso designado
como “proceso de E/S”.
„
„
„
Permiten que al menos un proceso pueda acceder a la entrada
y salida estándar y a la salida de error.
Para salida, el proceso de E/S recogerá los datos de los demás
y los imprimirá.
Para entrada, el proceso de E/S leerá los datos necesarios y los
difundirá a los demás procesos.
Así pues, cada comunicador contendrá un proceso designado
para la E/S.
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 65
Arquitectura e Ingeniería de Computadores
Ejemplo del algoritmo de Gauss-Seidel
/* Integración de un sistema por el algoritmo de Gauss-Seidel.
Algoritmo:
1. Se realiza una división por bloques. El número de procesos ha de ser un
divisor de la constante MATRIZ.
2. Cada proceso inicializa su bloque. A cada bloque se le añaden dos filas
“fantasma” para intercambiar datos con los procesos asociados a los bloques
superior e inferior.
3a. Cada proceso realiza el cómputo del promedio, intercambiando los
resultados con sus vecinos.
3b. El proceso 0 “reduce” las diferencias de todos, para ver si se ha de
terminar. En tal caso, difunde el valor done=0 a todos los demás.
4. Cada proceso imprime su bloque en un fichero.
*/
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 66
Arquitectura e Ingeniería de Computadores
Ejemplo del algoritmo de Gauss-Seidel
#
#
#
#
include
include
include
include
#
#
#
#
#
#
#
#
#
define
define
define
define
define
define
define
define
define
<stdlib.h>
<stdio.h>
<math.h>
"mpi.h"
MATRIZ
LAMBDA
LADO_I
LADO_D
ARRIBA
ABAJO
INTERNO
FILA
TOL
(128)
0.2
30
30
50
50
15
1
0.001
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 67
Arquitectura e Ingeniería de Computadores
Ejemplo del algoritmo de Gauss-Seidel
void Resuelve(float myA[][MATRIZ+2], int pid, int nprocs) {
int i,j, n1 = 2 + MATRIZ, n2 = 2 + MATRIZ/nprocs, done = 0;
float temp, diff, mydiff = 0;
MPI_Status status;
while (!done) {
mydiff = 0;
if (pid != 0)
MPI_Send(&myA[1][0], n1, MPI_FLOAT, pid - 1,
if (pid != nprocs-1)
MPI_Send(&myA[n2 - 2][0], n1, MPI_FLOAT, pid
MPI_COMM_WORLD);
if (pid != 0)
MPI_Recv(&myA[0][0], n1, MPI_FLOAT, pid - 1,
MPI_COMM_WORLD, &status);
if (pid != nprocs-1)
MPI_Recv(&myA[n2 - 1][0], n1, MPI_FLOAT, pid
MPI_COMM_WORLD, &status);
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
FILA, MPI_COMM_WORLD);
+ 1, FILA,
FILA,
+ 1, FILA,
7. Programación paralela de paso de mensajes 68
Ejemplo del algoritmo de Gauss-Seidel
Arquitectura e Ingeniería de Computadores
for (i = 1; i < n2 - 1; i++) {
for (j = 1; j < n1 - 1; j++) {
temp = myA[i][j];
myA[i][j] = LAMBDA*(myA[i][j]
+ myA[i][j-1] + myA[i-1][j] +
myA[i][j+1] + myA[i+1][j]);
mydiff += fabs(myA[i][j] - temp);
}
}
MPI_Reduce(&mydiff, &diff, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);
if (pid == 0) {
if (diff/(MATRIZ*MATRIZ) < TOL) {
done = 1;
}
}
MPI_Bcast(&done, 1, MPI_INT, 0, MPI_COMM_WORLD);
}
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 69
Arquitectura e Ingeniería de Computadores
Ejemplo del algoritmo de Gauss-Seidel
void Inicializa(float myA[][MATRIZ+2], int pid, int nprocs) {
int i,j;
int n1 = 2 + MATRIZ, n2 = 2 + MATRIZ/nprocs;
for (i = 0; i < n2; i++)
{
for (j = 0; j < n1; j++)
myA[i][j] = INTERNO;
}
for (i = 0; i < n2; i++)
{
myA[i][0]
= LADO_I;
myA[i][n1 - 1] = LADO_D;
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 70
Arquitectura e Ingeniería de Computadores
Ejemplo del algoritmo de Gauss-Seidel
if (pid == 0) {
for (j = 0; j < n1; j++) {
myA[0][j] = ARRIBA;
}
}
if (pid == nprocs - 1) {
for (j = 0; j < n1; j++) {
myA[n2 - 1][j] = ABAJO;
}
}
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 71
Arquitectura e Ingeniería de Computadores
Ejemplo del algoritmo de Gauss-Seidel
void PrintData(float myA[][MATRIZ+2], int pid, int nprocs)
{
int i, j;
int n1 = 2 + MATRIZ;
int n2 = 2 + MATRIZ/nprocs;
FILE *fpc = NULL;
char name[256] = "";
sprintf(name, "out%d.dat", pid);
if ((fpc = fopen(name, "w")) != NULL) {
for (i = 1; i < n2 - 1 ; i++)
{
for (j = 1; j < n1 - 1; j++)
fprintf(fpc, "%.3f ", myA[i][j]);
fprintf(fpc, "\n");
}
fclose(fpc);
}
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 72
Arquitectura e Ingeniería de Computadores
Ejemplo del algoritmo de Gauss-Seidel
main(int argc, char *argv[]) {
float *myA
= NULL; /* Matriz en este proceso
*/
int
pid
= 0;
/* Rango del proceso
*/
int
nprocs = 0;
/* Número de procesos usados */
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &pid);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
myA = (float *)malloc(sizeof(float)*(2 + MATRIZ)*(2 + MATRIZ/nprocs));
Inicializa(myA, pid, nprocs); /* Inicializamos la matriz */
MPI_Barrier(MPI_COMM_WORLD); /* Espera que se inicialicen todos */
Resuelve(myA, pid, nprocs);
/* Resolvemos */
MPI_Barrier(MPI_COMM_WORLD); /* Espera que acaben todos */
PrintData(myA, pid, nprocs); /* Imprimimos los resultados en ficheros */
MPI_Finalize();
return 0;
}
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 73
Arquitectura e Ingeniería de Computadores
Resultado de la ejecución serie
Matriz de 32 × 32 puntos
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 74
Arquitectura e Ingeniería de Computadores
Resultado usando MPI
Matriz de 32 × 32 puntos
© J. A. de Frutos Redondo, R. Durán 2005
V1.4
7. Programación paralela de paso de mensajes 75
Descargar