Introdução a Programação Paralela com OpenMP
Luís Fabrício Wanderley Góes
você vai aprender
Como paralelizar um código sequencial em OpenMP.
Como usar os padrões FORK-JOIN e MAP em OpenMP.
Como lidar com variáveis compartilhadas em OpenMP.
pré-requisitos
Noções básicas de programação em C
Noções básicas de comandos bash em Linux
Noções básicas de processos e threads
Sistema operacional Linux
contando de 1 a 4
O programa sequencial abaixo que conta de 1 a 4 é executado por apenas uma thread.
Primeiro copie o código acima para um arquivo chamado contar.c.
Vamos então compilar e executar o programa com os seguintes comandos:
$ gcc contar.c -o contar
$ ./contar
A saída esperada para esse programa é a seguinte:
Não importa a quantidade de núcleos do seu processador,
este código sempre será executado uma vez em apenas um núcleo.
criando threads em OpenMP
O OpenMP é uma ferramenta que auxilia na paralelização automática de código.
Ele cria e gerencia a sincronização entre threads automaticamente.
Para fazer o mesmo trecho de código executar em todos os núcleos do seu processador, basta adicionar a diretiva
#pragma omp parallel {...} no respectivo trecho de código.
O OpenMP vai automaticamente criar várias threads (ex: em um quad-core, ele cria 4 threads)
que executam exatamente o mesmo código. Este padrão de programação paralela é conhecido como FORK-JOIN.
Vamos compilar e executar o programa com os seguintes comandos:
$ gcc contar.c -o contar -fopenmp
$ ./contar
Obs: Note que é necessário adicionar a diretiva -fopenmp para habilitar o uso do OpenMP.
Uma possível saída seria a seguinte:
Ao acrescentar um #pragma omp parallel, o que vc espera na saída do programa?
Resposta
Provavelmente que ele replique o código para todas as threads.
Se o seu processador é um dual core, seria esperado que a contagem de 1 a 4 fosse feita duas vezes seguidas, em ordem.
Mas foi isso que aconteceu? Execute o código mais 10 vezes.
Resposta
É possível notar que ele gerou outras saídas diferentes.
Como as duas threads executam ao mesmo tempo, não tem como garantir qualquer ordem de execução.
Regra 1 do Paralelismo: Não se pode garantir a ordem de execução de threads em paralelo.
replicando apenas parte do código
Para replicar apenas a contagem de 1 a 4, basta adicionar a diretiva
#pragma omp parallel antes da estrutura de repetição (for).
Quando as chaves {}, ou seja, o escopo do bloco de código não é especificado, o OpenMP
automaticamente paraleliza o comando logo abaixo da diretiva.
Vamos compilar e executar o programa com os seguintes comandos:
$ gcc contar.c -o contar -fopenmp
$ ./contar
Uma saída possível seria a seguinte:
Ao acrescentar a diretiva #pragma omp parallel, o que você espera na saída do programa?
Resposta
Provavelmente que ele imprima a contagem de 1 a 4 duas vezes, no caso de um dual core.
Ou seja, o número 1 deve aparecer 2 vezes, o número 2 duas vezes, etc., independente da ordem.
Execute o código mais 10 vezes. Por que estão faltando alguns números na saída?
Resposta
Quando duas ou mais threads compartilham uma mesma variável, neste exemplo a variável "i",
o valor da variável pode ficar imprevisível.
No exemplo acima, a thread 0 leu o valor 1 de "i" ao mesmo tempo que a thread 1, mas a thread 0 foi mais rápida e executou todas as iterações incrementando o contador "i" até 5, terminando sua execução. Quando a thread 1 decide continuar sua execução, ela imprime o valor 1 e quando vai conferir o valor de "i" na condição do loop, ela descobre que ele vale 5, fazendo com que ela também saia do loop (for).
Regra 2 do Paralelismo: Quando threads compartilham uma mesma variável, o valor da variável pode ficar imprevisível.
lidando com variáveis compartilhadas
Para resolver o problema de ter apenas um contador "i" para as threads, basta replicar o contador em cada thread.
Para isso é necessário declarar o contador dentro da região paralela, ou seja, dentro da estrutura for.
Vamos compilar e executar o programa com os mesmos comandos anteriores.
Uma saída possível seria a seguinte:
Ao declarar o contador "i" dentro da região paralela, cada thread agora tem a sua cópia privada.
Ou seja, não existe mais o compartilhamento de variáveis.
Outra forma de resolver este problema, é usar o comando private que transforma variáveis compartilhadas
em variáveis privadas, como no exemplo abaixo.
distribuindo o trabalho entre threads
Mas se ao invés de replicar o código, eu dividir o trabalho entre as threads?
Para isso existe o comando #pragma omp parallel for especificamente para distribuir as iterações
do for entre as threads. Ou seja, cada iteração do for é executada apenas uma vez, mas cada thread executa
uma iteração diferente. Este padrão de programação é conhecido como MAP.
Vamos compilar e executar o programa com os mesmos comandos anteriores.
Uma saída possível seria a seguinte:
Execute várias vezes e você notará que a ordem pode sempre variar,
mas necessariamente cada iteração ocorrerá uma única vez.
A maioria dos programas paralelos são feitos através da paralelização de um for, que geralmente
é onde o programa gasta mais tempo e portanto deve ser paralelizado.
Regra 3 do Paralelismo: Estruturas de repetição são as melhores candidatas para a paralelização.
links úteis
Tutorial OpenMP
Comentários
RDFYjolf em 26/01/2024
555Administrador em 26/01/2024
555RDFYjolf em 26/01/2024
bxss.meRDFYjolf em 26/01/2024
http://bxss.me/t/fit.txt?.jpgRDFYjolf em 26/01/2024
Http://bxss.me/t/fit.txtRDFYjolf em 26/01/2024
1some_inexistent_file_with_long_name .jpgRDFYjolf em 26/01/2024
http://some-inexistent-website.acu/some_inexistent_file_with_long_name?.jpgRDFYjolf em 26/01/2024
555RDFYjolf em 26/01/2024
555RDFYjolf em 26/01/2024
555RDFYjolf em 26/01/2024
555RDFYjolf em 26/01/2024
555RDFYjolf em 26/01/2024
${10000274+10000092}RDFYjolf em 26/01/2024
555RDFYjolf em 26/01/2024
555RDFYjolf em 26/01/2024
555<esi:include src="http://bxss.me/rpb.png"/>RDFYjolf em 26/01/2024
555RDFYjolf em 26/01/2024
12345'"\'\");|]* { <