O OpenMP (do inglês Open Multi-Processing, ou Multi-processamento aberto) é uma interface de programação de aplicativo(API) para a programação multi-processo de memória compartilhada em múltiplas plataformas. Permite acrescentar simultaneidade aos programas escritos em C, C++ e Fortran sobre a base do modelo de execução fork-join. Está disponível em muitas arquiteturas, incluindo as plataforma Unix e Microsoft Windows. É constituída por um conjunto de diretivas de compilador, rotinas de biblioteca, e variáveis de ambiente que influenciam o comportamento do tempo de execução. Definido em conjunto por um grupo principal de fornecedores de hardware e de software, o OpenMP é um modelo de programação portável e escalável que proporciona aos programadores uma interface simples e flexível para o desenvolvimento de aplicações paralelas para as plataformas que vão dos computadores de escritório até os supercomputadores. Uma aplicação construída com um modelo de programação paralela híbrido pode ser executado em um cluster de computadores utilizando tanto os OpenMP e MPI, ou mais transparentemente através das extensões do OpenMP para os sistemas de memória distribuída.

Introdução OpenMP é uma implementação de multithreading, um método de paralelização no qual o "master thread"(uma série de instruções executadas consecutivamente) forks ("bifurca") um específico número de threads escravos e uma tarefa é dividida entre eles. Os thread são então executados simultaneamente, com ambiente de execução distribuindo as threads para diferentes processadores.

A parte de código que é criada para funcionar em paralelo é marcada de acordo com uma diretriz pré-processador que criará os thread para formar a secção antes de ser executada. Cada thread tem um "id" (endereço) anexado a ele, que pode ser obtido através de uma função (chamada omp_get_thread_num() em C / C + + e OMP_GET_THREAD_NUM() em Fortran). O thread "id" é um número inteiro e o master thread possui o id "0". Após a execução do código em paralelo, os "threads" retornam ao master thread, o qual continua progressivo até o fim do programa.

Por padrão, cada thread executa a seção do código paralelo independentemente. "Construções de Partilha" pode ser usada para dividir uma tarefa entre os threads de modo que cada thread execute a sua parte do código atribuído. Ambas as Task Parallelism (Paralelismo de tarefas) e Data parallelism (Paralelismo de dados) pode ser conseguida utilizando OpenMP desta forma.

O tempo de execução comum distribui threads aos processadores de acordo com a utilização, o desempenho da máquina e outros fatores. O números de threads pode ser fixado pelo ambiente de execução baseado em variaveis de ambiente ou um código usando funções. As funções do OpenMP estão incluidas no header file (biblioteca) rotulado "omp.h" em C/C++.

História

O Conselho de Análise e Arquitetura (Architecture Review Board, ARB) do OpenMP publicou suas primeiras especificações da API, para o OpenMP Fortran 1.0, em Outubro de 1997. Em Outubro do ano seguinte, foi liberado o C / C + + padrão. Em 2000 viu-se a versão 2.0 das especificações, com especificações do Fortran versão 2.0 do C / C + + sendo liberado em 2002. A versão 2.5 é um combinado das especificações C / C + + / Fortran que foi liberado em 2005. A versão 3.0, lançada em maio de 2008, é a atual versão das especificações da API. Incluídas nas novas funcionalidades do 3.0 estão o conceito de tasks (Tarefas) e a task construct (Construir Tarefa). Estas novas funcionalidades estão resumidas no Apêndice F do OpenMP 3.0 specifications.

Elementos centrais

Os principais elementos do OpenMP são os construídos por "criação de fios de execução" (threads creations), "distribuição de carga do trabalho" (work sharing), "gestão do ambiente de dados" (data environment management), "sincronização de fios de execução" (thread synchronization), "tempo de execução das rotinas do usuário" (user level runtime routines) e "variáveis do ambiente" (environment variables). A diretriz em compilador C / C + + é chamado pragma (informação pragmática). A compilação de diretrizes específicas para OpenMP em C / C + + são escritas em códigos, conforme segue:

pragma omp <rest de pragma>

Os pragmas específicos do OpenMP estão listados a seguir:

Criação de Threads

omp paralelo. É utilizado para separar threads adicionais para a realização do trabalho incluso na construção em paralelo. O processo original será chamado master thread com ID 0. Exemplo: Exibição "Hello, world", utilizando múltiplas threads.

#include <stdio.h> #include <omp.h>

int main(int argc, char* argv[]) { #pragma omp parallel printf("Hello, world.\n"); return 0;

Construção do compartilhamento

Usado para especificar como atribuir um trabalho independente para um ou todos as threads. omp for ou omp do: usado para fracionar os laços entre os caminhos de execução, também chamados "construtores de laço". sections: atribuindo consecutivos, porém independentes, blocos de código para os diferentes threads. single: especificando um bloco de código, que é então executado por apenas uma thread, com uma barreira implícita no fim master: semelhante ao single, mas o bloco de código será executado apenas pela thread mestre e nenhuma barreira estará implícita no final. Exemplo: inicializar o valor de uma grande variedade em paralelo, utilizando cada fio de execução para fazer parte do trabalho.

#include <stdio.h> #include <omp.h>

int main(int argc, char **argv) { const int N = 100000; int i, a[N];

#pragma omp parallel for for (i = 0; i < N; i++) a[i] = 2 * i;

return 0; }

Cláusulas do OpenMP

Desde que foi compartilhada a programação modelo do OpenMP, a maior parte das variaveis no código do OpenMP são visíveis a todos threads por padrão. Porém algumas variaveis individuais são necessárias para evitar race conditions e existe a necessidade de passar valores entre a parte sequencial e a região paralela ( o código executado em paralelo ), para que os dados de gestão ambiental sejam introduzidos como as cláusulas atribuídas ao compartilhamento de dados colocando-lhes as diretrizes do OpenMP. Os diferentes tipos de cláusulas são:

Características da Partilha de Dados

shared: os dados dentro de uma região paralela são compartilhados, o que significa visível e acessível por todas as threads em simultâneo. Por padrão, todas as variáveis da região de compartilhamento são partilhados, exceto o laço (loop). private: os dados em uma região paralela são individuais para cada thread, com este recurso cada thread terá uma cópia local e será utilizada como uma variável temporária. Uma variável não é inicializada e o valor não é mantido para o uso fora da região paralela. Por padrão, os contadores no "construção do laço OpenMP" (OpenMP loop constructs) são individuais. default: permite ao programador declarar a extensão do default data scoping (padrão de dados) que será compartilhado (ou nenhuma região) na região paralela, em C/C++, ou shared, firstprivate ou none para Fortran. A opção none força o programador a declarar cada variável na região paralela utilizando as cláusulas atribuídas ao compartilhamento de dados (data sharing attribute clauses). firstprivate: como private exceto ao inicializar pelo valor original. lasprivate: como private exceto que o valor original é atualizado depois da construção. reduction: um caminho seguro de juntar todas as threads após a construção.

Sincronização

critical section: o código incluso será executado por somente um thread por vez e não simultaneamente executado por múltiplos threads. É frequentemente usado para proteger os dados compartilhados das race conditions. atomic: semelhante à critical section, mas informamos ao compilador para usar instruções especias de hardware para um melhor desempenho. Os compiladores podem optar por ignorar essa sugestão dos usuários e usar a critical section em vez da atomic. ordered: o bloco estruturado é executado na ordem em que as iterações seriam executadas em um loop sequencial. barrier: cada thread espera até que todos os outros threads de um grupo tenham alcançado este ponto. Uma construção de partilha tem uma barreira de sincronização implícita no final. nowait: especifica quais threads podem completar sua instrução sem esperar todos os outros threads do grupo para concluir. Na ausência de tal cláusula acontece o mesmo da barrier.

Programação

schedule (type chunk): É útil se a construção de partilha for um do-loop ou for-loop. A iteração, ou as iterações, na construção de partilha são atribuídas para as threads de acordo com o método programado definido nesta cláusula. Os três tipos de programações são:

1. static: Aqui, a todos os threads são atribuídas as iteração antes de executar o laço de repetição. As interações são divididas igualmente entre os threads por padrão. No entanto, especificando um inteiro para um parâmetro "chunk" fixará um número "chunk" de iterações sequenciadas a uma determinada lista de threads.

2. dynamic: Aqui, algumas das iterações são atribuídas a um número menor de threads. Após o término da iteração atribuída a um thread em particular, ele retorna para buscar uma das iterações restantes. O parâmetro "chunk" define o número de iterações sequenciais que são atribuídas a um thread por vez.

3. guided: Um grande "chunk" de iterações sequenciadas são atribuídos a cada thread dinamicamente (como acima). O tamanho do "chunk" diminui exponencialmente com cada atribuição sucessiva até um tamanho mínimo especificado no parâmetro chunk.

Comando if

if: Isto fará com que as threads paralelizem a tarefa se a condição for satisfeita. Caso contrário, o bloco de código é executado em série.

Inicialização

firstprivate: os dados são individuais para cada thread, mas inicializando o valor da variável usando o mesmo nome a partir da thread master. lastprivate: os dados são individuais para cada thread. Se a iteração atual for a última iteração no loop paralelizado, o valor desses dados individuais serão copiados para uma variável global, usando o mesmo nome fora da região paralela. Uma variável pode ser ambas, firstprivate e lastprivate. threadprivate: O dado é um dado global, mas é individual para cada região paralela durante a execução. A diferença entre threadprivate e private é que o escopo global é associado com o threadprivate e o valor é preservado além das regiões paralelas.

Cópia de dados

copyin: semelhante ao firstprivate para variáveis privadas, variáveis threadprivate não são inicializadas, a não ser usando copyin para passar o valor das variáveis globais correspondentes. O copyout é necessária porque o valor de uma variável threadprivate é mantida durante toda a execução do programa inteiro. copyprivate: usado como único apoio para a cópia dos valores dos dados a partir de objetos particulares em uma thread (a thread única) para os objetos correspondentes em outras threads do grupo.

Redução

reduction(operador | intrinsic: list) a variável tem uma copia local em cada thread, mas os valores das copias locais são resumidos (reduzidos) em uma variavel global compartilhada. Isso é muito útil se uma operação particular (especificada em "operador" para essa clausula particular) em um datatype que roda interativelmente então esse valor em uma interação particular depende do valor da interação anterior. Basicamente, os passos que mais dão importância ao incremento operacional são paralelizados, mas os threads reúnem-se e esperam antes de fazer o update dos datatype, depois incrementa o datatype em ordem para evitar perda de dados. Isso seria requerido paralelizando funções de integração numérica e equações diferenciadas, como um exemplo comum.

Outros

Flush: O valor desta variável é restaurado a partir do registro da memória para ser usado fora da parte paralela.