| | | |

Computação Paralela com C++

2 Multiprocessamento (MP)

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+(π2-1)e-tsen(π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)=e-tsen(π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

utt-uxx=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
| | | |