| | | | |

3.4 Reduções

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

Em revisão

Reduções são rotinas que reduzem um conjunto de dados em um conjunto menor de dados. Um exemplo de redução é a rotina de calcular o traço de uma matriz quadrada. Aqui, vamos apresentar algumas soluções MPI de rotinas de redução.

A rotina MPI_Reduce, permite a redução de dados distribuídos em um processo. Sua sintaxe é a seguinte

int MPI_Reduce(
  const void *sendbuf,
  void *recvbuf,
  int count,
  MPI_Datatype datatype,
  MPI_Op op,
  int root,
  MPI_Comm comm)

O argumento sendbuf aponta para o endereço de memória do dado a ser enviado por cada processo, enquando que o argumento recvbuf aponta para o endereço de memória onde o resultado da redução será alocada no processo root. O argumento count é o número de dados enviados por cada processo e datatype é o tipo de dado a ser enviado (o qual deve ser igual ao tipo do resultado da redução). O argumento comm é o comunicador entre os processos envolvidos na redução. A operação de redução é definida pelo argumento op e pode ser um dos seguintes

    MPI_MAX             maximum
    MPI_MIN             minimum
    MPI_SUM             sum
    MPI_PROD            product
    MPI_LAND            logical and
    MPI_BAND            bit-wise and
    MPI_LOR             logical or
    MPI_BOR             bit-wise or
    MPI_LXOR            logical xor
    MPI_BXOR            bit-wise xor
    MPI_MAXLOC          max value and location
    MPI_MINLOC          min value and location
Exemplo 3.4.1.

No seguinte código reduce.cc, cada processo aloca um número randômico na variável x. Em seguida, o processo 0 recebe o máximo entre os números alocados em todos os processos (inclusive no processo 0).

Código: reduce.cc
1#include <stdio.h>
2#include <stdlib.h>
3
4// API MPI
5#include <mpi.h>
6
7int main(int argc, char** argv) {
8
9  // Inicializa o MPI
10  MPI_Init(NULL, NULL);
11
12  // numero total de processos
13  int world_size;
14  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
15
16  // ID (rank) do processo
17  int world_rank;
18  MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
19
20  // cronometro
21  time_t init = time (NULL);
22
23  // semente do gerador randomico
24  srand (init + world_rank);
25
26  double x = double (rand ()) / RAND_MAX;
27
28  printf ("%d: %f\n",
29          world_rank, x);
30
31  double y;
32  MPI_Reduce (&x, &y, 1, MPI_DOUBLE,
33              MPI_MAX, 0, MPI_COMM_WORLD);
34
35  if (world_rank == 0)
36    printf ("Max. entre os numeros = %f\n",
37            y);
38  // Finaliza o MPI
39  MPI_Finalize();
40
41  return 0;
42}

Segue um teste de rodagem:

$ mpic++ reduce.cc
$ mpirun -np 3 a.out
0: 0.417425
1: 0.776647
2: 0.633021
Máx. entre os números = 0.776647

Verifique!

No caso de uma array de dados, a operação de redução será feita para cada componente. Veja o próximo exemplo.

Exemplo 3.4.2.

No reduce2.cc, cada processo aloca um vetor com dois números randômicos. Em seguida, o processo 0 recebe o vetor resultante da soma dos vetores alocados em cada processo (inclusive no processo 0).

Código: reduce2.cc
1#include <stdio.h>
2#include <stdlib.h>
3
4// API MPI
5#include <mpi.h>
6
7int main(int argc, char** argv) {
8
9  // Inicializa o MPI
10  MPI_Init(NULL, NULL);
11
12  // numero total de processos
13  int world_size;
14  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
15
16  // ID (rank) do processo
17  int world_rank;
18  MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
19
20  // cronometro
21  time_t init = time (NULL);
22
23  // semente do gerador randomico
24  srand (init + world_rank);
25
26  double x[2] = {double (rand ()) / RAND_MAX,
27                 double (rand ()) / RAND_MAX};
28
29  printf ("%d: %f %f\n",
30          world_rank, x[0], x[1]);
31
32  double y[2];
33  MPI_Reduce (&x, &y, 2, MPI_DOUBLE,
34              MPI_SUM, 0, MPI_COMM_WORLD);
35
36  if (world_rank == 0)
37    printf ("Vetor soma = %f %f\n",
38            y[0], y[1]);
39
40  // Finaliza o MPI
41  MPI_Finalize();
42
43  return 0;
44}

Segue um teste de rodagem:

$ mpic++ reduce2.cc
$ mpirun -np 3 a.out
0: 0.193702 0.035334
Vetor soma = 0.656458 0.843728
1: 0.051281 0.447279
2: 0.411475 0.361114

Verifique!

Observação 3.4.1.

A rotina MPI_Allreduce executa uma redução de dados e o resultado é alocada em todos os processos. Sua sintaxe é similar a rotina MPI_Reduce, com exceção do argumento root, o qual não é necessário nessa rotina. Verifique!

Observação 3.4.2.

As rotinas MPI_Ireduce e MPI_Iallreduce são versões assíncronas das rotinas MPI_Reduce e MPI_Allreduce, respectivamente.

3.4.1 Exercícios

Em revisão

E. 3.4.1.

Faça um código MPI em cada processo aloca um número randômico na variável x. Então, o processo 0 recebe o mínimo entre os números alocados em cada processo, inclusive nele mesmo.

E. 3.4.2.

Faça um código MPI em cada processo aloca um número randômico na variável x. Então, o processo 0 recebe o produtório entre os números alocados em cada processo, inclusive nele mesmo.

E. 3.4.3.

Faça um código MPI para computar a soma dos termos de um vetor de n números randômicos, com nnp, sendo np o número de processos. Use a rotina MPI_Reduce e certifique-se de que cada processo aloque somente os dados necessários, otimizando o uso de memória computacional.

E. 3.4.4.

Faça um código MPI para computar a norma do máximo de um vetor de n números randômicos, com nnp, sendo np o número de processos. Use a rotina MPI_Reduce e certifique-se de que cada processo aloque somente os dados necessários, otimizando o uso de memória computacional.

E. 3.4.5.

Faça um código MPI para computar a norma L2 de um vetor de n números randômicos, com nnp, sendo np o número de processos. Use a rotina MPI_Allreduce de forma que cada processo contenha a norma computada ao final do código.

E. 3.4.6.

Faça um código MPI para computar

erf(x)=2π0xet2𝑑t (3.14)

usando a regra composta do ponto médio. Use a rotina MPI_Reduce.


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!