| | | |

Computação Paralela com C++

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

3.1 Olá, Mundo!

A computação paralela com MPI inicia-se simultaneamente com múltiplos processadores (instâncias de processamento), cada um utilizando seu próprio endereço de memória (memória distribuída). Cada processo lê e escreve em seu próprio endereço de memória privada. Observamos que o processamento já inicia-se ramificado e distribuído, sendo possível a comunicação entre os processos por instruções explícitas (instruções MPI, Message Passing Interface). A sincronização entre os processos também requer instruções específicas.

Vamos escrever nosso primeiro código MPI. O Código ola.cpp é paralelamente executado por diferentes processadores, cada processo escreve “Olá” e identifica-se.

Código 8: Ola.cpp
1#include <cstdio>
2
3// API MPI
4#include <mpi.h>
5
6int main()
7{
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 // Escreve mensagem
21 printf("Ola! Eu sou o processo %d/%d.\n",
22 world_rank, world_size);
23
24 // Finaliza o MPI
25 MPI_Finalize();
26
27 return 0;
28}

Na linha 4, o API Open MPI é incluído no código. O ambiente MPI é inicializado na linha 10 com a rotina MPI_init inicializa o ambiente MPI. Na inicialização, o comunicador MPI_COMM_WORLD é construído entre todos os processos inicializados e um identificador (rank) é atribuído a cada processo. O número total de processos é obtido com a rotina MPI_Comm_size. Cada processo é identificado por um número natural sequencial 0, 1, …, world_size-1. O identificador (rank) de um processo é obtido com a rotina MPI_Comm_rank (veja a linha 18). A rotina MPI_Finalize finaliza o ambiente MPI.

Para compilar este código, podemos digitar no terminal

$ mpic++ ola.cc

Esta instrução de compilação é análoga a

1$ g++ ola.cc -I/usr/lib/x86_64-linux-gnu/openmpi/include/openmpi
2 -I/usr/lib/x86_64-linux-gnu/openmpi/include
3 -pthread -L/usr/lib/x86_64-linux-gnu/openmpi/lib
4 -lmpi_cxx -lmpi

ou semelhante dependendo da instalação. Para ver a sua configuração, digite

1$ mpic++ ola.cc --showme

Ao compilar, um executável a.out será criado. Para executá-lo, basta digitar no terminal:

1$ mpirun -np 4 a.out

Esta instrução inicializa simultaneamente 4 cópias (-np 4, dois processos) do código ola.cpp (do executável a.out). Cada processo é executado de forma independente (em paralelo e não sincronizados).

Ao executar, devemos ver a saída do terminal como algo parecido com

Olá! Eu sou o processo 2/4.
Olá! Eu sou o processo 1/4.
Olá! Eu sou o processo 3/4.
Olá! Eu sou o processo 0/4.
Olá! Eu sou o processo 0/2.

A saída irá variar conforme o processo que primeiro enviar a mensagem para o dispositivo de saída. Execute o código várias vezes e analise as saídas!

Observação 3.1.1.(Execução com threads)

Por padrão, o MPI não permite a execução de um número de processos menor ou igual ao número de núcleos (cores) disponíveis na máquina. Para permitir a execução nas threads disponíveis, deve-se utilizar a opção --use-hwthreads-cpus na linha de comando mpirun. Verifique!

Exemplo 3.1.1.

O seguinte código pode ser executado com 2 instâncias de processamento. Cada processo recebe os números inteiros

int n = 2;
int m = 3;

Então, um dos processos computa e imprime a soma n+m e o outro o produto.

1#include <cstdio>
2
3// API MPI
4#include <mpi.h>
5
6int main(int argc, char **argv)
7{
8 // Inicializa o MPI
9 MPI_Init(NULL, NULL);
10
11 // numero total de processos
12 int world_size;
13 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
14
15 // verifica o num. de processos
16 if (world_size != 2)
17 {
18 printf("ERRO! Numero de processos "
19 "deve ser igual 2.\n");
20 int errorcode = -1;
21 MPI_Abort(MPI_COMM_WORLD, errorcode);
22 }
23
24 // ID (rank) do processo
25 int world_rank;
26 MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
27
28 int n = 2;
29 int m = 3;
30
31 if (world_rank == 0)
32 printf("n+m = %d\n", n + m);
33 else if (world_rank == 1)
34 printf("n*m = %d\n", n * m);
35
36 // Finaliza o MPI
37 MPI_Finalize();
38
39 return 0;
40}

Neste código, os processos são abortados caso o usuário tente executá-lo com um número de processos diferente de 2. Para abortar todos os processos ativos, utiliza-se a rotina MPI_Abort (consulte as linhas 16-22). O argumento de entrada errorcode é arbitrário e serve para informar a usuária de uma categoria de erros conforme a política de programação utilizada.

Observação 3.1.2.(MPI_Abort)

MPI_Abort é uma rotina de comunicação coletiva e não requer sincronização entre os processos. O primeiro processo a executá-la aborta todos os demais processos ativos. A execução do código é interrompida e o sistema operacional retorna ao terminal.

3.1.1 Exercícios

E. 3.1.1.

Faça um código C++/Open MPI para computar a soma de dois vetores de n elementos.

  1. a)

    Faça uma versão para ser executada com apenas 2 processos, onde cada um aloca e computa com uma metade do vetor.

  2. b)

    Faça uma versão para ser executada com n processos, onde cada um aloca e computa um pedaço do vetor.

E. 3.1.2.

Faça um código C++/Open MPI para computar a multiplicação de uma matriz A m×n por um vetor x de n elementos arbitrários.

  1. a)

    Faça uma versão em que a computação é feita por linhas.

  2. b)

    Faça uma versão em que a computação é feita por colunas.

E. 3.1.3.

Faça um código C++/Open MPI para computar a multiplicação de duas matrizes A m×n e B n×p.

  1. a)

    Faça uma versão em que a computação é distribuída pela linhas da matriz A.

  2. b)

    Faça uma versão em que a computação é distribuída pela colunas da matriz B.


Envie seu comentário

Aproveito para agradecer a todas/os que de forma assídua ou esporádica contribuem enviando correções, sugestões e críticas!

Opcional. Preencha seu nome para que eu possa lhe contatar.
Opcional. Preencha seu e-mail para que eu possa lhe contatar.
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.

Licença Creative Commons
Este texto é disponibilizado nos termos da Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional. Ícones e elementos gráficos podem estar sujeitos a condições adicionais.

Computação Paralela com C++

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

3.1 Olá, Mundo!

A computação paralela com MPI inicia-se simultaneamente com múltiplos processadores (instâncias de processamento), cada um utilizando seu próprio endereço de memória (memória distribuída). Cada processo lê e escreve em seu próprio endereço de memória privada. Observamos que o processamento já inicia-se ramificado e distribuído, sendo possível a comunicação entre os processos por instruções explícitas (instruções MPI, Message Passing Interface). A sincronização entre os processos também requer instruções específicas.

Vamos escrever nosso primeiro código MPI. O Código ola.cpp é paralelamente executado por diferentes processadores, cada processo escreve “Olá” e identifica-se.

Código 8: Ola.cpp
1#include <cstdio>
2
3// API MPI
4#include <mpi.h>
5
6int main()
7{
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 // Escreve mensagem
21 printf("Ola! Eu sou o processo %d/%d.\n",
22 world_rank, world_size);
23
24 // Finaliza o MPI
25 MPI_Finalize();
26
27 return 0;
28}

Na linha 4, o API Open MPI é incluído no código. O ambiente MPI é inicializado na linha 10 com a rotina MPI_init inicializa o ambiente MPI. Na inicialização, o comunicador MPI_COMM_WORLD é construído entre todos os processos inicializados e um identificador (rank) é atribuído a cada processo. O número total de processos é obtido com a rotina MPI_Comm_size. Cada processo é identificado por um número natural sequencial 0, 1, …, world_size-1. O identificador (rank) de um processo é obtido com a rotina MPI_Comm_rank (veja a linha 18). A rotina MPI_Finalize finaliza o ambiente MPI.

Para compilar este código, podemos digitar no terminal

$ mpic++ ola.cc

Esta instrução de compilação é análoga a

1$ g++ ola.cc -I/usr/lib/x86_64-linux-gnu/openmpi/include/openmpi
2 -I/usr/lib/x86_64-linux-gnu/openmpi/include
3 -pthread -L/usr/lib/x86_64-linux-gnu/openmpi/lib
4 -lmpi_cxx -lmpi

ou semelhante dependendo da instalação. Para ver a sua configuração, digite

1$ mpic++ ola.cc --showme

Ao compilar, um executável a.out será criado. Para executá-lo, basta digitar no terminal:

1$ mpirun -np 4 a.out

Esta instrução inicializa simultaneamente 4 cópias (-np 4, dois processos) do código ola.cpp (do executável a.out). Cada processo é executado de forma independente (em paralelo e não sincronizados).

Ao executar, devemos ver a saída do terminal como algo parecido com

Olá! Eu sou o processo 2/4.
Olá! Eu sou o processo 1/4.
Olá! Eu sou o processo 3/4.
Olá! Eu sou o processo 0/4.
Olá! Eu sou o processo 0/2.

A saída irá variar conforme o processo que primeiro enviar a mensagem para o dispositivo de saída. Execute o código várias vezes e analise as saídas!

Observação 3.1.1.(Execução com threads)

Por padrão, o MPI não permite a execução de um número de processos menor ou igual ao número de núcleos (cores) disponíveis na máquina. Para permitir a execução nas threads disponíveis, deve-se utilizar a opção --use-hwthreads-cpus na linha de comando mpirun. Verifique!

Exemplo 3.1.1.

O seguinte código pode ser executado com 2 instâncias de processamento. Cada processo recebe os números inteiros

int n = 2;
int m = 3;

Então, um dos processos computa e imprime a soma n+m e o outro o produto.

1#include <cstdio>
2
3// API MPI
4#include <mpi.h>
5
6int main(int argc, char **argv)
7{
8 // Inicializa o MPI
9 MPI_Init(NULL, NULL);
10
11 // numero total de processos
12 int world_size;
13 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
14
15 // verifica o num. de processos
16 if (world_size != 2)
17 {
18 printf("ERRO! Numero de processos "
19 "deve ser igual 2.\n");
20 int errorcode = -1;
21 MPI_Abort(MPI_COMM_WORLD, errorcode);
22 }
23
24 // ID (rank) do processo
25 int world_rank;
26 MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
27
28 int n = 2;
29 int m = 3;
30
31 if (world_rank == 0)
32 printf("n+m = %d\n", n + m);
33 else if (world_rank == 1)
34 printf("n*m = %d\n", n * m);
35
36 // Finaliza o MPI
37 MPI_Finalize();
38
39 return 0;
40}

Neste código, os processos são abortados caso o usuário tente executá-lo com um número de processos diferente de 2. Para abortar todos os processos ativos, utiliza-se a rotina MPI_Abort (consulte as linhas 16-22). O argumento de entrada errorcode é arbitrário e serve para informar a usuária de uma categoria de erros conforme a política de programação utilizada.

Observação 3.1.2.(MPI_Abort)

MPI_Abort é uma rotina de comunicação coletiva e não requer sincronização entre os processos. O primeiro processo a executá-la aborta todos os demais processos ativos. A execução do código é interrompida e o sistema operacional retorna ao terminal.

3.1.1 Exercícios

E. 3.1.1.

Faça um código C++/Open MPI para computar a soma de dois vetores de n elementos.

  1. a)

    Faça uma versão para ser executada com apenas 2 processos, onde cada um aloca e computa com uma metade do vetor.

  2. b)

    Faça uma versão para ser executada com n processos, onde cada um aloca e computa um pedaço do vetor.

E. 3.1.2.

Faça um código C++/Open MPI para computar a multiplicação de uma matriz A m×n por um vetor x de n elementos arbitrários.

  1. a)

    Faça uma versão em que a computação é feita por linhas.

  2. b)

    Faça uma versão em que a computação é feita por colunas.

E. 3.1.3.

Faça um código C++/Open MPI para computar a multiplicação de duas matrizes A m×n e B n×p.

  1. a)

    Faça uma versão em que a computação é distribuída pela linhas da matriz A.

  2. b)

    Faça uma versão em que a computação é distribuída pela colunas da matriz B.


Envie seu comentário

Aproveito para agradecer a todas/os que de forma assídua ou esporádica contribuem enviando correções, sugestões e críticas!

Opcional. Preencha seu nome para que eu possa lhe contatar.
Opcional. Preencha seu e-mail para que eu possa lhe contatar.
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.

Licença Creative Commons
Este texto é disponibilizado nos termos da Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional. Ícones e elementos gráficos podem estar sujeitos a condições adicionais.

Pedro H A Konzen
| | | |