Ajude a manter o site livre, gratuito e sem propagandas. Colabore!
Em revisão
Nesta seção, vamos discutir sobre rotinas de comunicações MPI coletivas. Basicamente, rotinas de sincronização, envio e recebimento de dados envolvendo múltiplas instâncias de processamento ao mesmo tempo.
Em revisão
Podemos forçar a sincronização de todos os processos em um determinado ponto do código utilizando a rotina de sincronização MPI_Barrier
int MPI_Barrier (MPI_Comm comm)
Quando um processo encontra esta rotina ele aguarda todos os demais processos. No momento em que todos os processo tiverem alcançados esta rotina, todos são liberados para seguirem com suas computações.
No Código barrier.cc
, abaixo, cada instância de processamento aguarda randomicamente até 3 segundos para alcançar a rotina de sincronização MPI_Barrier na linha 35. Em seguida, elas são liberadas juntas. Estude o código.
Vamos observar o seguinte teste de rodagem
$ mpic++ barrier.cc $ mpirun -np 2 a.out 1 chegou na barreira: 1 s. 0 chegou na barreira: 3 s. 0 saiu da barreira: 3 s. 1 saiu da barreira: 3 s.
Neste caso, o processo 1 foi o primeiro a alcançar a barreira de sincronização e permaneceu esperando aproximadamente 2 segundos até que o processo 0 alcançasse a barreira. Imediatamente após o processo 1 chegar a barreira, ambos seguiram suas computações. Rode várias vezes este código e analise as saídas!
No Código barrier.cc
acima, o gerador de números randômicos é inicializado com a semente
srand (init + world_rank);
onde, init
é o tempo colhido pela rotina time
no início do processamento (veja as linhas 22-25). Observamos que somar o identificado rank
garante que cada processo inicie o gerador randômico com uma semente diferente.
Em revisão
A rotina de transmissão de dados MPI_Bcast permite o envio de dados de um processo para todos os demais. Sua sintaxe é a seguinte
int MPI_Bcast( void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
O primeiro argumento buffer
aponta para o endereço da memória do dado a ser transmitido. O argumento count
é a quantidade de dados sucessivos que serão transmitidos (tamanho do buffer
). O tipo de dado é informado no argumento datatype
. Por fim, root
é o identificador rank
do processo que está transmitindo e comm
é o comunicador.
No seguinte Código bcast.cc
, o processo 0 inicializa a variável de ponto flutuante (linhas 22-23) e, então, transmite ela para todos os demais processos (linhas 25-26). Por fim, cada processo imprime no terminal o valor alocado na sua variável (linhas 28-29).
Vejamos o seguinte teste de rodagem
$ mpic++ bcast.cc $ mpirun -np 3 ./a.out Processo 0 x = 3.141593 Processo 1 x = 3.141593 Processo 2 x = 3.141593
Em revisão
A rotina MPI_Scatter permite que um processo faça a distribuição uniforme de pedaços sequenciais de um array de dados para todos os demais processos. Sua sintaxe é a seguinte
int MPI_Scatter( const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm)
O primeiro argumento sendbuf
aponta para o endereço de memória do array de dados a ser distribuído. O argumento sendcount
é o tamanho do pedaço e sendtype
é o tipo de dado a ser transmitido. Os argumentos recvbuf
, recvcount
e recvtype
se referem ao ponteiro para o local de memória onde o dado recebido será alocado, o tamanho do pedaço a ser recebido e o tipo de dado, respectivamente. Por fim, o argumento root
identifica o processo de origem da distribuição dos dados e comm
é o comunicador.
No Código scatter.cc
abaixo, o processo 0 aloca o vetor
(3.5) |
distribui pedaços sequenciais do vetor para cada processo no comunicador MPI_COMM_WORLD
e, então, cada processo computa a soma dos elementos recebidos.
Vejamos o seguinte teste de rodagem
$ mpic++ scatter.cc -lgsl -lgslcblas $ mpirun -np 2 ./a.out Processo 0 soma = 15.000000 Processo 1 soma = 40.000000
Neste caso, o processo 0 recebe
(3.6) |
enquanto o processo 1 recebe
(3.7) |
Note que o MPI_Scatter distribuí apenas pedaços de arrays de mesmo tamanho. Para a distribuição de pedaços de tamanhos diferentes entre os processos, pode-se usar a rotina MPI_Scatterv. Veja os exercícios resolvidos abaixo.
Em revisão
A rotina MPI_Gather, permite que um processo receba simultaneamente dados que estão distribuídos entre os demais processos. Sua sintaxe é a seguinte
int MPI_Gather( const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm)
Sua sintaxe é parecida com a da rotina MPI_Scatter. Veja lá! Aqui, root
é o identificador rank do processo receptor.
No Código gather.cc
, cada processo aloca um vetor
(3.8) |
então, o processo 0 recebe estes vetores alocando-os em um único vetor
(3.9) |
onde é o número de processos inicializados.
Vejamos o seguinte teste de rodagem
$ mpic++ gather.cc -lgsl -lgslcblas $ mpirun -np 2 ./a.out v = 1.000000 2.000000 3.000000 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 10.000000
Neste caso, o processo 0 aloca
(3.10) |
e o processo 1 aloca
(3.11) |
Então, o processo 0, recebe os dois pedaços de cada um, formando o vetor
(3.12) |
Verifique!
Para recebimento de pedaços distribuídos e de tamanhos diferentes, pode-se usar a rotina MPI_Gatherv. Veja os exercícios resolvidos abaixo.
Observamos que com a rotina MPI_Gather podemos juntar pedaços de dados distribuídos em um único processo. Analogamente, a rotina MPI_Allgather nos permite juntar os pedaços de dados distribuídos e ter uma cópia do todo em cada um dos processos. Sua sintaxe é a seguinte
int MPI_Allgather( const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm)
Note que esta rotina não contém o argumento root
, pois neste caso todos os processos receberam os dados juntados na variável recvbuf
!
Em revisão
Faça um código MPI para computar a média aritmética simples de números randômicos em ponto flutuante.
Faça um código MPI para computar o produto interno de dois vetores de elementos randômicos em ponto flutuante.
Faça um código MPI para computar a norma de um vetor de elementos randômicos em ponto flutuante.
Faça uma implementação MPI do método de Jacobi para computar a solução de um sistema . Inicialize e com números randômicos em ponto flutuante.
As informações preenchidas são enviadas por e-mail para o desenvolvedor do site e tratadas de forma privada. Consulte a Política de Uso de Dados para mais informações. Aproveito para agradecer a todas/os que de forma assídua ou esporádica contribuem enviando correções, sugestões e críticas!