• Nenhum resultado encontrado

Programação com OpenMP

No documento 4 a 6 de Abril de 2018 (páginas 31-36)

30 ERAD 2018- Porto Alegre, 4 a 6 de abril de 2018

O OpenMP utiliza o recurso de pragma, reconhecidos apenas por compiladores compatíveis. Com ospragma, os programadores indicam ao compilador as opções de pa-ralelismo e de controle, e o compilador fica responsável por “embutir” o código necessário para a realização dessas tarefas. Além dospragma, um conjunto de funções auxiliam o programador a desenvolver seu código.

Em Fortran os pragma OpenMP são identificados por “!$”, e servem para defi-nir o início de uma diretiva OpenMP, neste trabalho serão mostrados algumas diretivas consideradas básicas, explicando seu funcionamento.

Para compilar um programa fazendo uso de OpenMP, é necessário utilizar a flag identificando o uso da API, que varia de acordo com o compilador utilizado. Para o compilador gfortran, a flag que indica o uso de OMP é “-fopenmp”, exemplificado na Figura 4.2.

Figura 4.2: Exemplo de compilação utlilizando OpenMP com o compilador GFortran..

Tem-se na Figura 4.2a o esquema geral de compilação de um programa utilizando OpenMP, os únicos argumentos obrigatórios são a flag que indica o uso de OpenMP e o nome do código fonte. O uso de ‘-o’ é opcional e serve para alterar o nome do arquivo de saída. A Figura 4.2b mostra um exemplo de uso comum do compilador com a flag OpenMP de compilação ativada. Como apresentado na figura 4.2c, pode-se incluir outros parâmetros caso desejado. A execução é igual a qualquer outro programa.

4.3.1. Funções básicas do OpenMP

OpenMP possui quatro funções básicas que são utilizadas para controle de threads, são elas:

• OMP_GET_NUM_THREADS(): Retorna o número de threads utilizadas em uma seção paralela;

• OMP_GET_MAX_THREADS(): Retorna o número de threads disponível;

• OMP_SET_NUM_THREADS(nthreads): Define o número de threads a ser usado pelas seções paralelas,

• OMP_GET_THREAD_NUM(): Retorna o número identificador(ID) da thread. as IDs variam de 0(thread master) até OMP_GET_NUM_THREADS() -1.

A Figura 4.3 ilustra o funcionamento das funções descritas acima. É importante notar que as funções OMP_GET_THREAD_NUM(), OMP_GET_NUM_THREADS() e OMP_GET_MAX_THREADS() precisam ser declaradas como EXTERNAL (linha 4).

Minicursos 31

Figura 4.3: Exemplo de utilização da funções básicas..

Na linha 5 da Figura 4.3a, é utilizada a função OMP_GET_MAX_THREADS(), que como podemos ver na figura 4.3b, tem como resultado o valor oito, significando que o processador utilizado possui oito threads. Em seguida (linha 7 da figura 4.3a) é definido o máximo de threads a serem usadas como cinco, assim, a seção paralela a seguir será executada por cinco threads, com IDs variando de zero a quatro, como mostrado na figura 4.3b. Na linha 11 da figura 4.3a, é selecionado o processo de ID zero para calcular e mostrar na tela o número de threads utilizadas.

4.3.2. Blocos de código para execução em paralelo

O objetivo principal do OpenMP é realizar a execução em paralelo de programas.

Isso pode ser realizado com o par!$OMP PARALLEL e !$OMP END PARALLEL, que marca, respectivamente, o início e o fim de um bloco a ser processado em paralelo.

É importante definir quais variáveis serão privadas (PRIVATE) ou compartilhadas (SHA-RED) entre as threads.

As variáveis privadas possuem valor indefinido ao iniciar o processamento da se-ção paralela, onde é criada uma cópia das mesmas para cada thread. Ao terminar a sese-ção paralela, a variável privada não mantém o valor da seção paralela, pois como possui um valor único para cada thread não é possível definir o valor que deveria ser atribuído. Há também outros dois tipos de variáveis privadas, são eles FIRSTPRIVATE e LASTPRI-VATE. FIRSTPRIVATE inicia a seção paralela com o valor que possuía na sequencial,

32 ERAD 2018- Porto Alegre, 4 a 6 de abril de 2018 enquanto o LASTPRIVATE mantém ao sair da seção paralela o valor que teria caso a mesma fosse executada de forma sequencial, como por exemplo, a última iteração de um laço de repetição. A diferença entre os tipos PRIVATE e FIRSTPRIVATE pode ser notada na linha 13 da figura 4.4a e na linha correspondente da figura 4.4b.

Figura 4.4: Exemplo do uso de variáveis PRIVATE e FIRSTPRIVATE.

Variáveis do tipo PRIVATE iniciam a seção paralela sem um valor definido, como pode ser visto na Figura 4.4b, onde tem seu valor alterado para 10, e o mesmo é perdido ao sair da seção. No entanto, a variável FIRSTPRIVATE inicia o processamento paralelo com o valor que possuía anteriormente, porém o mesmo é perdido ao terminar a seção.

Quando for necessário que apenas uma thread execute um deteminado código, pode-se utilizar um dos seguintespragma:

• !$OMP MASTER e !$OMP END MASTER;

• !$OMP SINGLE e !$OMP END SINGLE.

Com uso do par depragma!$OMP MASTERe!$OMP END MASTERé defi-nida uma seção a ser executada apenas pela thread master, ou seja, a que possui ID igual a zero( OMP_GET_THREAD_NUM()==0). Um exemplo de seu uso pode ser observado nas linhas 11 e 14 da figura 4.3a.

Ao ser utilizado o par de pragma!$OMP SINGLEe!$OMP END SINGLE, o funcionamento e sintaxe é semelhante a!$OMP MASTER, porém o trecho de código é executado apenas pela primeira thread a alcançar a seção, independentemente de sua ID.

4.3.3. Divisão do processamento entre as threads

A divisão do processamento a ser realizado pelas threads pode ser controlada usando um dos seguintespragma:

Minicursos 33

• !$OMP DO e !$OMP END DO;

• !$OMP SECTIONS e !$OMP END SECTIONS.

Pode ser usado o par !$OMP DOe !$OMP END DOpara delimitar um trecho de código, composto da iteração do loop DO. Os índices do laço são divididos entre as threads. É importante notar que a variável de controle de um loop (step), é definida como PRIVATE por padrão, não sendo necessário especificar seu escopo de compartilhamento.

Um exemplo de uso do!$OMP DOe !$OMP END DO pode ser observada das linhas 12 a 17 da figura 4.5a.

Figura 4.5: Exemplo do uso de !$OMP DO e !$OMP CRITICAL.

Pode ser usado a união dospragma !$OMP PARALLEL e!$OMP DO(e seus delimitadores finais) para declarar de uma só vez seção paralela e o laço de repetição na mesma linha.

Com o objetivo de criar uma seção crítica para as threads em execução num ponto do código, pode-se utilizar o par de diretivas!$OMP CRITICAL <nome> e !$OMP END CRITICAL <nome>para indicar que as threads devem executar a seção uma de cada vez, ou seja, nunca haverá duas threads executando ao mesmo tempo aquele código.

O campo <nome> não é necessário, porém é recomendado seu uso, uma vez que, caso di-ferentes seções críticas possuírem o mesmo nome, serão tratadas como uma só, o mesmo se aplica caso o nome não seja definido. Um exemplo onde uma seção crítica é neces-sária é onde o resultado de uma operação é acumulado em uma variável compartilhada, como pode ser visto na linha 19 da figura 4.5a, evitando que haja conflito entre diferentes threads atualizando a mesma variável simultaneamente.

O par de diretivas !$OMP SECTIONS e !$OMP END SECTIONSé utilizado para definir tarefas diferentes a serem executadas por cada thread. O código a ser excecu-tado em cada thread deve ser precedido por!$OMP SECTION sendo finalizado por outra diretiva igual ou por um !$OMP END SECTIONS. Podem ser especificadas quantas seções for desejado, sendo cada uma delas executada numa thread diferente.

34 ERAD 2018- Porto Alegre, 4 a 6 de abril de 2018

No documento 4 a 6 de Abril de 2018 (páginas 31-36)

Documentos relacionados