| | | | |

3.2 Rotinas de comunicação ponto-a-ponto

Ajude a manter o site livre, gratuito e sem propagandas. Colabore!

Em revisão

Em computação distribuída, rotinas de comunicação entre as instâncias de processamento são utilizadas para o compartilhamento de dados. Neste capítulo, vamos discutir sobre as rotinas de comunicação ponto-a-ponto, i.e. comunicações entre uma instância de processamento com outra.

3.2.1 Envio e recebimento síncronos

Em revisão

O envio e recebimento de dados entre duas instâncias de processamento pode ser feita com as rotinas MPI_Send e MPI_Recv. A primeira é utilizada para o envio de um dado a partir de uma instância de processamento e a segunda é utilizada para o recebimento de um dado em uma instância de processamento.

A sintaxe da MPI_Send é

int MPI_Send(
  const void *buf,
  int count,
  MPI_Datatype datatype,
  int dest,
  int tag,
  MPI_Comm comm)

e a sintaxe da MPI_Recv é

int MPI_Recv(
  void *buf,
  int count,
  MPI_Datatype datatype,
  int source,
  int tag,
  MPI_Comm comm,
  MPI_Status *status)

O primeiro argumento é o ponteiro do buffer de dados. No caso do MPI_Send é o ponteiro para a posição da memória do dado a ser enviado. No caso do MPI_Recv é o ponteiro para a posição da memória do dado a ser recebido. O segundo argunto count é o número de dados sequenciais a serem enviados. O argundo datatype é o tipo de dado. O MPI suporta os seguintes tipos de dados

MPI_SHORT               short int
MPI_INT                 int
MPI_LONG                long int
MPI_LONG_LONG           long long int
MPI_UNSIGNED_CHAR       unsigned char
MPI_UNSIGNED_SHORT      unsigned short int
MPI_UNSIGNED            unsigned int
MPI_UNSIGNED_LONG       unsigned long int
MPI_UNSIGNED_LONG_LONG  unsigned long long int
MPI_FLOAT               float
MPI_DOUBLE              double
MPI_LONG_DOUBLE         long double
MPI_BYTE                char

Ainda sobre as sintaxes acima, o argumento source é o identificador rank da instância de processamento. O argunmento tag é um número arbitrário para identificar a operação de envio e recebimento. O argumento Comm especifica o comunicador (MPI_COMM_WORLD para aplicações básicas) e o último (somente para o MPI_Recv) fornece informação sobre o status do recebimento do dado.

Vamos estudar o seguinte código abaixo.

Código: sendRecv.cc
1#include <stdio.h>
2
3// API MPI
4#include <mpi.h>
5
6int main (int argc, char** argv) {
7  // Inicializa o MPI
8  MPI_Init(NULL, NULL);
9
10  // numero total de processos
11  int world_size;
12  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
13
14  // ID (rank) do processo
15  int world_rank;
16  MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
17
18  if (world_rank == 0) {
19    double x = 3.1416;
20    MPI_Send (&x, 1, MPI_DOUBLE, 1,
21              0, MPI_COMM_WORLD);
22  } else {
23    double y;
24    MPI_Recv (&y, 1, MPI_DOUBLE, 0,
25              0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
26    printf ("Processo 1 recebeu o "\
27            "numero %f do processo 0.\n", y);
28  }
29
30
31  // Finaliza o MPI
32  MPI_Finalize ();
33
34  return 0;
35}

O código acima pode rodado com pelo menos duas instâncias de processamento (veja as linhas 14-19). Nas linhas 28-29, o processo 0 envia o número 3.1416 (alocado na variável x) para o processo 1. Nas linhas 32-33, o processo 1 recebe o número enviado pelo processo 0 e o aloca na variável y.

Importante! As rotinas MPI_Send e MPI_Recv provocam a sincronização entre os processos envolvidos. Por exemplo, no código acima, no que o processo 0 atinge a rotina MPI_Send ele ficará aguardando o processo 1 receber todos os dados enviados e só, então, irá seguir adiante no código. Analogamento, no que o processo 1 atingir a rotina MPI_Recv, ele ficará aguardando o processo 0 enviar todos os dados e só, então, irá seguir adiante no código.

Envio e recebimento de array

Em revisão

As rotinas MPI_Send e MPI_Recv podem ser utilizadas para o envio e recebimento de arrays. A sintaxe é a mesma vista acima, sendo que o primeiro argumento *buf deve apontar para o início do array e o segundo argumento count corresponde ao tamanho da array.

Vamos estudar o seguinte código. Nele, o processo 0 aloca v=(0,1,2,3,4) e o processo 1 aloca w=(4,3,2,1,0). O processo 0 envia os valores v1,v2,v3 para o processo 1. Então, o processo 1 recebe estes valores e os aloca em w0,w1,w2. Desta forma, a saída impressa no terminal é

w=(1,2,3,1,0). (3.1)

Verifique!

Código: sendRecvArray.cc
1#include <stdio.h>
2
3// API MPI
4#include <mpi.h>
5
6int main (int argc, char** argv) {
7  // Inicializa o MPI
8  MPI_Init(NULL, NULL);
9
10  // numero total de processos
11  int world_size;
12  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
13
14  // ID (rank) do processo
15  int world_rank;
16  MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
17
18  if (world_rank == 0) {
19
20    int v[5];
21    for (int i=0; i<5; i++)
22      v[i] = i;
23
24    MPI_Send (&v[1], 3, MPI_INT, 1,
25              0, MPI_COMM_WORLD);
26  } else {
27    int w[5];
28    int i=0;
29    for (int j=5; j --> 0; i++)
30      w[j] = i;
31
32    MPI_Recv (&w[0], 3, MPI_INT, 0,
33              0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
34    printf ("Processo 1: w=\n");
35    for (int i=0; i<5; i++)
36      printf ("%d ", w[i]);
37    printf("\n");
38  }
39
40  // Finaliza o MPI
41  MPI_Finalize ();
42
43  return 0;
44}

3.2.2 Envio e recebimento assíncrono

Em revisão

O MPI também suporta rotinas MPI_Isend de envio e MPI_Irecv de recebimento assíncronos. Neste caso, o processo emissor envia o dado para outro processo e segue imediatamente a computação. O processo receptor deve conter uma rotina MPI_Irecv, mas também não aguarda sua conclusão para seguir a computação.

As sintaxes destas rotinas são semelhantes as das rotinas MPI_Send e MPI_Recv.

int MPI_Isend(
  const void *buf,
  int count,
  MPI_Datatype datatype,
  int dest,
  int tag, MPI_Comm comm,
  MPI_Request *request)
int MPI_Irecv(
  void *buf,
  int count,
  MPI_Datatype datatype,
  int source,
  int tag,
  MPI_Comm comm,
  MPI_Request *request)

O último argumento permite verificar os envios e recebimentos.

Vamos estudar o seguinte código.

Código: isendRecv.cc
1#include <stdio.h>
2
3// API MPI
4#include <mpi.h>
5
6int main (int argc, char** argv) {
7  // Inicializa o MPI
8  MPI_Init(NULL, NULL);
9
10  // numero total de processos
11  int world_size;
12  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
13
14  if (world_size < 2) {
15    printf ("Num. de processos deve"\
16            "maior que 2.\n");
17    int errorcode = -1;
18    MPI_Abort (MPI_COMM_WORLD, errorcode);
19  }
20
21  // ID (rank) do processo
22  int world_rank;
23  MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
24
25  // MPI_Status & MPI_Request
26  MPI_Status status;
27  MPI_Request request;
28
29  if (world_rank == 0) {
30    double x = 3.1416;
31    MPI_Isend (&x, 1, MPI_DOUBLE, 1,
32               0, MPI_COMM_WORLD, &request);
33  } else {
34    double y = 0.0;
35    MPI_Irecv (&y, 1, MPI_DOUBLE, 0,
36              0, MPI_COMM_WORLD, &request);
37    double x = y + 1.0;
38    printf ("x = %f\n", x);
39    int recvd = 0;
40    while (!recvd)
41      MPI_Test (&request, &recvd, &status);
42    x = y + 1;
43    printf ("x = %f\n", x);
44  }
45
46  // Finaliza o MPI
47  MPI_Finalize ();
48
49  return 0;
50}

Neste código, MPI_Status e MPI_Request são alocados nas linhas 26 e 27, respectivamente. O Processo 0 faz uma requisição de envio do número 3.1416 para o processo 1, não aguarda o recebimento e segue adiante. O processo 1 tem uma rotina de requisição de recebimento não assíncrona na linha 35. Neste momento, ele não necessariamente recebe o dado enviado pelo processador (isto pode ocorrer a qualquer momento mais adiante). Na linha 37, o valor de y deve ainda ser 0.0, veja a saída do código.

$ mpic++ isendRecv.cc
$ $ mpirun -np 2 ./a.out
x = 1.000000
x = 4.141600

Pode-se verificar se uma requisição de envio (ou recebimento) foi completata usando-se a rotina MPI_Test. A sua sintaxe é

int MPI_Test(
  MPI_Request *request,
  int *flag,
  MPI_Status *status)

O flag == 0 caso a requisição ainda não foi completada e flag == 1 caso a requisição foi executada.

No Código isendRecv.cc acima, as linhas de código 39-41 são utilizadas para fazê-lo aguardar até que a requisição de recebimento seja completada. Desta forma, na linha 42 o valor de y é 3.1416 (o valor enviado pelo processo 0. Verifique!

Observação 3.2.1.

No Código isendRecv.cc acima, as linhas 39-41 podem ser substituídas pela rotina MPI_Wait, a qual tem sintaxe

int MPI_Wait(
  MPI_Request *request,
  MPI_Status *status)

Verifique!

3.2.3 Exercícios

Em revisão

E. 3.2.1.

Faça um código MPI para ser executado com 2 processadores. Um processo aloca x=0 e o outro processo aloca y=1. Logo, os processos trocam os valores, de forma que ao final o processo zero tem x=1 e o processo 1 tem y=0.

E. 3.2.2.

Faça um código MPI para ser executado com 2 processadores. O processo 0 aloca um vetor de n1 elementos randômicos em ponto flutuante, envia o vetor para o processo 1. O processo 0, imprime no terminal a soma dos termos do vetor e o processo 1 imprime o produto dos termos do vetor.

E. 3.2.3.

Faça um código MPI para computar a média

1ni=0n1xi (3.2)

onde xi é um número em ponto flutuante e n1. Para a comunicação entre os processos, utilize apenas as rotinas MPI_Send e MPI_Recv.

E. 3.2.4.

Faça um código MPI para computação do produto interno entre dois vetores

x=(x0,x1,,xn), (3.3)
y=(y0,y1,,yn). (3.4)

Para a comunicação entre os processos, utilize apenas as rotinas MPI_Send e MPI_Recv. O processo 0 deve receber os resultados parciais dos demais processos e escrever na tela o valor computado do produto interno.

E. 3.2.5.

Modifique o código do exercício anterior (Exercício 3.2.4) de forma a fazer a comunicação entre os processos com as rotinas MPI_Isend e MPI_Irecv. Há vantagem em utilizar estas rotinas? Se sim, quais?


Envie seu comentário

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!