Paradigmas de Computação Paralela
Passagem de Mensagens
João Luís Ferreira Sobral
Departamento do Informática
Universidade do Minho
Passagem de Mensagens
Paradigmas para aplicações distribuídas
Passagem de Mensagens
Conceitos base
l
Especificação do paralelismo através de processos com um espaço de endereçamento
próprio
l
Processos podem ser idênticos (SPMD, e.g. MPI) ou não (MIMD, e.g., PVM)
l
Identificação de entidades através de portas
l
Envio e recepção explícito de mensagem (de/para uma porta específica)
l
Empacotamento/desempacotamento explícito da informação em mensagens
l
Primitivas mais elaboradas para comunicação (difusão/redução/barreiras)
Passagem de Mensagens
Passagem de mensagens
versus
Invocação remota de métodos (RMI)
Passagem de Mensagens Invocação Remota de Métodos
Dados a transmitir Empacotamento de dados Lista de parâmetros
Envio de pedidos/ informação
Envio explícito de mensagens
etiquetadas Invocação de um método específico
Recepção de pedidos/ informação
Recepção explícita de mensagens
Execução implícita do método invocado
Reacção do receptor
A acção a executar é determinada pela etiqueta (tag)
da mensagem
A acção a executar é determinada pelo método invocado
Passagem de Mensagens
MPI (Message Passing Interface)
http://www.mpi-forum.org
l
Standard para a passagem de mensagens que surgiu do esforço de várias entidades para
tornar as aplicações paralelas portáveis
l
Baseado num modelo SPMD (o mesmo processo é executado em várias máquinas)
l O mesmo executável é lançado num conjunto de máquinas (seleccionadas na linha de comando)
l Cada processo tem um identificador único
l Entrega de mensagens por ordem (in-order delivery)
l
Implementado através de uma biblioteca de funções
l
Implementações mais frequentes (de uso livre): OpenMPI,MPICH, e LamMPI
l
Principais características:
l Vários modos de passagem de mensagens
§ Síncrona / assíncrona
l Grupos de comunicação / topologias
l Grande variedade de operações colectivas
§ Broadcast, Scatter/gather, reduce, all-to-all, barrier
l MPI-2
Passagem de Mensagens
Estrutura de um programa MPI
l
Iniciar a biblioteca e comunicações
l MPI_Init
§ inicia a biblioteca
l MPI_Comm_size
§ Número de processos total
l MPI_Comm_rank
§ ID do processo atual
l
Executar o corpo do programa
l MPI_Send
l MPI_Recv l
Terminar
l MPI_Finalize
l
Compilação e execução dos programas (MPICH)
l compilar: mpicc ou mpicxx (em /opt/mpich/gnu/bin)
l executar: mpirun –np <nºprocessos> a.out
#include “mpi.h” #include <stdio.h>
int main( int argc, char *argv[]) { int rank, msg;
MPI_Status status; MPI_Init(&argv, &argc);
MPI_Comm_rank( MPI_COMM_WORLD, &rank ); /* Process 0 sends and Process 1 receives */ if (rank == 0) {
msg = 123456;
MPI_Send( &msg, 1, MPI_INT, 1, 0, MPI_COMM_WORLD); }
else if (rank == 1) {
MPI_Recv( &msg, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status ); printf( “Received %d\n”, msg);
}
MPI_Finalize(); return 0;
Passagem de Mensagens
MPI – C++
l
Exemplo 2 (C++)
#include “mpi.h” #include <iostream>
int main( int argc, char *argv[]) { int rank, buf;
MPI::Init(argv, argc);
rank = MPI::COMM_WORLD.Get_rank(); // Process 0 sends and Process 1 receives if (rank == 0) {
buf = 123456;
MPI::COMM_WORLD.Send( &buf, 1, MPI::INT, 1, 0 ); }
else if (rank == 1) {
MPI::COMM_WORLD.Recv( &buf, 1, MPI::INT, 0, 0 ); std::cout << “Received “ << buf << “\n”;
}
MPI::Finalize(); return 0;
Passagem de Mensagens
MPI (Funcionalidades – cont.)
l
Comunicação ponto-a-ponto entre processos
int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
l é necessário especificar o tipo de dados da mensagem (MPI_INT, MPI_DOUBLE, etc)
l cada processo é identificado pela sua ordem dentro de um grupo
§ por defeito existe um grupo que inclui todos os processos: MPI_COMM_WORLD
§ dest / source indicam o destinatário / seccionam a origem da mensagem
l a etiqueta (tag) permite distinguir entre mensagens
l envio: Retorna assim que a mensagem foi colocada no buffer do receptor
l recepção: Espera pela chegada de uma mensagem com a(s) característica(s) pretendidas
Passagem de Mensagens
MPI – Modos de Comunicação ponto-a-ponto
l
Sobrecargas em passagem de mensagens
l Transferência da mensagem (cópia para a rede, transmissão na rede, entrega no buffer receptor)
l Espera pelo processo receptor
l
MPI_Ssend (blocking synchronous send)
l o emissor espera até a mensagem ser recebida pelo receptor (c/ MPI_Recv)
l
MPI_Bsend (Buffered send)
l Retorna assim que a mensagem foi colocada num buffer do lado do emissor
l Não tem sobrecarga de sincronização com o receptor, mas implica uma cópia para o buffer local l
MPI_Rsend (Ready send)
l Retorna assim que a mensagem foi colocada na rede
l Implica que do lado de receptor os recv devem preceder os send para evitar “deadlocks” l
MPI_Ixxx (non-bloking sends) c/ MPI_wait / MPI_Test /MPI_Probe
l Retorna imediatamente sendo o programador responsável por verificar se a operação está concluída c/ wait
Passagem de Mensagens
MPI – Operações colectivas
l
int MPI_Barrier(MPI_Comm comm)
l Espera que todos os processos cheguem à barreira
l
int MPI_Bcast(void* buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
l Difunde os dados de root para todos os processos
l
int MPI_Gather e int MPI_Scatter
(void* sbuf, int scount, MPI_Datatype stype, void* rbuf, int rcount, MPI_Datatype rtype, int root, MPI_Comm comm )
l Gather: Junta os dados de todos os processos para o root
l Scather: Espalha os dados de root todos os processos
l int MPI_Allgather & int MPI_Alltoall l
int MPI_Reduce
(void* sbuf, void* rbuf, int count, MPI_Datatype stype, MPI_Op op, int root, MPI_Comm comm)
l Combina os valores recebidos de todos os processos em root através de MPI_Op
l int MPI_Allreduce & int MPI_Reduce_scatter
Passagem de Mensagens
Implementação em Java
l
mpiJava – distribuição mais conhecida, “binding” para MPI (requer a instalação de MPI)
l
MPJExpress – sucessor de mpiJava com suporte transparente a MPI e “nio”;
l
CCJ, JMPI – implementações em java.io; fraco desempenho
l
MPP – baseada em “nio” de Java 4; versão mais eficiente, não segue o standard MPI
Communicator comm=new BlockCommunicator(); // package mpp.* int proc = comm.size();
int rank = comm.rank(); int [] buf = new int[1];
// Process 0 sends and Process 1 receives if (rank == 0) {
buf[0] = 123456;
comm.send( buf, 0, 1, 1 ); // send(int[] data, [int offset, int length,] int peer) } else if (rank == 1) {
comm.recv( buf, 0, 1, 0 ); // recv(int[] data, [int offset, int length,] int peer) System.out.println(“Recebi” + buf[0]);
} }
Exercício
Cálculo de números primos através do crivo de Eratosthenes
l
algoritmo para calcular todos os primos até um determinado máximo
l
pode ser implementado por uma cadeia de atividades, onde cada elemento filtra os seus
múltiplos
l
os números são enviados para a cadeia por ordem crescente. Cada elemento que chega ao
fim da cadeia é primo e é acrescentado ao fim desta como um novo filtro.
l
atividade paralela tem um rácio entre computação e a comunicação de uma operação
aritmética de inteiros (divisão) por mensagem
l rácio demasiado baixo para a generalidade das plataformas de memória distribuída.
2 3 3 2 3 2 5 3 2 3 2 4 5 3 2 5 Parallel task Message flow