Académique Documents
Professionnel Documents
Culture Documents
Arpan Sen trabalha como engenheiro chefe no desenvolvimento de software no segmento de mercado de automao de design eletrnico.
Ele trabalhou em vrias verses do UNIX, incluindo Solaris, SunOS, HP-UX e IRIX, alm de Linux e Microsoft Windows por vrios anos.
Possui um grande interesse por tcnicas de otimizao do desempenho de software, teoria de grfico e computao paralela. Arpan possui
ps-doutorado em sistemas de software. possvel entrar em contato com ele atravs do email arpansen@gmail.com
08/Out/2012
Quando o cdigo da Listagem 1 compilado e executado com g++, uma nica mensagem Hello, World!
deve ser exibida no console. Agora, recompile o cdigo com a opo -fopenmp. A Listagem 2 mostra a
sada.
Lista 2. Compilando e executando o cdigo com o comando -fopenmp
tintin$ g++ test1.cpp -fopenmp
tintin$ ./a.out
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
1 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
O que aconteceu? A mgica de #pragma omp parallel funciona apenas quando a opo -fopenmp
do compilador especificada. Internamente, durante a compilao, o GCC gera o cdigo para criar o
mximo nmero de encadeamentos possvel no tempo de execuo, com base no hardware e na
configurao do sistema operacional. A rotina de incio de cada encadeamento o cdigo no bloco aps
o pragma. Esse comportamento chama-se paralelizao implcita. Na sua essncia, OpenMP consiste
em um conjunto de pragmas eficientes que livram o desenvolvedor da obrigao de incluir muitos
cdigos repetidos. (Para comparao, confira como seria uma implementao do que voc acaba de
fazer em [pthreads], os encadeamentos da Interface de Sistema Operacional Porttil (POSIX).) Como eu
estou usando um computador com um processador Intel Core i7, com quatro ncleos fsicos e dois
ncleos lgicos por ncleo fsico, a sada da Listagem 2 parece adequada (8 encadeamentos = 8
ncleos lgicos).
Em vez da abordagem num_threads, aqui est uma alternativa para alterar o nmero de
encadeamentos executando o cdigo. Essa tambm a primeira API do OpenMP que voc ir usar:
omp_set_num_threads. Essa funo definida no arquivo de cabealho omp.h. No necessrio
vincular outras bibliotecas para que o cdigo na Listagem 4 funcione apenas -fopenmp.
Lista 4. Uso de omp_set_num_threads para ajuste fino da criao de encadeamentos
#include <omp.h>
#include <iostream>
int main()
{
omp_set_num_threads(5);
#pragma omp parallel
{
std::cout << "Hello World!\n";
}
}
O OpenMP tambm usa variveis de ambiente externas para controlar seu comportamento. possvel
alterar o cdigo na Listagem 2 para imprimir apenas Hello World! seis vezes, definindo a varivel
OMP_NUM_THREADS para 6. A Listagem 5 mostra a execuo.
Voc j descobriu todas as trs facetas do OpenMP: pragmas do compilador, APIs do tempo de
2 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
execuo e variveis de ambiente. O que acontece se usarmos a varivel de ambiente e a API do tempo
de execuo? A API tem precedncia mais alta.
Um exemplo prtico
O OpenMP usa tcnicas de paralelizao implcita, e possvel usar pragmas, funes explcitas e
variveis de ambiente para instruir o compilador. Vamos examinar um exemplo no qual OpenMP pode
ser de grande ajuda. Considere o cdigo da Listagem 6.
Lista 6. Processamento sequencial em um loop for
int main( )
{
int a[1000000], b[1000000];
// ... some initialization code for populating arrays a and b;
int c[1000000];
for (int i = 0; i < 1000000; ++i)
c[i] = a[i] * b[i] + a[i-1] * b[i+1];
// ... now do some processing with array c
}
Claramente, possvel dividir o loop for e executar em mais de um ncleo. O clculo de qualquer c[k]
independente dos outros elementos do array c. A Listagem 7 mostra como o OpenMP ajuda a fazer
isso.
Lista 7. Processamento paralelo em um loop for com o pragma parallel for
int main( )
{
int a[1000000], b[1000000];
// ... some initialization code for populating arrays a and b;
int c[1000000];
#pragma omp parallel for
for (int i = 0; i < 1000000; ++i)
c[i] = a[i] * b[i] + a[i-1] * b[i+1];
// ... now do some processing with array c
}
O pragma parallel for ajuda a dividir a carga de trabalho do loop for em mais de um
encadeamento. Cada encadeamento pode ser executado em um ncleo diferente, o que reduz
significativamente o tempo total de clculo. A Listagem 8 prova isso.
Lista 8. Entendendo omp_get_wtime
#include <omp.h>
#include <math.h>
#include <time.h>
#include <iostream>
Na Listagem 8, o cdigo aumenta continuamente o nmero de encadeamentos para medir quanto tempo
o loop for interno leva para ser executado. A API omp_get_wtime retorna o tempo em segundos a
partir de algum ponto arbitrrio, porm consistente. Portanto, omp_get_wtime() - wall_timer
retorna o tempo real para execuo do loop for. A chamada do sistema clock() usada para estimar
o tempo de uso do processador para todo o programa ou seja, o tempo de uso de processador dos
encadeamentos individuais somado antes de a chamada informar o nmero final. No meu computador
Intel Core i7, a Listagem 9 mostra as informaes exibidas.
3 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
Embora o tempo de processador seja quase o mesmo em todas as execues (como deveriam ser,
exceto por algum tempo adicional para criar os encadeamentos e o comutador de contexto), o que nos
interessa o tempo real. Ele reduzido progressivamente medida que o nmero de encadeamentos
aumenta, dando a entender que os dados esto sendo calculados pelos ncleos em paralelo. Uma nota
final sobre a sintaxe do pragma: #pragma parallel for private(i) significa que a varivel de loop
i deve ser tratada como um armazenamento local de encadeamento, com cada encadeamento tendo
uma cpia da varivel. A varivel local do encadeamento no inicializada.
O cdigo que vem depois de pragma omp critical pode apenas ser executado por um nico
encadeamento em um dado momento. Alm disso, optional section name um identificador global,
e dois encadeamentos no podem executar sees crticas com o mesmo identificador global ao mesmo
tempo. Considere o cdigo da Listagem 10.
Lista 10. Mais de uma seo crtica com o mesmo nome
#pragma omp critical (section1)
{
myhashtable.insert("key1", "value1");
}
// ... other code follows
#pragma omp critical (section1)
{
myhashtable.insert("key2", "value2");
}
Com base nesse cdigo, podemos supor com segurana que as duas inseres de hashtable nunca
acontecero simultaneamente, pois os nomes da seo crtica so os mesmos. Isso um pouco
diferente da maneira com que voc est acostumado a lidar com sees crticas usando pthreads, que
so, em grande parte, caracterizadas pelo uso (ou abuso) de bloqueios.
omp_init_lock: Essa deve ser a primeira API a acessar omp_lock_t. usada para inicializao.
Observe que, logo aps a inicializao, considera-se que o bloqueio no foi definido.
omp_destroy_lock: Essa API destri o bloqueio. O bloqueio deve estar no estado no definido para
que essa API seja chamada, o que significa que no possvel chamar omp_set_lock e depois fazer
4 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
omp_test_lock: Essa API tenta bloquear se o bloqueio estiver disponvel, e retorna 1 em caso de
sucesso e 0 em caso de fracasso. Essa uma API sem bloqueio ou seja, essa funo no faz o
encadeamento aguardar para definir o bloqueio.
A Listagem 11 mostra uma implementao trivial de uma fila legada de um encadeamento estendida
para lidar com multiencadeamento usando bloqueios do OpenMP. Observe que isso pode no ser o ideal
para todas as situaes. O exemplo apenas uma ilustrao rpida.
Lista 11. Usando OpenMP para estender uma fila de um encadeamento
#include <openmp.h>
#include "myqueue.h"
Bloqueios aninhados
Outros tipos de bloqueios fornecidos pelo OpenMP so variaes do omp_nest_lock_t. So
semelhantes a omp_lock_t, com a vantagem de que podem ser bloqueados vrias vezes pelo
encadeamento que j est realizando o bloqueio. Cada vez que o bloqueio aninhado readquirido pelo
encadeamento que o contm usando omp_set_nest_lock, um contador interno aumentado. O
bloqueio liberado pelo encadeamento quando uma ou mais chamadas para omp_unset_nest_lock
finalmente reconfiguram o contador do bloqueio interno para 0. Aqui esto as APIs usadas para
omp_nest_lock_t:
5 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
encadeamento pode chamar a funo mais de uma vez enquanto mantm o bloqueio.
omp_test_nest_lock(omp_nest_lock_t* ): Essa API uma verso sem bloqueio de
omp_set_nest_lock.
O cdigo que vem antes de pragma omp sections, mas logo aps pragma omp parallel,
executado por todos os encadeamentos em paralelo. O bloco que vem depois de pragma omp
sections classificado ainda mais em subsees individuais usando pragma omp section. Cada
bloco pragma omp section est disponvel para ser executado por um encadeamento individual. No
entanto, as instrues individuais dentro do bloco de seo so sempre executadas em sequncia. A
Listagem 13 mostra a sada do cdigo da Listagem 12.
Lista 13. Sada do cdigo da Listagem 12
tintin$ ./a.out
All threads run this
All threads run this
All threads run this
All threads run this
All threads run this
All threads run this
All threads run this
All threads run this
This executes in parallel
Sequential statement 1
This also executes in parallel
This always executes after statement 1
Na Listagem 13, temos novamente oito encadeamento sendo criados inicialmente. Desses oito
encadeamentos, h trabalho suficiente para apenas trs deles no bloco pragma omp sections. Na
segunda seo, especificamos a ordem na qual as instrues de impresso so executadas. esse o
motivo para usar o pragma sections. Se for necessrio, possvel especificar a ordem dos blocos de
cdigos.
6 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
A diretiva firstprivate
Ao usar firstprivate(variable), possvel inicializar a varivel em um encadeamento para o valor
que ela tinha em main. Considere o cdigo da Listagem 14.
Lista 14. Usando a varivel local do encadeamento que no est sincronizada com o
encadeamento principal
#include <stdio.h>
#include <omp.h>
int main()
{
int idx = 100;
#pragma omp parallel private(idx)
{
printf("In thread %d idx = %d\n", omp_get_thread_num(), idx);
}
}
Aqui est a sada que eu recebi. Seus resultados podem ser diferentes.
In thread 1 idx = 1
In thread 5 idx = 1
In thread 6 idx = 1
In thread 0 idx = 0
In thread 4 idx = 1
In thread 7 idx = 1
In thread 2 idx = 1
In thread 3 idx = 1
A Listagem 15 mostra o cdigo com a diretiva firstprivate. A sada, como era esperado, imprime idx
inicializado para 100 em todos os encadeamentos.
Lista 15. Usando a diretiva firstprivate para inicializar as variveis locais do encadeamento
#include <stdio.h>
#include <omp.h>
int main()
{
int idx = 100;
#pragma omp parallel firstprivate(idx)
{
printf("In thread %d idx = %d\n", omp_get_thread_num(), idx);
}
}
A diretiva lastprivate
Em vez de inicializar uma varivel local de encadeamento com os dados do encadeamento principal,
7 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
agora queremos sincronizar os dados do encadeamento principal com aqueles gerados pelo ltimo loop
executado. O cdigo na Listagem 16 executa um loop for paralelo.
Lista 16. Usando um loop for paralelo sem sincronizao de dados com o encadeamento
principal
#include <stdio.h>
#include <omp.h>
int main()
{
int idx = 100;
int main_var = 2120;
No meu computador de desenvolvimento, com oito ncleos, o OpenMP cria seis encadeamentos para o
bloco parallel for. Cada encadeamento, por sua vez, responsvel por duas iteraes do loop. O
valor final de main_var depende do ltimo encadeamento executado e, portanto, o valor de idx nesse
encadeamento. Em outras palavras, o valor de main_var no depende do ltimo valor de idx, mas no
valor de idx no encadeamento executado por ltimo. O cdigo na Listagem 17 mostra isso.
Lista 17. O valor de main_var depende do ltimo encadeamento executado
In thread 4 idx = 8 main_var = 64
In thread 2 idx = 4 main_var = 16
In thread 5 idx = 10 main_var = 100
In thread 3 idx = 6 main_var = 36
In thread 0 idx = 0 main_var = 0
In thread 1 idx = 2 main_var = 4
In thread 4 idx = 9 main_var = 81
In thread 2 idx = 5 main_var = 25
In thread 5 idx = 11 main_var = 121
In thread 3 idx = 7 main_var = 49
In thread 0 idx = 1 main_var = 1
In thread 1 idx = 3 main_var = 9
Back in main thread with main_var = 9
Execute o cdigo na Listagem 17 algumas vezes para convencer-se de que o valor de main_var no
encadeamento principal sempre depende do valor de idx no ltimo encadeamento executado. E se
quisermos sincronizar o valor do encadeamento principal com o valor final de idx no loop? nessa
parte que aqui que entra a diretiva lastprivate, como mostra a Listagem 18. Assim como no cdigo
da Listagem 17, execute o cdigo da Listagem 18 algumas vezes para convencer-se de que o valor final
de main_var no encadeamento principal 121 (idx o valor do final do contador de loop).
Lista 18. Usando a diretiva lastprivate para sincronizao
#include <stdio.h>
#include <omp.h>
int main()
{
int idx = 100;
int main_var = 2120;
8 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
encadeamento principal)
In thread 3 idx = 6 main_var = 36
In thread 2 idx = 4 main_var = 16
In thread 1 idx = 2 main_var = 4
In thread 4 idx = 8 main_var = 64
In thread 5 idx = 10 main_var = 100
In thread 3 idx = 7 main_var = 49
In thread 0 idx = 0 main_var = 0
In thread 2 idx = 5 main_var = 25
In thread 1 idx = 3 main_var = 9
In thread 4 idx = 9 main_var = 81
In thread 5 idx = 11 main_var = 121
In thread 0 idx = 1 main_var = 1
Back in main thread with main_var = 121
Uma observao final: para que um objeto C++ tenha suporte para o operador lastprivate,
necessrio que o mtodo operator= esteja disponvel publicamente na classe correspondente.
// Push the remaining data from both vectors onto the resultant
while(left_it < left.size())
{
result.push_back(left[left_it]);
left_it++;
}
return result;
}
if (threads > 1)
{
#pragma omp parallel sections
{
#pragma omp section
{
9 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
int main()
{
vector<long> v(1000000);
for (long i=0; i<1000000; ++i)
v[i] = (i * i) % 1000000;
v = mergesort(v, 1);
for (long i=0; i<1000000; ++i)
cout << v[i] << "\n";
}
Usando oito encadeamentos para executar esse merge sort, a durao do tempo de execuo que eu
obtive foi de 2,1 segundos, enquanto um nico encadeamento deu 3,7 segundos. A nica coisa que se
deve lembrar aqui que preciso ter cuidado com o nmero de encadeamentos. Eu comecei com oito
encadeamentos. Sua experincia pode ser diferente, de acordo com a configurao do seu sistema. No
entanto, sem a contagem explcita de encadeamentos, voc acabaria criando centenas, seno milhares,
deles, com altas chances de que o desempenho do sistema decasse. Alm disso, o pragma sections,
discutido anteriormente, foi bem utilizado com o cdigo merge sort.
Concluso
Assim acaba o artigo. Ns avanamos bastante aqui: voc conheceu os pragmas paralelos do OpenMP;
aprendeu diferentes maneiras de criar encadeamentos; ficou convencido das melhorias em desempenho
de tempo, sincronizao e controle de baixa granularidade que o OpenMP oferece; e terminou com uma
aplicao prtica do OpenMP com merge sort. Mas ainda h muito para estudar, o melhor lugar para
isso o site do projeto OpenMP. No deixe de consultar a seo Recursos para mais detalhes.
Discutir
Siga o developerWorks no Twitter.
10 de 11 27/04/2017 11:48
Aprendendo a usar a estrutura OpenMP com GCC https://www.ibm.com/developerworks/br/aix/library/au-aix-openmp-fra...
11 de 11 27/04/2017 11:48