| | | |

Computação Paralela com C++

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

2.1 Olá, Mundo!

A computação paralela com MP inicia-se por uma instância de processamento master thread. Todas as instâncias de processamento disponíveis (threads) leem e escrevem variáveis compartilhadas. A ramificação (fork) do processo entre os threads disponíveis é feita por instrução explícita no início de uma região paralela do código. Ao final da região paralela, todos os threads sincronizam-se e o processo segue apenas com o master thread. Veja a Figura 2.1.

Refer to caption
Figura 2.1: Fluxograma de um processo MP.

Vamos escrever nosso primeiro código MP.

Código 1: ola.cpp
1#include <stdio.h>
2
3// OpenMP API
4#include <omp.h>
5
6int main(int argc, char *argv[]) {
7
8 // região paralela
9 #pragma omp parallel
10 {
11 // therad id
12 int id = omp_get_thread_num();
13
14 printf("Thread %d, olá!\n", id);
15 }
16
17 return 0;
18}

Na linha 4, a API OpenMP é incluída no código. A região paralela vale dentro do escopo iniciado pela instrução

# pragma omp parallel

i.e., entre as linhas 9 e 15. Em paralelo, cada thread registra seu número de identificação na variável inteira id (linha 12). Na linha 14, escrevem a saudação, identificando-se.

O código pode ser compilado com

$ g++ -fopenmp ola.cc

Ao compilar, um executável a.out será criado. Ao executar, devemos ver a saída do terminal parecida com

1Thread 0, olá!
2Thread 3, olá!
3Thread 1, olá!
4Thread 2, olá!

A saída irá depender do número de threads disponíveis na máquina e a ordem dos threads pode variar a cada execução. Execute o código várias vezes e verifique!

Observação 2.1.1.(Variáveis privadas e compartilhadas)

As variáveis declaradas dentro de uma região paralela são privadas de cada thread. As variáveis declaradas fora de uma região paralela são compartilhadas, sendo acessíveis por todos os threads.

Observação 2.1.2.(Número de threads)

Por padrão, o número de threads disponíveis é o número de núcleos disponíveis na máquina. Alternativamente, o número de threads a serem disponibilizados pode ser alterado com o método omp_set_num_threads(int n). Analogamente, o método omp_get_num_threads() retorna o número total de threads disponibilizados.

Exemplo 2.1.1.

O seguinte código computa a soma entre dois vetores de n números randômicos em ponto flutuante. Para a distribuição da tarefa, os vetores são divididos em pedaços (chunks) de tamanho n/np, onde np é o número de threads disponíveis. Cada thread computa a soma dos elementos do seu respectivo pedaço.

Código 2: ’ExAddArrays.cpp’
1#include <iostream>
2// Matrix computation library
3#include <eigen3/Eigen/Eigen>
4// OpenMP API
5#include <omp.h>
6
7int main()
8{
9 // vetores
10 int n = 5;
11 Eigen::VectorXd u =
12 Eigen::VectorXd::Random(n);
13 Eigen::VectorXd v =
14 Eigen::VectorXd::Random(n);
15 Eigen::VectorXd w(n);
16
17 // num threads disponibilizadas
18 omp_set_num_threads(2);
19
20 // região paralela
21 #pragma omp parallel
22 {
23 int nthreads = omp_get_num_threads();
24 int thread_id = omp_get_thread_num();
25
26 // thread chunk
27 int chunk_size = n / nthreads;
28 int start = thread_id * chunk_size;
29 int end = (thread_id + 1) * chunk_size;
30 if (thread_id == nthreads - 1) {
31 end = n;
32 }
33
34 // somas parciais dos vetores
35 for (int i = start; i < end; i++) {
36 w(i) = u(i) + v(i);
37 }
38 }
39
40 // print
41 std::cout << "u = " << u.transpose() << std::endl;
42 std::cout << "v = " << v.transpose() << std::endl;
43 std::cout << "w = " << w.transpose() << std::endl;
44
45 return 0;
46}
u = 0.680375 -0.211234 0.566198 0.59688 0.823295
v = -0.604897 -0.329554 0.536459 -0.444451 0.10794
w = 0.0754782 -0.540789 1.10266 0.152429 0.931235

2.1.1 Exercícios

E. 2.1.1.

Escreva um código MP para ser executado com 2 threads. A master thread deve ler dois números em ponto flutuante. Então, em paralelo, uma duas threads deve calcular a soma dos dois números e a outra thread deve calcular o produto.

Resposta 0.
1#include <stdio.h>
2
3// OpenMP API
4#include <omp.h>
5
6using namespace std;
7
8int main(int argc, char *argv[]) {
9
10 double a,b;
11 printf("Digite o primeiro número: ");
12 scanf("%lf", &a);
13
14 printf("Digite o segundo número: ");
15 scanf("%lf", &b);
16
17 // regiao paralela
18 #pragma omp parallel
19 {
20 // id do processo
21 int id = omp_get_thread_num();
22
23 if (id == 0) {
24 printf("Soma: %f\n", (a+b));
25 }
26 else if (id == 1) {
27 printf("Produto: %f\n", (a*b));
28 }
29 }
30
31 return 0;
32}
E. 2.1.2.

Faça um código OpenMP/C++ para computar:

  1. a)

    a multiplicação elemento-a-elemento de dois vetores de n elementos randômicos em ponto flutuante;

  2. b)

    a divisão elemento-a-elemento de dois vetores de n elementos randômicos em ponto flutuante;

E. 2.1.3.

Faça um código MP para ser executado com 2 threads. O master thread deve ler dois números a e b não nulos em ponto flutuante. Em paralelo, um dos thread deve computar ab e o outro deve computar a/b. Por fim, o master thread deve escrever (ab)+(a/b).

E. 2.1.4.

Escreva um código MP para computar a multiplicação de uma matriz n×n com um vetor de n elementos. Inicialize todos os elementos com números randômicos em ponto flutuante. Ainda, o código deve ser escrito para um número arbitrário m1 de instâncias de processamento. Por fim, compare o desempenho do código MP com uma versão serial do código.

E. 2.1.5.

Escreva um código MP para computar o produto de uma matriz n×m com uma matriz de m×n elementos, com nm. Inicialize todos os elementos com números randômicos em ponto flutuante. Ainda, o código deve ser escrito para um número arbitrário m>1 de instâncias de processamento. Por fim, compare o desempenho do código MP com uma versão serial do código.


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!

2.1 Olá, Mundo!

A computação paralela com MP inicia-se por uma instância de processamento master thread. Todas as instâncias de processamento disponíveis (threads) leem e escrevem variáveis compartilhadas. A ramificação (fork) do processo entre os threads disponíveis é feita por instrução explícita no início de uma região paralela do código. Ao final da região paralela, todos os threads sincronizam-se e o processo segue apenas com o master thread. Veja a Figura 2.1.

Refer to caption
Figura 2.1: Fluxograma de um processo MP.

Vamos escrever nosso primeiro código MP.

Código 1: ola.cpp
1#include <stdio.h>
2
3// OpenMP API
4#include <omp.h>
5
6int main(int argc, char *argv[]) {
7
8 // região paralela
9 #pragma omp parallel
10 {
11 // therad id
12 int id = omp_get_thread_num();
13
14 printf("Thread %d, olá!\n", id);
15 }
16
17 return 0;
18}

Na linha 4, a API OpenMP é incluída no código. A região paralela vale dentro do escopo iniciado pela instrução

# pragma omp parallel

i.e., entre as linhas 9 e 15. Em paralelo, cada thread registra seu número de identificação na variável inteira id (linha 12). Na linha 14, escrevem a saudação, identificando-se.

O código pode ser compilado com

$ g++ -fopenmp ola.cc

Ao compilar, um executável a.out será criado. Ao executar, devemos ver a saída do terminal parecida com

1Thread 0, olá!
2Thread 3, olá!
3Thread 1, olá!
4Thread 2, olá!

A saída irá depender do número de threads disponíveis na máquina e a ordem dos threads pode variar a cada execução. Execute o código várias vezes e verifique!

Observação 2.1.1.(Variáveis privadas e compartilhadas)

As variáveis declaradas dentro de uma região paralela são privadas de cada thread. As variáveis declaradas fora de uma região paralela são compartilhadas, sendo acessíveis por todos os threads.

Observação 2.1.2.(Número de threads)

Por padrão, o número de threads disponíveis é o número de núcleos disponíveis na máquina. Alternativamente, o número de threads a serem disponibilizados pode ser alterado com o método omp_set_num_threads(int n). Analogamente, o método omp_get_num_threads() retorna o número total de threads disponibilizados.

Exemplo 2.1.1.

O seguinte código computa a soma entre dois vetores de n números randômicos em ponto flutuante. Para a distribuição da tarefa, os vetores são divididos em pedaços (chunks) de tamanho n/np, onde np é o número de threads disponíveis. Cada thread computa a soma dos elementos do seu respectivo pedaço.

Código 2: ’ExAddArrays.cpp’
1#include <iostream>
2// Matrix computation library
3#include <eigen3/Eigen/Eigen>
4// OpenMP API
5#include <omp.h>
6
7int main()
8{
9 // vetores
10 int n = 5;
11 Eigen::VectorXd u =
12 Eigen::VectorXd::Random(n);
13 Eigen::VectorXd v =
14 Eigen::VectorXd::Random(n);
15 Eigen::VectorXd w(n);
16
17 // num threads disponibilizadas
18 omp_set_num_threads(2);
19
20 // região paralela
21 #pragma omp parallel
22 {
23 int nthreads = omp_get_num_threads();
24 int thread_id = omp_get_thread_num();
25
26 // thread chunk
27 int chunk_size = n / nthreads;
28 int start = thread_id * chunk_size;
29 int end = (thread_id + 1) * chunk_size;
30 if (thread_id == nthreads - 1) {
31 end = n;
32 }
33
34 // somas parciais dos vetores
35 for (int i = start; i < end; i++) {
36 w(i) = u(i) + v(i);
37 }
38 }
39
40 // print
41 std::cout << "u = " << u.transpose() << std::endl;
42 std::cout << "v = " << v.transpose() << std::endl;
43 std::cout << "w = " << w.transpose() << std::endl;
44
45 return 0;
46}
u = 0.680375 -0.211234 0.566198 0.59688 0.823295
v = -0.604897 -0.329554 0.536459 -0.444451 0.10794
w = 0.0754782 -0.540789 1.10266 0.152429 0.931235

2.1.1 Exercícios

E. 2.1.1.

Escreva um código MP para ser executado com 2 threads. A master thread deve ler dois números em ponto flutuante. Então, em paralelo, uma duas threads deve calcular a soma dos dois números e a outra thread deve calcular o produto.

Resposta 0.
1#include <stdio.h>
2
3// OpenMP API
4#include <omp.h>
5
6using namespace std;
7
8int main(int argc, char *argv[]) {
9
10 double a,b;
11 printf("Digite o primeiro número: ");
12 scanf("%lf", &a);
13
14 printf("Digite o segundo número: ");
15 scanf("%lf", &b);
16
17 // regiao paralela
18 #pragma omp parallel
19 {
20 // id do processo
21 int id = omp_get_thread_num();
22
23 if (id == 0) {
24 printf("Soma: %f\n", (a+b));
25 }
26 else if (id == 1) {
27 printf("Produto: %f\n", (a*b));
28 }
29 }
30
31 return 0;
32}
E. 2.1.2.

Faça um código OpenMP/C++ para computar:

  1. a)

    a multiplicação elemento-a-elemento de dois vetores de n elementos randômicos em ponto flutuante;

  2. b)

    a divisão elemento-a-elemento de dois vetores de n elementos randômicos em ponto flutuante;

E. 2.1.3.

Faça um código MP para ser executado com 2 threads. O master thread deve ler dois números a e b não nulos em ponto flutuante. Em paralelo, um dos thread deve computar ab e o outro deve computar a/b. Por fim, o master thread deve escrever (ab)+(a/b).

E. 2.1.4.

Escreva um código MP para computar a multiplicação de uma matriz n×n com um vetor de n elementos. Inicialize todos os elementos com números randômicos em ponto flutuante. Ainda, o código deve ser escrito para um número arbitrário m1 de instâncias de processamento. Por fim, compare o desempenho do código MP com uma versão serial do código.

E. 2.1.5.

Escreva um código MP para computar o produto de uma matriz n×m com uma matriz de m×n elementos, com nm. Inicialize todos os elementos com números randômicos em ponto flutuante. Ainda, o código deve ser escrito para um número arbitrário m>1 de instâncias de processamento. Por fim, compare o desempenho do código MP com uma versão serial do código.


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
| | | |