Computación Paralela
Tema 3
MPI parte 3
Curso 2021/2022
Computación Paralela
1 / 35
N
Colectivas variables
Colectivas con número de elementos variables
Computación Paralela
2 / 35
N
Colectivas con número de elementos variable
I Varias operaciones colectivas tienen una versión que permite
definir para cada proceso un número de elementos diferente y
un desplazamiento dentro del buffer de envı́o/recepción.
I Varying count (V).
Operation Varying version
MPI Scatter MPI Scatterv
MPI Gather MPI Gatherv
MPI Allgather MPI Allgatherv
MPI Alltoall MPI Alltoallv
Computación Paralela
3 / 35
N
Colectivas con número de elementos variable
Ejemplo:
int MPI_Scatterv( void *sendbuf, int *sendcnts, int
*displs, MPI_Datatype sendtype, void *recvbuf,
int recvcnt, MPI_Datatype recvtype, int root,
MPI_Comm comm)
P0 A1 A2 A3 A4 A5 A6 A7 A8 A9 P0 A1
P1 A2 A3
P2 A6
displs 0 1 5 6 6
P3
cnts 1 2 1 0 3 P4 A7 A8 A9
Computación Paralela
4 / 35
N
Modos de comunicación
Diferentes tipos de comunicación y su protocolo.
Computación Paralela
5 / 35
N
Modos de comunicación
Dos conceptos:
I Modo de comunicación: Hace referencia al uso que se hace
de buffers internos en el envı́o y/o recepción.
I Comunicación no bloqueante: Comunicación que se ejecuta
en segundo plano, las funciones de envı́o/recepción retornan
inmediatamente y se usan más adelante otras funciones para
comprobar si la operación ha terminado o esperar a que
termine.
Computación Paralela
6 / 35
N
Envı́o y recepción
MPI_Send(data,5,MPI_INT,2,...) MPI_Recv(data,7,MPI_INT,1,...)
P1 P2
User User
memory memory
Network
Computación Paralela
7 / 35
N
Envı́o y recepción
MPI_Send(data,5,MPI_INT,2,...) MPI_Recv(data,7,MPI_INT,1,...)
P1 P2
User User
memory memory
Network
Computación Paralela
7 / 35
N
Envı́o y recepción
MPI_Send(data,5,MPI_INT,2,...) MPI_Recv(data,7,MPI_INT,1,...)
P1 P2
User User
memory memory
MPI lib MPI lib
memory memory
Network
Computación Paralela
7 / 35
N
Envı́o y recepción
MPI_Send(data,5,MPI_INT,2,...) MPI_Recv(data,7,MPI_INT,1,...)
P1 P2
User User
memory memory
MPI lib MPI lib
memory memory
Network
Computación Paralela
7 / 35
N
Envı́o y recepción
MPI_Send(data,5,MPI_INT,2,...) MPI_Recv(data,7,MPI_INT,1,...)
P1 P2
User User
memory memory
MPI lib MPI lib
memory memory
Network
Computación Paralela
7 / 35
N
Ventajas e inconvenientes de usar buffering
I Ventajas
I Agrupar mensajes pequeños mejora eficiencia.
I Permite realizar comunicaciones ası́ncronas (no bloqueantes).
I Reduce el tiempo de espera en envı́o.
I Inconvenientes
I Puede complicar/ralentizar el proceso de mensajes grandes.
I Incremento del uso de memoria.
I En la recepción se incrementa el tiempo de la comunicación
innecesariamente si el destino ya está esperando.
MPI permite controlar el buffering con los modos de comunicación.
Computación Paralela
8 / 35
N
Modos de comunicación
MPI define cuatro modos de envı́o y uno de recepción.
En general, la llamada a la función de envı́o termina cuando la
zona de datos que se envı́an puede volver a utilizarse.
I Buffered El usuario reserva su propio espacio para el buffer de
mensajes (MPI Buffer attach()).
I Synchronous El envı́o se bloquea hasta que se invoca en el
destino la operación de recepción correspondiente.
I Standard Es el modo por defecto. Puede utilizar buffer o ser
sı́ncrona. Depende de la implementación y las circunstancias.
I Ready Unicamente funciona si la operación de recepción ya
está activa.
Computación Paralela
9 / 35
N
Recv
I Solo hay un modo de recepción.
I La recepción se bloquea hasta que se recibe el mensaje.
I Al finalizar la función el cuerpo del mensaje se ha copiado a la
zona de memoria apuntada por el puntero data.
int MPI_Recv(void *data, int count, MPI_Datatype
datatype, int source, int tag, MPI_Comm comm,
MPI_Status *status)
Computación Paralela
10 / 35
N
Buffered send
I El usuario define su propio buffer
I Uso habitual: Cuando se conoce el volumen máximo de datos
a enviar (en una o más operaciones), el volumen es grande
(seguramente más que los bufferes internos de MPI), tenemos
memoria libre, no queremos que las ciertas operaciones Send
se bloqueen esperando a tener sitio en los bufferes de MPI
para copiar el mensaje, y queremos agilizar las copias al buffer.
I Operaciones: Se hace un alloc de memoria, se le indica a MPI
que use ese puntero como buffer
I Se utiliza una función de send especı́fica para que use ese
buffer
Computación Paralela
11 / 35
N
Buffered send
int MPI_Buffer_attach(void *buffer, int size)
int MPI_Bsend(void *data, int count, MPI_Datatype
datatype, int dest, int tag, MPI_Comm comm)
Computación Paralela
12 / 35
N
Buffered send
I MPI Bsend copia los datos al buffer.
I Cuando la función retorna los datos están en el buffer y los
datos originales o el puntero data ya se pueden modificar.
I El envı́o se realiza más adelante.
P1 P2
MPI_Bsend()
Buffer
MPI_Recv()
otherStuff()
Computación Paralela
13 / 35
N
Synchronous send
I Cuando el que envı́a no quiere continuar hasta estar seguro de
que el receptor ya está preparado para recibir.
int MPI_Ssend(void *data, int count, MPI_Datatype
datatype, int dest, int tag, MPI_Comm comm)
Computación Paralela
14 / 35
N
Synchronous send
I MPI Ssend se bloquea hasta que el destino ejecuta el recv.
P1 P2
MPI_Ssend()
MPI_Recv()
otherStuff()
Computación Paralela
15 / 35
N
Ready send
I Cuando no importa que se pierdan mensajes, sino maximiar la
eficiencia eliminando copias en bufferes innecesarias.
int MPI_Rsend(void *data, int count, MPI_Datatype
datatype, int dest, int tag, MPI_Comm comm)
Computación Paralela
16 / 35
N
Ready send
I Supone que el destino ya ha invocado el recv.
I Utiliza un protocolo más rápido.
I Si el mensaje llega antes de la recepción, se pierde.
P1 P2
MPI_Recv()
MPI_Rsend()
Computación Paralela
17 / 35
N
Default send
I El habitual, el que se utiliza por defecto
I Se ejecuta por MPI en uno de dos modos posibles
int MPI_Send(void *data, int count, MPI_Datatype
datatype, int dest, int tag, MPI_Comm comm)
Computación Paralela
18 / 35
N
Default send
I Puede ser synchronous o buffered.
I Depende de la implementación y la gestión interna de buffers.
I El buffer (en caso de utilizarlo) no lo define el usuario.
I Pueden aparecer deadlocks según la implementación, tamaño
de datos, etc.
I Los programas que usan default send deberı́an funcionar
correctamente para la implementación más restrictiva
(synchronous send).
I Suponer MPI Send = MPI Ssend.
Computación Paralela
19 / 35
N
Deadlock
I Bloqueo que se produce entre los diferentes procesos de una
comunicación cuando esperan la finalización de una
operación, que no puede ocurrir, para iniciar la siguiente.
I Dos tipos tı́picos en MPI:
I Blocking recv
I Synchronous send.
Computación Paralela
20 / 35
N
Deadlock tipo 1
int rank, size;
int data;
int tag = 0;
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Comm_size( MPI_COMM_WORLD, &size );
int dest = (rank +1 ) % size;
int source = (rank -1 + size) % size;
MPI_Recv(&data, 1, MPI_INT, source, tag, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
data = 123;
MPI_Send(&data, 1, MPI_INT, dest, tag, MPI_COMM_WORLD);
Computación Paralela
21 / 35
N
Deadlock tipo 1
P1 P2
MPI_Recv()
MPI_Recv()
MPI_Send() MPI_Send()
Computación Paralela
22 / 35
N
Deadlock tipo 2
int rank, size;
int data = 123;
int tag = 0;
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Comm_size( MPI_COMM_WORLD, &size );
int dest = (rank +1 ) % size;
int source = (rank -1 + size) % size;
MPI_Send(&data, 1, MPI_INT, dest, tag, MPI_COMM_WORLD);
MPI_Recv(&data, 1, MPI_INT, source, tag, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
Computación Paralela
23 / 35
N
¿Deadlock tipo 2?
P1 P2
MPI_Bsend()
MPI_Bsend()
MPI_Recv() MPI_Recv()
Computación Paralela
24 / 35
N
Deadlock tipo 2
P1 P2
MPI_Ssend()
MPI_Ssend()
MPI_Recv() MPI_Recv()
Computación Paralela
25 / 35
N
Comunicaciones no bloqueantes
Comunicaciones en dos fases, solapamiento de tareas y solución a
deadlocks.
Computación Paralela
26 / 35
N
Comunicaciones no bloqueantes
I Inician la comunicación pero las funciones retornan
inmediatamente, antes de que se complete.
I Permiten solapar comunicación y computación.
I Solución para los deadlocks.
I La llamada a la función puede terminar antes de que el
mensaje haya sido copiado al/desde el buffer.
I Para asegurar que la comunicación ha terminado se necesita
llamar a la función MPI Wait.
I Se pueden combinar envı́os bloqueantes/no bloqueantes con
recepciones bloqueantes/no bloqueantes.
I Estas funciones comienzan por MPI I*, (abreviatura de
Inmediato).
Computación Paralela
27 / 35
N
Nonblocking recv
Recepción: Se indica el mensaje que se espera recibir.
La función Wait espera a que el mensaje esté copiado en la
memoria indicada por el programador en el recv.
P1 P2
MPI_Irecv()
Recv data array
otherStuff() should not be
modified
MPI_Wait()
MPI_Send()
The message is
moreStuff() in the data array
Computación Paralela
28 / 35
N
Nonblocking send
Envı́o: Se indica el mensaje y destinatario.
La función Wait espera a que la zona de memoria pueda volver a
utilizarse. La semántica concreta depende del modo.
P1 P2
MPI_Isend()
Sent data array
otherStuff() should not be
modified
MPI_Wait()
MPI_Recv()
The data is in
the buffer or
moreStuff()
it has been
delivered
Computación Paralela
29 / 35
N
Modos y comunicaciones no bloqueantes
Modo Bloqueante No bloqueante
Buffered MPI Bsend MPI Ibsend
Synchronous MPI Ssend MPI Issend
Standard MPI Send MPI Isend
Ready MPI Rsend MPI Irsend
Recv MPI Recv MPI Irecv
Probe MPI Probe MPI Iprobe
Computación Paralela
30 / 35
N
Nonblocking send and recv
int MPI_Isend(void *data, int count, MPI_Datatype
datatype, int dest, int tag, MPI_Comm comm,
MPI_Request *request)
int MPI_Irecv(void *data, int count, MPI_Datatype
datatype, int source, int tag, MPI_Comm comm,
MPI_Request *request)
I MPI Request: Objeto con el estado de una operación no
bloqueante.
Computación Paralela
31 / 35
N
Finalización de comunicación
I MPI Wait: Espera hasta la finalización de una operación no
bloqueante.
int MPI_Wait(MPI_Request *request, MPI_Status *stat)
I MPI Test: Comprueba la finalización de una operación no
bloqueante. La variable flag toma el valor verdadero si ha
terminado.
int MPI_Test(MPI_Request *request, int *flag,
MPI_Status *status)
Computación Paralela
32 / 35
N
Solapar computación y comunicación
P1 P2 P1 P2
MPI_Isend()
MPI_Irecv()
otherStuff()
MPI_Send() MPI_Recv() otherStuff()
message message
transmision transmision
MPI_Wait() MPI_Wait()
Computación Paralela
33 / 35
N
Solución al deadlock
int rank, size;
int data_out = 123;
int data_in;
int tag = 0;
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Comm_size( MPI_COMM_WORLD, &size );
int dest = (rank +1 ) % size;
int source = (rank -1 + size) % size;
MPI_Request request;
MPI_Irecv(&data_in, 1, MPI_INT, source, tag, MPI_COMM_WORLD, &request);
MPI_Send(&data_out, 1, MPI_INT, dest, tag, MPI_COMM_WORLD);
MPI_Wait(&request, MPI_STATUS_IGNORE);
Computación Paralela
34 / 35
N
Otra solución al deadlock
int rank, size;
int data_out = 123;
int data_in;
int tag = 0;
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Comm_size( MPI_COMM_WORLD, &size );
int dest = (rank +1 ) % size;
int source = (rank -1 + size) % size;
MPI_Request request;
MPI_Isend(&data_out, 1, MPI_INT, dest, tag, MPI_COMM_WORLD, &request);
MPI_Recv(&data_in, 1, MPI_INT, source, tag, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
MPI_Wait(&request, MPI_STATUS_IGNORE);
Computación Paralela
35 / 35
N