Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Seguem-se exemplos das construções definidas neste documento. Uma declaração que segue uma diretiva é composta apenas quando necessário, e uma declaração não composta é recuada em relação a uma diretiva que a precede.
A.1 Um loop simples em paralelo
O exemplo a seguir demonstra como paralelizar um loop usando a diretiva parallel for . A variável de iteração de loop é privada por padrão, portanto, não é necessário especificá-la explicitamente em uma cláusula privada.
#pragma omp parallel for
for (i=1; i<n; i++)
b[i] = (a[i] + a[i-1]) / 2.0;
A.2 Compilação condicional
Os exemplos a seguir ilustram o uso da compilação condicional usando o _OPENMP de macro OpenMP. Com a compilação OpenMP, a _OPENMP macro torna-se definida.
# ifdef _OPENMP
printf_s("Compiled by an OpenMP-compliant implementation.\n");
# endif
O operador de pré-processador definido permite que mais de uma macro seja testada em uma única diretiva.
# if defined(_OPENMP) && defined(VERBOSE)
printf_s("Compiled by an OpenMP-compliant implementation.\n");
# endif
A.3 Regiões paralelas
A diretiva paralela pode ser usada em programas paralelos de grão grosso. No exemplo a seguir, cada thread na região paralela decide em qual parte da matriz x global trabalhar, com base no número do thread:
#pragma omp parallel shared(x, npoints) private(iam, np, ipoints)
{
iam = omp_get_thread_num();
np = omp_get_num_threads();
ipoints = npoints / np;
subdomain(x, iam, ipoints);
}
A.4 A cláusula nowait
Se houver muitos loops independentes dentro de uma região paralela, você pode usar a cláusula nowait para evitar a barreira implícita no final da for diretiva, da seguinte maneira:
#pragma omp parallel
{
#pragma omp for nowait
for (i=1; i<n; i++)
b[i] = (a[i] + a[i-1]) / 2.0;
#pragma omp for nowait
for (i=0; i<m; i++)
y[i] = sqrt(z[i]);
}
A.5 A diretiva crítica
O exemplo a seguir inclui várias diretivas críticas . O exemplo ilustra um modelo de fila no qual uma tarefa é retirada e processada. Para se proteger contra muitos threads que enfileiram a mesma tarefa, a operação de enfileiramento deve estar em uma critical seção. Como as duas filas neste exemplo são independentes, elas são protegidas por critical diretivas com nomes diferentes, xaxis e yaxis.
#pragma omp parallel shared(x, y) private(x_next, y_next)
{
#pragma omp critical ( xaxis )
x_next = dequeue(x);
work(x_next);
#pragma omp critical ( yaxis )
y_next = dequeue(y);
work(y_next);
}
A.6 A última cláusula privada
A execução correta às vezes depende do valor que a última iteração de um loop atribui a uma variável. Tais programas devem listar todas essas variáveis como argumentos para uma cláusula lastprivate para que os valores das variáveis sejam os mesmos de quando o loop é executado sequencialmente.
#pragma omp parallel
{
#pragma omp for lastprivate(i)
for (i=0; i<n-1; i++)
a[i] = b[i] + b[i+1];
}
a[i]=b[i];
No exemplo anterior, o valor de i no final da região paralela será igual n-1a , como no caso sequencial.
A.7 A cláusula de redução
O exemplo a seguir demonstra a cláusula de redução :
#pragma omp parallel for private(i) shared(x, y, n) \
reduction(+: a, b)
for (i=0; i<n; i++) {
a = a + x[i];
b = b + y[i];
}
A.8 Secções paralelas
No exemplo a seguir (para a seção 2.4.2), as funções xaxis, yaxis e zaxis podem ser executadas simultaneamente. A primeira section diretiva é facultativa. Todas as section diretivas precisam aparecer na extensão lexical do parallel sections constructo.
#pragma omp parallel sections
{
#pragma omp section
xaxis();
#pragma omp section
yaxis();
#pragma omp section
zaxis();
}
A.9 Diretivas únicas
O exemplo a seguir demonstra a diretiva única . No exemplo, apenas um thread (geralmente o primeiro thread que encontra a single diretiva) imprime a mensagem de progresso. O usuário não deve fazer nenhuma suposição sobre qual thread executará a single seção. Todos os outros threads irão pular a seção single e parar na barreira ao fim da estrutura single. Se outros segmentos puderem prosseguir sem esperar que o segmento execute a single seção, uma cláusula nowait pode ser especificada na diretiva single.
#pragma omp parallel
{
#pragma omp single
printf_s("Beginning work1.\n");
work1();
#pragma omp single
printf_s("Finishing work1.\n");
#pragma omp single nowait
printf_s("Finished work1 and beginning work2.\n");
work2();
}
A.10 Ordenação sequencial
As seções ordenadas são úteis para ordenar sequencialmente a saída do trabalho feito em paralelo. O seguinte programa imprime os índices em ordem sequencial:
#pragma omp for ordered schedule(dynamic)
for (i=lb; i<ub; i+=st)
work(i);
void work(int k)
{
#pragma omp ordered
printf_s(" %d", k);
}
A.11 Um número fixo de roscas
Alguns programas dependem de um número fixo e pré-especificado de threads para serem executados corretamente. Como a configuração padrão para o ajuste dinâmico do número de threads é definida pela implementação, esses programas podem optar por desativar o recurso de threads dinâmicos e definir o número de threads explicitamente para manter a portabilidade. O exemplo a seguir mostra como fazer isso usando omp_set_dynamic e omp_set_num_threads:
omp_set_dynamic(0);
omp_set_num_threads(16);
#pragma omp parallel shared(x, npoints) private(iam, ipoints)
{
if (omp_get_num_threads() != 16)
abort();
iam = omp_get_thread_num();
ipoints = npoints/16;
do_by_16(x, iam, ipoints);
}
Neste exemplo, o programa é executado corretamente somente se for executado por 16 threads. Se a implementação não for capaz de suportar 16 threads, o comportamento deste exemplo será definido pela implementação.
O número de threads que executam uma região paralela permanece constante durante uma região paralela, independentemente da configuração de threads dinâmicos. O mecanismo de threads dinâmicos determina o número de threads a utilizar no início da região paralela e mantém-no constante ao longo da região.
A.12 A diretiva atómica
O seguinte exemplo evita condições de concorrência (atualizações simultâneas de um elemento de x por várias threads) utilizando a diretiva atómica:
#pragma omp parallel for shared(x, y, index, n)
for (i=0; i<n; i++)
{
#pragma omp atomic
x[index[i]] += work1(i);
y[i] += work2(i);
}
A vantagem de usar a atomic diretiva neste exemplo é que ela permite que atualizações de dois elementos diferentes de x ocorram em paralelo. Se uma diretiva crítica for usada em vez disso, todas as atualizações dos elementos de x serão executadas em série (embora não em qualquer ordem garantida).
A diretiva atomic aplica-se apenas à instrução C ou C++ imediatamente a seguir. Como resultado, os elementos de y não são atualizados atomicamente neste exemplo.
A.13 Uma diretiva "flush" com uma lista
O exemplo a seguir usa a diretiva flush para sincronização ponto a ponto de objetos específicos entre pares de threads.
int sync[NUMBER_OF_THREADS];
float work[NUMBER_OF_THREADS];
#pragma omp parallel private(iam,neighbor) shared(work,sync)
{
iam = omp_get_thread_num();
sync[iam] = 0;
#pragma omp barrier
// Do computation into my portion of work array
work[iam] = ...;
// Announce that I am done with my work
// The first flush ensures that my work is
// made visible before sync.
// The second flush ensures that sync is made visible.
#pragma omp flush(work)
sync[iam] = 1;
#pragma omp flush(sync)
// Wait for neighbor
neighbor = (iam>0 ? iam : omp_get_num_threads()) - 1;
while (sync[neighbor]==0)
{
#pragma omp flush(sync)
}
// Read neighbor's values of work array
... = work[neighbor];
}
A.14 Uma diretiva de descarga sem lista
O exemplo a seguir (para a seção 2.6.5) distingue os objetos compartilhados afetados por uma flush diretiva sem lista dos objetos compartilhados que não são afetados:
// omp_flush_without_list.c
#include <omp.h>
int x, *p = &x;
void f1(int *q)
{
*q = 1;
#pragma omp flush
// x, p, and *q are flushed
// because they are shared and accessible
// q is not flushed because it is not shared.
}
void f2(int *q)
{
#pragma omp barrier
*q = 2;
#pragma omp barrier
// a barrier implies a flush
// x, p, and *q are flushed
// because they are shared and accessible
// q is not flushed because it is not shared.
}
int g(int n)
{
int i = 1, j, sum = 0;
*p = 1;
#pragma omp parallel reduction(+: sum) num_threads(10)
{
f1(&j);
// i, n and sum were not flushed
// because they were not accessible in f1
// j was flushed because it was accessible
sum += j;
f2(&j);
// i, n, and sum were not flushed
// because they were not accessible in f2
// j was flushed because it was accessible
sum += i + j + *p + n;
}
return sum;
}
int main()
{
}
A.15 O número de roscas utilizadas
Considere o seguinte exemplo incorreto (para o ponto 3.1.2):
np = omp_get_num_threads(); // misplaced
#pragma omp parallel for schedule(static)
for (i=0; i<np; i++)
work(i);
A omp_get_num_threads() chamada retorna 1 na seção serial do código, portanto, np sempre será igual a 1 no exemplo anterior. Para determinar o número de threads que serão alocados para a região paralela, a chamada deve estar inserida dentro dessa região paralela.
O exemplo a seguir mostra como reescrever este programa sem incluir uma consulta para o número de threads:
#pragma omp parallel private(i)
{
i = omp_get_thread_num();
work(i);
}
A.16 Fechaduras
No exemplo a seguir (para a seção 3.2), o argumento para as funções de bloqueio deve ser do tipo omp_lock_t, sendo que não há necessidade de o liberar. As funções de bloqueio fazem com que o thread fique ocioso enquanto aguarda a entrada na primeira seção crítica, mas que realize outro tipo de trabalho enquanto aguarda a entrada na segunda seção. A função omp_set_lock bloqueia, mas a função omp_test_lock não, permitindo que o trabalho em skip() seja realizado.
// omp_using_locks.c
// compile with: /openmp /c
#include <stdio.h>
#include <omp.h>
void work(int);
void skip(int);
int main() {
omp_lock_t lck;
int id;
omp_init_lock(&lck);
#pragma omp parallel shared(lck) private(id)
{
id = omp_get_thread_num();
omp_set_lock(&lck);
printf_s("My thread id is %d.\n", id);
// only one thread at a time can execute this printf
omp_unset_lock(&lck);
while (! omp_test_lock(&lck)) {
skip(id); // we do not yet have the lock,
// so we must do something else
}
work(id); // we now have the lock
// and can do the work
omp_unset_lock(&lck);
}
omp_destroy_lock(&lck);
}
A.17 Fechos neestáveis
O exemplo a seguir (para a seção 3.2) demonstra como um bloqueio aninhado pode ser usado para sincronizar atualizações tanto para toda a estrutura quanto para um dos seus membros.
#include <omp.h>
typedef struct {int a,b; omp_nest_lock_t lck;} pair;
void incr_a(pair *p, int a)
{
// Called only from incr_pair, no need to lock.
p->a += a;
}
void incr_b(pair *p, int b)
{
// Called both from incr_pair and elsewhere,
// so need a nestable lock.
omp_set_nest_lock(&p->lck);
p->b += b;
omp_unset_nest_lock(&p->lck);
}
void incr_pair(pair *p, int a, int b)
{
omp_set_nest_lock(&p->lck);
incr_a(p, a);
incr_b(p, b);
omp_unset_nest_lock(&p->lck);
}
void f(pair *p)
{
extern int work1(), work2(), work3();
#pragma omp parallel sections
{
#pragma omp section
incr_pair(p, work1(), work2());
#pragma omp section
incr_b(p, work3());
}
}
A.18 Aninhado para diretivas
O seguinte exemplo de for aninhamento de diretivas é compatível porque as diretivas internas e externas for se ligam a diferentes regiões paralelas:
#pragma omp parallel default(shared)
{
#pragma omp for
for (i=0; i<n; i++)
{
#pragma omp parallel shared(i, n)
{
#pragma omp for
for (j=0; j<n; j++)
work(i, j);
}
}
}
Uma variação a seguir do exemplo anterior também é compatível:
#pragma omp parallel default(shared)
{
#pragma omp for
for (i=0; i<n; i++)
work1(i, n);
}
void work1(int i, int n)
{
int j;
#pragma omp parallel default(shared)
{
#pragma omp for
for (j=0; j<n; j++)
work2(i, j);
}
return;
}
A.19 Exemplos que mostram aninhamento incorreto de diretivas de partilha de trabalho
Os exemplos nesta secção ilustram as regras de aninhamento de diretivas.
O exemplo a seguir não é compatível porque as diretivas for interna e externa são aninhadas e se ligam à mesma diretiva parallel.
void wrong1(int n)
{
#pragma omp parallel default(shared)
{
int i, j;
#pragma omp for
for (i=0; i<n; i++) {
#pragma omp for
for (j=0; j<n; j++)
work(i, j);
}
}
}
A seguinte versão aninhada dinamicamente do exemplo anterior também não é compatível:
void wrong2(int n)
{
#pragma omp parallel default(shared)
{
int i;
#pragma omp for
for (i=0; i<n; i++)
work1(i, n);
}
}
void work1(int i, int n)
{
int j;
#pragma omp for
for (j=0; j<n; j++)
work2(i, j);
}
O exemplo a seguir não é conforme porque as diretivas for e single são aninhadas e se ligam à mesma região paralela:
void wrong3(int n)
{
#pragma omp parallel default(shared)
{
int i;
#pragma omp for
for (i=0; i<n; i++) {
#pragma omp single
work(i);
}
}
}
O exemplo a seguir não é compatível porque uma barrier diretiva dentro de um for pode resultar em impasse:
void wrong4(int n)
{
#pragma omp parallel default(shared)
{
int i;
#pragma omp for
for (i=0; i<n; i++) {
work1(i);
#pragma omp barrier
work2(i);
}
}
}
O exemplo a seguir não é compatível porque resulta barrier em deadlock devido ao fato de que apenas um thread de cada vez pode entrar na seção crítica:
void wrong5()
{
#pragma omp parallel
{
#pragma omp critical
{
work1();
#pragma omp barrier
work2();
}
}
}
O exemplo a seguir não está em conformidade porque barrier resulta em deadlock porque apenas uma linha de execução executa a seção single.
void wrong6()
{
#pragma omp parallel
{
setup();
#pragma omp single
{
work1();
#pragma omp barrier
work2();
}
finish();
}
}
A.20 Diretivas vinculativas de barreira
As regras vinculativas da diretiva exigem que uma barrier diretiva vincule a diretiva anexa parallel mais próxima. Para mais informações sobre a vinculação de diretivas, ver secção 2.8.
No exemplo a seguir, a chamada de main para sub2 é compatível porque o barrier (em sub3) se liga à região paralela em sub2. A chamada de main para sub1 é compatível porque o barrier se liga à região paralela na subrotina sub2. A chamada de main para sub3 é compatível porque barrier não se liga a nenhuma região paralela e é ignorado. Além disso, o barrier sincroniza apenas o conjunto de threads na região paralela envolvente e não todos os threads criados em sub1.
int main()
{
sub1(2);
sub2(2);
sub3(2);
}
void sub1(int n)
{
int i;
#pragma omp parallel private(i) shared(n)
{
#pragma omp for
for (i=0; i<n; i++)
sub2(i);
}
}
void sub2(int k)
{
#pragma omp parallel shared(k)
sub3(k);
}
void sub3(int n)
{
work(n);
#pragma omp barrier
work(n);
}
A.21 Variáveis de âmbito com a cláusula privada
Os valores de i e j no exemplo a seguir são indefinidos na saída da região paralela:
int i, j;
i = 1;
j = 2;
#pragma omp parallel private(i) firstprivate(j)
{
i = 3;
j = j + 2;
}
printf_s("%d %d\n", i, j);
Para mais informações sobre a private cláusula, ver secção 2.7.2.1.
A.22 A cláusula default(none)
O exemplo a seguir distingue as variáveis que são afetadas pela default(none) cláusula das variáveis que não são:
// openmp_using_clausedefault.c
// compile with: /openmp
#include <stdio.h>
#include <omp.h>
int x, y, z[1000];
#pragma omp threadprivate(x)
void fun(int a) {
const int c = 1;
int i = 0;
#pragma omp parallel default(none) private(a) shared(z)
{
int j = omp_get_num_thread();
//O.K. - j is declared within parallel region
a = z[j]; // O.K. - a is listed in private clause
// - z is listed in shared clause
x = c; // O.K. - x is threadprivate
// - c has const-qualified type
z[i] = y; // C3052 error - cannot reference i or y here
#pragma omp for firstprivate(y)
for (i=0; i<10 ; i++) {
z[i] = y; // O.K. - i is the loop control variable
// - y is listed in firstprivate clause
}
z[i] = y; // Error - cannot reference i or y here
}
}
Para mais informações sobre a default cláusula, ver secção 2.7.2.5.
A.23 Exemplos da diretiva ordenada
É possível ter muitas seções ordenadas com um for especificado com a ordered cláusula. O primeiro exemplo é incompatível porque a API especifica a seguinte regra:
"Uma iteração de um loop com uma for construção não deve executar a mesma ordered diretiva mais de uma vez e não deve executar mais de uma ordered diretiva." (Ver secção 2.6.6.)
Neste exemplo não compatível, todas as iterações executam duas seções ordenadas:
#pragma omp for ordered
for (i=0; i<n; i++)
{
...
#pragma omp ordered
{ ... }
...
#pragma omp ordered
{ ... }
...
}
O exemplo compatível a seguir mostra uma for com mais de uma seção ordenada:
#pragma omp for ordered
for (i=0; i<n; i++)
{
...
if (i <= 10)
{
...
#pragma omp ordered
{ ... }
}
...
(i > 10)
{
...
#pragma omp ordered
{ ... }
}
...
}
A.24 Exemplo de cláusula privada
A cláusula privada de uma região paralela só está em vigor para a extensão lexical da região, não para a extensão dinâmica da região. Portanto, no exemplo a seguir, qualquer uso da variável a dentro do for loop na rotina f refere-se a uma cópia privada de a, enquanto um uso na rotina g refere-se ao global a.
int a;
void f(int n)
{
a = 0;
#pragma omp parallel for private(a)
for (int i=1; i<n; i++)
{
a = i;
g(i, n);
d(a); // Private copy of "a"
...
}
...
void g(int k, int n)
{
h(k,a); // The global "a", not the private "a" in f
}
A.25 Exemplos da cláusula de atributo copyprivate data
Exemplo 1: A cláusula copyprivate pode ser usada para transmitir valores adquiridos por um único thread diretamente para todas as instâncias das variáveis privadas nos outros threads.
float x, y;
#pragma omp threadprivate(x, y)
void init( )
{
float a;
float b;
#pragma omp single copyprivate(a,b,x,y)
{
get_values(a,b,x,y);
}
use_values(a, b, x, y);
}
Se a rotina init for chamada de uma região serial, o comportamento não será afetado pela presença das diretivas. Depois que a chamada para a rotina get_values tiver sido executada por um thread, nenhum thread deixará a construção até que os objetos privados designados por a, b, x e y em todos os threads tenham sido definidos com os valores lidos.
Exemplo 2: Em contraste com o exemplo anterior, suponha que a leitura deve ser executada por um thread específico, digamos o thread mestre. Nesse caso, a copyprivate cláusula não pode ser usada para fazer a transmissão diretamente, mas pode ser usada para fornecer acesso a um objeto compartilhado temporário.
float read_next( )
{
float * tmp;
float return_val;
#pragma omp single copyprivate(tmp)
{
tmp = (float *) malloc(sizeof(float));
}
#pragma omp master
{
get_float( tmp );
}
#pragma omp barrier
return_val = *tmp;
#pragma omp barrier
#pragma omp single
{
free(tmp);
}
return return_val;
}
Exemplo 3: Suponha que o número de objetos de bloqueio necessários dentro de uma região paralela não possa ser facilmente determinado antes de inseri-la. A copyprivate cláusula pode ser usada para fornecer acesso a objetos de bloqueio compartilhados alocados dentro dessa região paralela.
#include <omp.h>
omp_lock_t *new_lock()
{
omp_lock_t *lock_ptr;
#pragma omp single copyprivate(lock_ptr)
{
lock_ptr = (omp_lock_t *) malloc(sizeof(omp_lock_t));
omp_init_lock( lock_ptr );
}
return lock_ptr;
}
A.26 A diretiva threadprivate
Os exemplos a seguir demonstram como usar a diretiva threadprivate para dar a cada thread um contador separado.
Exemplo 1
int counter = 0;
#pragma omp threadprivate(counter)
int sub()
{
counter++;
return(counter);
}
Exemplo 2
int sub()
{
static int counter = 0;
#pragma omp threadprivate(counter)
counter++;
return(counter);
}
A.27 Matrizes de comprimento variável C99
O exemplo a seguir demonstra como usar C99 Variable Length Arrays (VLAs) em uma diretiva firstprivate .
Observação
Matrizes de comprimento variável não são suportadas atualmente no Visual C++.
void f(int m, int C[m][m])
{
double v1[m];
...
#pragma omp parallel firstprivate(C, v1)
...
}
A.28 A cláusula num_threads
O exemplo a seguir demonstra a cláusula num_threads . A região paralela é executada com um máximo de 10 threads.
#include <omp.h>
int main()
{
omp_set_dynamic(1);
...
#pragma omp parallel num_threads(10)
{
... parallel region ...
}
}
A.29 Construções de partilha de tarefas dentro de uma construção crítica
O exemplo a seguir demonstra o uso de uma construção de compartilhamento de trabalho dentro de uma critical construção. Este exemplo é compatível porque a construção de compartilhamento de trabalho e a critical construção não se ligam à mesma região paralela.
void f()
{
int i = 1;
#pragma omp parallel sections
{
#pragma omp section
{
#pragma omp critical (name)
{
#pragma omp parallel
{
#pragma omp single
{
i++;
}
}
}
}
}
}
A.30 Reprivatização
O exemplo a seguir demonstra a reprivatização de variáveis. As variáveis privadas podem ser marcadas private novamente numa diretiva aninhada. Você não precisa compartilhar essas variáveis na região paralela que o inclui.
int i, a;
...
#pragma omp parallel private(a)
{
...
#pragma omp parallel for private(a)
for (i=0; i<10; i++)
{
...
}
}
A.31 Funções de bloqueio seguras para threads
O exemplo C++ a seguir demonstra como inicializar uma matriz de bloqueios em uma região paralela usando omp_init_lock.
// A_13_omp_init_lock.cpp
// compile with: /openmp
#include <omp.h>
omp_lock_t *new_locks() {
int i;
omp_lock_t *lock = new omp_lock_t[1000];
#pragma omp parallel for private(i)
for (i = 0 ; i < 1000 ; i++)
omp_init_lock(&lock[i]);
return lock;
}
int main () {}