| | | |

Computação Paralela com C++

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

2.6 Seções

Seções paralelas são usadas para distribuir o trabalho de blocos de códigos entre os threads. Cada thread executa uma seção de código diferente. Seções podem ser abertas dentro de regiões paralelas usando a seguinte sintaxe:

1#pragma omp sections [clause[ [,] clause] ... ] new-line
2{
3[#pragma omp section new-line]
4 structured-block
5[#pragma omp section new-line
6 structured-block]
7...
8}

Ao final da região omp sections, o master thread espera que todos os outros threads terminem suas seções antes de continuar a execução do código. Esta sincronização implícita pode ser evitada com o uso da clausula nowait. Cada bloco estruturado deve ser precedido pela diretiva omp section, com exceção do primeiro bloco, para o qual a diretiva omp section é opcional. Por exemplo, estudemos o seguinte código.

1#pragma omp parallel num_threads(4)
2{
3 int tid = omp_get_thread_num();
4
5 #pragma omp sections
6 {
7 // bloco 1
8 printf("Bloco 1: tid = %d\n", tid);
9
10 #pragma omp section
11 {
12 // bloco 2
13 printf("Bloco 2: tid = %d\n", tid);
14 }
15
16 #pragma omp section
17 {
18 // bloco 3
19 printf("Bloco 3: tid = %d\n", tid);
20 }
21 }
22}
Bloco 2: tid = 1
Bloco 1: tid = 2
Bloco 3: tid = 0

Neste código, a primeira thread disponível executa o Bloco 1, a segunda thread executa o Bloco 2 e a terceira thread executa o Bloco 3. A quarta thread não executa nada, pois não há mais blocos de código disponíveis. A execução dos blocos de código é feita em paralelo, ou seja, as threads podem executar os blocos de código ao mesmo tempo.

2.6.1 single

A diretiva omp single é usada para indicar que um bloco de código deve ser executado por apenas um thread. Ela pode ser usada dentro de regiões paralelas empregando a seguinte sintaxe:

1#pragma omp single [clause[ [,] clause] ... ] new-line
2structured-block

A primeira thread que chegar à diretiva omp single executa o bloco de código (não necessariamente a master thread). Ao final da região single ocorre uma sincronização implícita entre todas as threads. A sincronização pode ser evitada com o uso da cláusula nowait.

Exemplo 2.6.1.

O seguinte código é uma implementação OpenMP/C++ do método de Jacobi para resolver um sistema linear Ax=b. Em cada iteração, a distribuição do laço nas linhas é feita com a diretiva omp for. A cada iteração, as threads devem esperar até que todas tenham terminado a iteração anterior. A verificação do critério de parada é feita com a diretiva omp single. Verifique!

1Eigen::VectorXd jacobi(const Eigen::MatrixXd &A,
2 const Eigen::VectorXd &b)
3{
4 // params
5 int itmax = 1000;
6 double tol = 1e-8;
7
8 int n = A.rows();
9 Eigen::VectorXd x = Eigen::VectorXd::Zero(n);
10
11 double r_norm = 0.0;
12
13 #pragma omp parallel num_threads(3)
14 {
15 // iteração de Jacobi
16 for (int it = 0; it < itmax; ++it)
17 {
18 #pragma omp for
19 for (int i = start; i < end; ++i)
20 {
21 double sum = 0.0;
22 for (int j = 0; j < n; ++j)
23 {
24 if (i != j)
25 sum += A(i, j) * x(j);
26 }
27 x(i) = (b(i) - sum) / A(i, i);
28 }
29
30 // verificação
31 #pragma omp single
32 {
33 r_norm = (A * x - b).norm();
34 if (r_norm < tol)
35 {
36 std::cout << "Converged in "
37 << it << " iterations."
38 << std::endl;
39 }
40 }
41
42 if (r_norm < tol)
43 break;
44 }
45 }
46
47 return x;
48
49}

2.6.2 master

A diretiva omp master é usada para indicar que um bloco de código deve ser executado apenas pela master thread. Ela pode ser usada dentro de regiões paralelas empregando a seguinte sintaxe:

1#pragma omp master new-line
2 structured-block

Não há sincronização implícita no início ou final dessa região.

Exemplo 2.6.2.(Aplicação: Equação do calor)

O seguinte código é uma implementação OpenMP/C++ do método de Euler explícito para a solução de diferenças finitas da equação do calor

ut=uxx+(π21)etsen(πx) (2.6)

no domínio t,x[0,1]2, com condição inicial

u(0,x)=sen(πx) (2.7)

e condições de contorno de Dirichlet homogêneas. O domínio é discretizado em nt passos no tempo e nx passos no espaço.

1#include <iostream>
2#include <omp.h>
3#include <cmath>
4#include <fstream>
5#include <eigen3/Eigen/Dense>
6
7// source
8double f(double t, double x)
9{
10 return (M_PI_2 - 1.0) * exp(-t) * sin(M_PI * x);
11}
12
13int main()
14{
15 // time
16 int nt = 10000;
17 int nt_write = 1000;
18 double dt = 1.0/nt;
19
20
21 // space
22 int nx = 10;
23 double dx = 1.0/nx;
24 Eigen::ArrayXd x(nx+1);
25
26 // solutions
27 Eigen::ArrayXd u0(nx+1);
28 Eigen::ArrayXd u = Eigen::ArrayXd::Zero(nx+1);
29
30 #pragma omp parallel
31 {
32 // initialize x and u0
33 #pragma omp for
34 for (int i = 0; i <= nx; i++)
35 {
36 x(i) = i * dx;
37 u0(i) = sin(M_PI * x(i));
38 }
39
40 // store initial condition
41 #pragma omp master
42 {
43 // write u0 to file
44 std::ofstream file("u_0.bin", std::ios::binary);
45 file.write(reinterpret_cast<const char*>(u0.data()), u0.size() * sizeof(double));
46 file.close();
47 std::cout << "u_0.bin created" << std::endl;
48 }
49
50 // time loop
51 for (int k = 0; k < nt; k++)
52 {
53 double t = (k+1) * dt;
54
55 // space loop
56 #pragma omp for
57 for (int i = 1; i < nx; i++)
58 {
59 u(i) = u0(i) + dt * (
60 (u0(i + 1) - 2 * u0(i) + u0(i - 1)) / (dx * dx) + \
61 f(t, x[i]));
62 }
63
64 // write u to file
65 #pragma omp master
66 {
67 if ((k+1) % 1000 == 0)
68 {
69 std::ofstream file("u_" + std::to_string(k+1) + ".bin", std::ios::binary);
70 file.write(reinterpret_cast<const char*>(u.data()), u.size() * sizeof(double));
71 file.close();
72 std::cout << "t = " << t << ": "
73 << "u_" << k+1 << ".bin created" << std::endl;
74 }
75 }
76
77 // update u0
78 #pragma omp for
79 for (int i = 0; i <= nx; i++)
80 {
81 u0(i) = u(i);
82 }
83 }
84 }
85
86 return 0;
87}

Verifique! A solução analítica do problema de calor acima é

u(t,x)=etsen(πx). (2.8)

2.6.3 Exercícios

E. 2.6.1.

Desenvolva um código OpenMP/C++ para computar a soma de dois vetores de n elementos. Use a diretiva omp sections para dividir o trabalho entre duas threads. Use a diretiva omp single para imprimir o resultado.

E. 2.6.2.

Desenvolva um código OpenMP/C++ para computar a solução de diferenças finitas do seguinte problema da onda

uttuxx=0, 0<t2, 0<x<1, (2.9)
u(0,x)=0, 0x1, (2.10)
ut(0,x)=πsen(πx), 0x1, (2.11)
u(t,0)=u(t,1)=0, 0t2. (2.12)

Use a diretiva omp master para que a master thread controle o armazenamento, em arquivo, de soluções discretas no tempo. Dica: a solução analítica deste problema é

u(t,x)=sen(πx)sen(πx). (2.13)

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.6 Seções

Seções paralelas são usadas para distribuir o trabalho de blocos de códigos entre os threads. Cada thread executa uma seção de código diferente. Seções podem ser abertas dentro de regiões paralelas usando a seguinte sintaxe:

1#pragma omp sections [clause[ [,] clause] ... ] new-line
2{
3[#pragma omp section new-line]
4 structured-block
5[#pragma omp section new-line
6 structured-block]
7...
8}

Ao final da região omp sections, o master thread espera que todos os outros threads terminem suas seções antes de continuar a execução do código. Esta sincronização implícita pode ser evitada com o uso da clausula nowait. Cada bloco estruturado deve ser precedido pela diretiva omp section, com exceção do primeiro bloco, para o qual a diretiva omp section é opcional. Por exemplo, estudemos o seguinte código.

1#pragma omp parallel num_threads(4)
2{
3 int tid = omp_get_thread_num();
4
5 #pragma omp sections
6 {
7 // bloco 1
8 printf("Bloco 1: tid = %d\n", tid);
9
10 #pragma omp section
11 {
12 // bloco 2
13 printf("Bloco 2: tid = %d\n", tid);
14 }
15
16 #pragma omp section
17 {
18 // bloco 3
19 printf("Bloco 3: tid = %d\n", tid);
20 }
21 }
22}
Bloco 2: tid = 1
Bloco 1: tid = 2
Bloco 3: tid = 0

Neste código, a primeira thread disponível executa o Bloco 1, a segunda thread executa o Bloco 2 e a terceira thread executa o Bloco 3. A quarta thread não executa nada, pois não há mais blocos de código disponíveis. A execução dos blocos de código é feita em paralelo, ou seja, as threads podem executar os blocos de código ao mesmo tempo.

2.6.1 single

A diretiva omp single é usada para indicar que um bloco de código deve ser executado por apenas um thread. Ela pode ser usada dentro de regiões paralelas empregando a seguinte sintaxe:

1#pragma omp single [clause[ [,] clause] ... ] new-line
2structured-block

A primeira thread que chegar à diretiva omp single executa o bloco de código (não necessariamente a master thread). Ao final da região single ocorre uma sincronização implícita entre todas as threads. A sincronização pode ser evitada com o uso da cláusula nowait.

Exemplo 2.6.1.

O seguinte código é uma implementação OpenMP/C++ do método de Jacobi para resolver um sistema linear Ax=b. Em cada iteração, a distribuição do laço nas linhas é feita com a diretiva omp for. A cada iteração, as threads devem esperar até que todas tenham terminado a iteração anterior. A verificação do critério de parada é feita com a diretiva omp single. Verifique!

1Eigen::VectorXd jacobi(const Eigen::MatrixXd &A,
2 const Eigen::VectorXd &b)
3{
4 // params
5 int itmax = 1000;
6 double tol = 1e-8;
7
8 int n = A.rows();
9 Eigen::VectorXd x = Eigen::VectorXd::Zero(n);
10
11 double r_norm = 0.0;
12
13 #pragma omp parallel num_threads(3)
14 {
15 // iteração de Jacobi
16 for (int it = 0; it < itmax; ++it)
17 {
18 #pragma omp for
19 for (int i = start; i < end; ++i)
20 {
21 double sum = 0.0;
22 for (int j = 0; j < n; ++j)
23 {
24 if (i != j)
25 sum += A(i, j) * x(j);
26 }
27 x(i) = (b(i) - sum) / A(i, i);
28 }
29
30 // verificação
31 #pragma omp single
32 {
33 r_norm = (A * x - b).norm();
34 if (r_norm < tol)
35 {
36 std::cout << "Converged in "
37 << it << " iterations."
38 << std::endl;
39 }
40 }
41
42 if (r_norm < tol)
43 break;
44 }
45 }
46
47 return x;
48
49}

2.6.2 master

A diretiva omp master é usada para indicar que um bloco de código deve ser executado apenas pela master thread. Ela pode ser usada dentro de regiões paralelas empregando a seguinte sintaxe:

1#pragma omp master new-line
2 structured-block

Não há sincronização implícita no início ou final dessa região.

Exemplo 2.6.2.(Aplicação: Equação do calor)

O seguinte código é uma implementação OpenMP/C++ do método de Euler explícito para a solução de diferenças finitas da equação do calor

ut=uxx+(π21)etsen(πx) (2.6)

no domínio t,x[0,1]2, com condição inicial

u(0,x)=sen(πx) (2.7)

e condições de contorno de Dirichlet homogêneas. O domínio é discretizado em nt passos no tempo e nx passos no espaço.

1#include <iostream>
2#include <omp.h>
3#include <cmath>
4#include <fstream>
5#include <eigen3/Eigen/Dense>
6
7// source
8double f(double t, double x)
9{
10 return (M_PI_2 - 1.0) * exp(-t) * sin(M_PI * x);
11}
12
13int main()
14{
15 // time
16 int nt = 10000;
17 int nt_write = 1000;
18 double dt = 1.0/nt;
19
20
21 // space
22 int nx = 10;
23 double dx = 1.0/nx;
24 Eigen::ArrayXd x(nx+1);
25
26 // solutions
27 Eigen::ArrayXd u0(nx+1);
28 Eigen::ArrayXd u = Eigen::ArrayXd::Zero(nx+1);
29
30 #pragma omp parallel
31 {
32 // initialize x and u0
33 #pragma omp for
34 for (int i = 0; i <= nx; i++)
35 {
36 x(i) = i * dx;
37 u0(i) = sin(M_PI * x(i));
38 }
39
40 // store initial condition
41 #pragma omp master
42 {
43 // write u0 to file
44 std::ofstream file("u_0.bin", std::ios::binary);
45 file.write(reinterpret_cast<const char*>(u0.data()), u0.size() * sizeof(double));
46 file.close();
47 std::cout << "u_0.bin created" << std::endl;
48 }
49
50 // time loop
51 for (int k = 0; k < nt; k++)
52 {
53 double t = (k+1) * dt;
54
55 // space loop
56 #pragma omp for
57 for (int i = 1; i < nx; i++)
58 {
59 u(i) = u0(i) + dt * (
60 (u0(i + 1) - 2 * u0(i) + u0(i - 1)) / (dx * dx) + \
61 f(t, x[i]));
62 }
63
64 // write u to file
65 #pragma omp master
66 {
67 if ((k+1) % 1000 == 0)
68 {
69 std::ofstream file("u_" + std::to_string(k+1) + ".bin", std::ios::binary);
70 file.write(reinterpret_cast<const char*>(u.data()), u.size() * sizeof(double));
71 file.close();
72 std::cout << "t = " << t << ": "
73 << "u_" << k+1 << ".bin created" << std::endl;
74 }
75 }
76
77 // update u0
78 #pragma omp for
79 for (int i = 0; i <= nx; i++)
80 {
81 u0(i) = u(i);
82 }
83 }
84 }
85
86 return 0;
87}

Verifique! A solução analítica do problema de calor acima é

u(t,x)=etsen(πx). (2.8)

2.6.3 Exercícios

E. 2.6.1.

Desenvolva um código OpenMP/C++ para computar a soma de dois vetores de n elementos. Use a diretiva omp sections para dividir o trabalho entre duas threads. Use a diretiva omp single para imprimir o resultado.

E. 2.6.2.

Desenvolva um código OpenMP/C++ para computar a solução de diferenças finitas do seguinte problema da onda

uttuxx=0, 0<t2, 0<x<1, (2.9)
u(0,x)=0, 0x1, (2.10)
ut(0,x)=πsen(πx), 0x1, (2.11)
u(t,0)=u(t,1)=0, 0t2. (2.12)

Use a diretiva omp master para que a master thread controle o armazenamento, em arquivo, de soluções discretas no tempo. Dica: a solução analítica deste problema é

u(t,x)=sen(πx)sen(πx). (2.13)

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