Comunicação Coletiva
● Sempre envolve um grupo de processos; ● Sempre envolve todos os processos de um comunicador; ● Podem ser implementadas com as rotinas ponto a ponto (mas é má idéia); ● Implementam os padrões de comunicação paralela vistos no início do curso! ● Podem (mas não precisam) retornar assim que sua participação na operação esteja completa;Comunicação Coletiva
● A comunicação é considerada completa quando é possível reusar os buffers de comunicação; ● Não significa que os outros processos completaram a comunicação! ● Não necessariamente uma comunicação coletiva sincroniza o programa (exceto MPI_BARRIER); ● Não há interferência entre comunicações coletivas e comunicações ponto a ponto;Rotinas
● MPI_BARRIER; ● MPI_BCAST; ● MPI_GATHER; ● MPI_GATHERV; ● MPI_SCATTER; ● MPI_SCATTERV; ● MPI_ALLGATHER; ● MPI_ALLGATHERV; ● MPI_ALLTOALL; ● MPI_ALLTOALLV; ● MPI_ALLTOALLW; ● MPI_REDUCE; ● MPI_ALLREDUCE; ● MPI_REDUCE_ SCATTER; ● MPI_SCAN; ● MPI_EXSCAN;MPI_COMM_BARRIER
● Sincroniza todos os processos em umcomunicador, só prosseguem quando todos chamarem esta rotina;
C: int MPI_Barrier(MPI_Comm comm) Fortran: MPI_BARRIER(COMM, IERROR)
INTEGER COMM, IERROR
MPI_WTIME
● Retorna o tempo em segundos, a partir de
um instante no passado;
● Ver também MPI_WTICKS;
C: double MPI_Wtime(void)
Fortran: DOUBLE PRECISION MPI_WTIME() C++: double MPI::Wtime()
MPI_BCAST
● Implementa o “single node broadcast”: um processo manda a mesma informação para todos os processos; ● Um argumento indica quem é a fonte do broadcast; ● Todos os processos devem chamar a função com os mesmos argumentos para “root” e “comm”!;MPI_BCAST
C:int MPI_Bcast(void* buffer, int count,
MPI_Datatype datatype, int root, MPI_Comm comm)
Fortran:
MPI_BCAST(BUFFER, COUNT, DATATYPE, ROOT, COMM, IERROR)
<type> BUFFER(*)
INTEGER COUNT, DATATYPE, ROOT, COMM, IERROR
C++:
void MPI::Comm::Bcast(void* buffer, int count, const MPI::Datatype& datatype, int root) const = 0
MPI_SCATTER
● Implementa o Single Node Scatter; ● Um processo manda informações diferentes (mas de mesmo tamanho) para todos os outros; ● Todos os processos devem chamar a função com a mesma raiz e comunicador; ● É possível evitar a cópia local com buffer “in place”.MPI_SCATTER
C:int MPI_Scatter(
void* sendbuf, int sendcount, MPI_Datatype sendtype, void* recvbuf, int recvcount,
MPI_Datatype recvtype, int root, MPI_Comm comm)
MPI_SCATTER
Fortran:MPI_SCATTER(SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNT, RECVTYPE, ROOT, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER SENDCOUNT, SENDTYPE, RECVCOUNT INTEGER RECVTYPE, ROOT, COMM, IERROR
MPI_SCATTER
C++:
void MPI::Comm::Scatter(
const void* sendbuf, int sendcount, const MPI::Datatype& sendtype, void* recvbuf, int recvcount,
const MPI::Datatype& recvtype, int root) const = 0
MPI_SCATTERV
● MPI_SCATTER, com dados de tamanho diferente; ● Começa a ficar complicado quando os tipos de dados são complexos; ● É necessário indicar de onde começa o dado destinado a cada processo; ● Também pode ser usado buffer “in place”; ● “No overlap!”MPI_SCATTERV
0 1 2 3 4 5 6 7 2 1 2 3 0 2 3 5 SendBuff Sendcounts DisplacementsMPI_SCATTERV
C:
int MPI_Scatter(
void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int recvcount, MPI_Datatype recvtype,
MPI_SCATTERV
Fortran:
MPI_SCATTER(SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNT, RECVTYPE, ROOT, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER SENDCOUNT, SENDTYPE, RECVCOUNT INTEGER RECVTYPE, ROOT, COMM, IERROR
MPI_SCATTERV
C++:
void MPI::Comm::Scatter(
const void* sendbuf, int sendcount, const MPI::Datatype& sendtype,
void* recvbuf, int recvcount,
const MPI::Datatype& recvtype, int root) const = 0
MPI_GATHER
● Operação “dual” ao MPI_SCATTER; ● Cada processo manda uma informação diferente para o mesmo processo raiz; ● Todos os processos devem chamar com o mesmo argumento para raiz e comunicador; ● Pode ser usada comunicação “in place”.MPI_GATHER
C:
int MPI_Gather(
void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int recvcount,
MPI_Datatype recvtype, int root, MPI_Comm comm)
MPI_GATHER
Fortran:
MPI_GATHER(
SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNT, RECVTYPE, ROOT, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER SENDCOUNT, SENDTYPE, RECVCOUNT INTEGER RECVTYPE, ROOT, COMM, IERROR
MPI_GATHER
C++:
void MPI::Comm::Gather(
const void* sendbuf, int sendcount, const MPI::Datatype& sendtype,
void* recvbuf, int recvcount,
const MPI::Datatype& recvtype, int root) const = 0
MPI_GATHERV
● “Dual” de MPI_SCATTERV;● Análogo com comprimento variável a
MPI_GATHERV
C:
int MPI_Gatherv(
void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int *recvcounts, int *displs,
MPI_Datatype recvtype, int root, MPI_Comm comm)
MPI_GATHERV
Fortran:
MPI_GATHERV(
SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNTS, DISPLS, RECVTYPE, ROOT, COMM, IERROR) <type> SENDBUF(*), RECVBUF(*)
INTEGER SENDCOUNT, SENDTYPE
INTEGER RECVCOUNTS(*), DISPLS(*)
MPI_GATHERV
C++:
void MPI::Comm::Gatherv(
const void* sendbuf, int sendcount, const MPI::Datatype& sendtype, void* recvbuf, const int recvcounts[],
const int displs[],
const MPI::Datatype& recvtype, int root) const = 0
MPI_ALLGATHER
● Equivalente a “size” MPI_GATHER's simultâneos, com a raiz em cada um dos processos; ● (ou MPI_GATHER + MPI_BCAST); ● Pode usar comunicação “in place”;MPI_ALLGATHER
C:
int MPI_Allgather(
void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int recvcount, MPI_Datatype recvtype,
MPI_ALLGATHER
Fortran:
MPI_ALLGATHER(
SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNT, RECVTYPE, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*) INTEGER SENDCOUNT, SENDTYPE INTEGER RECVCOUNT, RECVTYPE INTEGER COMM, IERROR
MPI_ALLGATHER
C++:
void MPI::Comm::Allgather(
const void* sendbuf, int sendcount, const MPI::Datatype& sendtype,
void* recvbuf, int recvcount,
const MPI::Datatype& recvtype) const = 0
MPI_ALLGATHERV
● Como MPI_ALLGATHER, com dados de tamanhos variáveis; ● Normalmente, é necessário enviar os tamanhos antes de enviar os dados de interesse; ● Também pode ser “in place”.MPI_ALLGATHERV
C:
int MPI_Allgatherv(
void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int *recvcounts, int *displs,
MPI_Datatype recvtype, MPI_Comm comm)
MPI_ALLGATHERV
Fortran:
MPI_ALLGATHERV(
SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNTS, DISPLS, RECVTYPE, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*) INTEGER SENDCOUNT, SENDTYPE
INTEGER RECVCOUNTS(*), DISPLS(*) INTEGER RECVTYPE, COMM
MPI_ALLGATHERV
C++:
void MPI::Comm::Allgatherv(
const void* sendbuf, int sendcount, const MPI::Datatype& sendtype,
void* recvbuf, const int recvcounts[], const int displs[],
const MPI::Datatype& recvtype) const = 0
MPI_ALLTOALL
● Cada processo manda dados diferentes para todos os outros processos; ● “Total Exchange”; ● Não há opção para comunicação “in place”;MPI_ALLTOALL
C:
int MPI_Alltoall(
void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int recvcount, MPI_Datatype recvtype,
MPI_ALLTOALL
Fortran:
MPI_ALLTOALL(
SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNT, RECVTYPE, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*) INTEGER SENDCOUNT, SENDTYPE INTEGER RECVCOUNT, RECVTYPE INTEGER COMM, IERROR
MPI_ALLTOALL
C++:
void MPI::Comm::Alltoall(
const void* sendbuf, int sendcount, const MPI::Datatype& sendtype,
void* recvbuf, int recvcount,
const MPI::Datatype& recvtype) const = 0
MPI_ALLTOALLV
● MPI_ALLTOALL com tamanho de mensagem variável; ● Quase o mais genérico possível; ● Normalmente, o tamanho das mensagens é enviado primeiro, com um MPI_ALLTOALL; ● Não há opção “in place”;MPI_ALLTOALLV
C:
int MPI_Alltoallv(
void* sendbuf, int *sendcounts, int *sdispls,
MPI_Datatype sendtype,
void* recvbuf, int *recvcounts, int *rdispls,
MPI_Datatype recvtype, MPI_Comm comm)
MPI_ALLTOALLV
Fortran:MPI_ALLTOALLV(
SENDBUF, SENDCOUNTS, SDISPLS, SENDTYPE,
RECVBUF, RECVCOUNTS, RDISPLS, RECVTYPE, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER SENDCOUNTS(*), SDISPLS(*), SENDTYPE INTEGER RECVCOUNTS(*), RDISPLS(*), RECVTYPE INTEGER COMM, IERROR
MPI_ALLTOALLV
C++:void MPI::Comm::Alltoallv( const void* sendbuf,
const int sendcounts[], const int sdispls[],
const MPI::Datatype& sendtype,
void* recvbuf, const int recvcounts[], const int rdispls[],
const MPI::Datatype& recvtype) const = 0
MPI_REDUCE
● Acumulação global; ● Combina elementos de cada processo com uma operação binária fornecida pelo usuário; ● Deve ser chamada por todos os membros do grupo com argumentos compatíveis; ● Pode ser aplicada a vetores de elementos, e a operação é aplicada aos elementos correspondentes; ● Pode usar comunicação “in place”;MPI_REDUCE
● As operações são sempre associativas; ● As operações prédefinidas são também comutativas; ● O usuário pode definir operações, que não precisam ser comutativas; – Neste caso, a ordem de avaliação é a ordem dos ranks dos processos; ● Curiosidade: operações de ponto flutuante não são estritamente associativas e comutativas!MPI_REDUCE - Operações
● MPI_MAX ● MPI_MIN ● MPI_SUM ● MPI_PROD ● MPI_LAND ● MPI_BAND ● MPI_LOR ● MPI_BOR ● MPI_LXOR ● MPI_BXOR ● MPI_MAXLOC ● MPI_MINLOCMPI_REDUCE
C:
int MPI_Reduce(
void* sendbuf, void* recvbuf,
int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)
MPI_REDUCE
Fortran:
MPI_REDUCE(
SENDBUF, RECVBUF, COUNT, DATATYPE, OP,
ROOT, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*) INTEGER COUNT, DATATYPE, OP INTEGER ROOT, COMM, IERROR
MPI_REDUCE
C++:
void MPI::Comm::Reduce(
const void* sendbuf, void* recvbuf, int count,
const MPI::Datatype& datatype, const MPI::Op& op, int root)
MPI_{MIN,MAX}LOC
● Calculam o mínimo (máximo) e um índice associado; ● Normalmente, a posição em um vetor, ou o rank do processo; ● Usa tipos de dados especiais, os índices são sempre inteiros e os elementos podem ter tipos variados;MPI_OP_CREATE
● Usuário pode criar funções de redução; ● Deve ser associativa, e pode ou não ser comutativa; – Neste caso, a ordem aplicada é a ordem padrão, crescente no rank; ● Esta função associa uma função escrita pelo programador a uma variável que pode ser usada como operação em MPI_REDUCE;MPI_OP_CREATE
C: int MPI_Op_create( MPI_User_function *function, int commute, MPI_Op *op)MPI_OP_CREATE
Fortran: MPI_OP_CREATE(FUNCTION, COMMUTE, OP, IERROR) EXTERNAL FUNCTION LOGICAL COMMUTEMPI_OP_CREATE
C++:
void MPI::Op::Init(
MPI::User_function* function, bool commute)
MPI_OP_CREATE
● Claro que a função tem que obedecer a um protótipo; ● Os argumentos desta função são vetores, o que permite eficiência na aplicação; ● Não é permitido chamar funções de comunicação do MPI dentro desta função! ● É permitido chamar MPI_ABORT em caso de erros;MPI_OP_CREATE – Protótipo
C:
typedef void MPI_User_function( void *invec, void *inoutvec, int *len,
MPI_OP_CREATE – Protótipo
Fortran:
SUBROUTINE
USER_FUNCTION(INVEC, INOUTVEC, LEN, TYPE)
<type> INVEC(LEN), INOUTVEC(LEN) INTEGER LEN, TYPE
MPI_OP_CREATE – Protótipo
C++:
typedef void MPI::User_function( const void* invec,
void *inoutvec, int len, const Datatype& datatype);
MPI_ALLREDUCE
● MPI_REDUCE, com resultado em todos os processadores; ● Normalmente é exatamente o que interessa; ● Mesmas características e facilidades que MPI_REDUCE; ● Suporta comunicação “in place”MPI_ALLREDUCE
C:
int MPI_Allreduce(
void* sendbuf, void* recvbuf, int count,
MPI_Datatype datatype, MPI_Op op,
MPI_ALLREDUCE
Fortran: MPI_ALLREDUCE( SENDBUF, RECVBUF, COUNT, DATATYPE, OP, COMM, IERROR)<type> SENDBUF(*), RECVBUF(*) INTEGER COUNT, DATATYPE
MPI_ALLREDUCE
C++:
void MPI::Comm::Allreduce( const void* sendbuf, void* recvbuf,
int count,
const MPI::Datatype& datatype, const MPI::Op& op) const = 0
MPI_REDUCE_SCATTER
● Executa a redução em um vetor, depois envia partes de tamanho diferente deste vetor para os processos do grupo; ● Equivalente a MPI_REDUCE (com um vetor) seguido de MPI_SCATTERV; ● Suporta comunicação “in place”;MPI_REDUCE_SCATTER
C: int MPI_Reduce_scatter( void* sendbuf, void* recvbuf, int *recvcounts, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)MPI_REDUCE_SCATTER
Fortran:
MPI_REDUCE_SCATTER(
SENDBUF, RECVBUF, RECVCOUNTS, DATATYPE, OP, COMM,
IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER RECVCOUNTS(*), DATATYPE INTEGER OP, COMM, IERROR
MPI_REDUCE_SCATTER
C++:
void MPI::Comm::Reduce_scatter( const void* sendbuf,
void* recvbuf, int recvcounts[], const MPI::Datatype& datatype, const MPI::Op& op) const = 0
MPI_SCAN
● Operação de Prefixo Inclusivo: o processador i obtém o resultado da redução dos processos 0 até i; ● Todo o resto como MPI_REDUCE; ● Há opção “in place”;MPI_SCAN
C: int MPI_Scan( void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm )MPI_SCAN
Fortran:
MPI_SCAN(
SENDBUF, RECVBUF, COUNT, DATATYPE,
OP, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*) INTEGER COUNT, DATATYPE
MPI_SCAN
C++:
void MPI::Intracomm::Scan( const void* sendbuf, void* recvbuf,
int count,
const MPI::Datatype& datatype, const MPI::Op& op) const
MPI_EXSCAN
● Operação de Prefixo Exclusivo: o processador i recebe o resultado da redução dos processos de 0 a i1; ● No processo 0 o resultado é indefinido; ● Todo o resto análogo a MPI_REDUCE; ● Não há opção “in place”;MPI_EXSCAN
C: int MPI_Exscan( void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm )MPI_EXSCAN
Fortran:
MPI_EXSCAN(
SENDBUF, RECVBUF, COUNT, DATATYPE,
OP, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*) INTEGER COUNT, DATATYPE
MPI_EXSCAN
C++:
void MPI::Intracomm::Exscan( const void* sendbuf,
void* recvbuf, int count,
const MPI::Datatype& datatype, const MPI::Op& op) const