1. Introdução
Este documento especifica uma coleção de diretivas do compilador, funções de biblioteca e variáveis de ambiente que você pode usar para especificar o paralelismo de memória compartilhada em programas C e C++. A funcionalidade descrita neste documento é coletivamente conhecida como Interface do Programa de Aplicativo (API) do OpenMP C/C++. O objetivo dessa especificação é fornecer um modelo para programação paralela que permita que um programa seja portátil em arquiteturas de memória compartilhada de diferentes fornecedores. Compiladores de muitos fornecedores são compatíveis com a API C/C++ do OpenMP. Mais informações sobre o OpenMP, incluindo a Interface do Programa de Aplicativo OpenMP Fortran, podem ser encontradas no seguinte site:
As diretivas, as funções de biblioteca e as variáveis de ambiente definidas neste documento permitem que você crie e gerencie programas paralelos, permitindo a portabilidade. As diretivas estendem o modelo de programação sequencial C e C++ com constructos de vários dados de programa único (SPMD), constructos de compartilhamento de trabalho e constructos de sincronização. Eles também são compatíveis com o compartilhamento e privatização de dados. Os compiladores compatíveis com a API de OpenMP C e C++ incluirão uma opção de linha de comando que ativa e permite a interpretação de todas as diretivas do compilador de OpenMP.
1.1 Escopo
Essa especificação abrange apenas a paralelização direcionada pelo usuário, na qual você define explicitamente quais ações o compilador e o sistema de tempo de execução tomam para executar o programa em paralelo. As implementações de C e C++ do OpenMP não são necessárias para verificar se há dependências, conflitos, deadlocks, condições de corrida ou outros problemas que resultam na execução incorreta do programa. Você é responsável por garantir que o aplicativo que usa as construções de API C e C++ do OpenMP seja executado corretamente. As diretivas e a paralelização automática geradas pelo compilador para auxiliar essa paralelização não são abordadas neste documento.
1.2 Definição de termos
Os seguintes termos são usados no documento:
barrier
Um ponto de sincronização que todos os threads de uma equipe devem alcançar. Cada thread aguarda até que todos os threads da equipe cheguem neste ponto. Há barreiras explícitas identificadas por diretivas e barreiras implícitas criadas pela implementação.
constructo
Um constructo é uma instrução. Ele consiste em uma diretiva, seguida por um bloco estruturado. Algumas diretivas não fazem parte de uma construção. (Consulte openmp-directive no apêndice C).
diretiva
Um C ou C++
#pragma
seguido peloomp
identificador, outro texto e uma nova linha. A diretiva especifica o comportamento do programa.extensão dinâmica
Todas as instruções na extensão lexical, além de qualquer instrução dentro de uma função executada como resultado da execução de instruções dentro da extensão lexical. Uma extensão dinâmica também é conhecida como uma região.
extensão lexical
Instruções mantidas lexicamente em um bloco estruturado.
thread mestre
O thread que cria uma equipe quando uma região paralela é inserida.
região paralela
Instruções que se associam a uma construção paralela do OpenMP e podem ser executadas por muitos threads.
private
Uma variável privada nomeia um bloco de armazenamento exclusivo para o thread que faz a referência. Há várias maneiras de especificar que uma variável é privada: uma definição dentro de uma região paralela, uma
threadprivate
diretiva, umaprivate
,firstprivate
,lastprivate
ou cláusulareduction
, ou o uso da variável como umafor
variável de controle de loop em umfor
loop imediatamente seguindo uma diretivafor
ouparallel for
.region
Uma extensão dinâmica.
região serial
Instruções executadas apenas pelo thread mestre fora da extensão dinâmica de qualquer região paralela.
serialize
Para executar uma construção paralela com:
uma equipe de threads que consiste em apenas um único thread (que é o thread mestre para essa construção paralela),
ordem serial de execução para as instruções dentro do bloco estruturado (a mesma ordem como se o bloco não fosse parte de uma construção paralela) e
nenhum efeito sobre o valor retornado por
omp_in_parallel()
(além dos efeitos de quaisquer construções paralelas aninhadas).
compartilhado
Uma variável compartilhada nomeia um único bloco de armazenamento. Todos os threads em uma equipe que acessam essa variável também acessam esse único bloco de armazenamento.
bloco estruturado
Um bloco estruturado é uma instrução (única ou composta) que tem uma única entrada e uma única saída. Se houver um salto para dentro ou para fora de uma instrução, essa instrução é um bloco estruturado. (Essa regra inclui uma chamada para
longjmp
(3C) ou o uso dethrow
, embora uma chamada paraexit
seja permitida.) Se sua execução sempre começar na abertura{
e sempre terminar no fechamento}
, uma instrução composta será um bloco estruturado. Uma instrução de expressão, uma instrução de seleção, uma instrução de iteração ou um blocotry
é um bloco estruturado se a instrução composta correspondente obtida colocando-a em{
e}
seria um bloco estruturado. Uma instrução de salto, uma instrução rotulada ou uma instrução de declaração não é um bloco estruturado.equipe
Um ou mais threads cooperando na execução de um constructo.
thread
Uma entidade de execução com um fluxo serial de controle, um conjunto de variáveis privadas e acesso a variáveis compartilhadas.
variável
Um identificador, opcionalmente qualificado por nomes de namespace, que nomeia um objeto.
1.3 Modelo de execução
O OpenMP usa o modelo de junção de fork da execução paralela. Embora esse modelo de junção de fork possa ser útil para resolver vários problemas, ele é personalizado para aplicativos baseados em matriz grande. O OpenMP destina-se a dar suporte a programas que são executados corretamente como programas paralelos (muitos threads de execução e uma biblioteca de suporte do OpenMP completa). Ele também é para programas que são executados corretamente como programas sequenciais (diretivas ignoradas e uma biblioteca simples de stubs do OpenMP). No entanto, é possível e permitido desenvolver um programa que não se comporte corretamente quando executado sequencialmente. Além disso, diferentes graus de paralelismo podem resultar em resultados numéricos diferentes devido a alterações na associação de operações numéricas. Por exemplo, uma redução de adição serial pode ter um padrão diferente de associações de adição do que uma redução paralela. Essas associações diferentes podem alterar os resultados da adição de ponto flutuante.
Um programa escrito com a API do OpenMP C/C++ inicia a execução como um único thread de execução chamado thread mestre. O thread mestre é executado em uma região serial até que a primeira construção paralela seja encontrada. Na API C/C++ do OpenMP, a diretiva parallel
constitui uma construção paralela. Quando um constructo paralelo é encontrado, o thread mestre cria uma equipe de threads e o mestre se torna mestre da equipe. Cada thread na equipe executa as instruções na extensão dinâmica de uma região paralela, exceto para as construções de compartilhamento de trabalho. Todos os threads na equipe devem encontrar constructos de compartilhamento de trabalho na mesma ordem e um ou mais threads executa as instruções dentro do bloco estruturado associado. A barreira implícita no final de uma construção de compartilhamento de trabalho sem uma cláusula nowait
é executada por todos os threads da equipe.
Se um thread modificar um objeto compartilhado, ele afetará não apenas seu próprio ambiente de execução, mas também os dos outros threads do programa. A modificação é garantida para ser concluída, do ponto de vista de outro thread, no próximo ponto de sequência (conforme definido na linguagem base) somente se o objeto for declarado volátil. Caso contrário, a modificação será garantida após a primeira modificação do thread. Em seguida, os outros threads (ou simultaneamente) veem uma diretiva flush
que especifica o objeto (implicitamente ou explicitamente). Quando as diretivas flush
que são implícitas por outras diretivas OpenMP não garantem a ordenação correta de efeitos colaterais, é responsabilidade do programador fornecer diretivas flush
adicionais e explícitas.
Após a conclusão da construção paralela, os threads na equipe são sincronizados em uma barreira implícita e apenas o thread mestre continua a execução. Qualquer número de constructos paralelos pode ser especificado em um único programa. Como resultado, um programa pode bifurcar e ingressar muitas vezes durante a execução.
A API C/C++ do OpenMP permite que os programadores usem diretivas em funções chamadas de dentro de constructos paralelos. As diretivas que não aparecem na extensão lexical de uma construção paralela, mas podem estar na extensão dinâmica, são chamadas de diretivas órfãs. Com diretivas órfãs, os programadores podem executar partes principais de seu programa em paralelo, com apenas alterações mínimas no programa sequencial. Com essa funcionalidade, você pode codificar construções paralelas nos níveis superiores da árvore de chamadas do programa e usar diretivas para controlar a execução em qualquer uma das funções chamadas.
Chamadas não sincronizadas para funções de saída C e C++ que gravam no mesmo arquivo podem resultar na saída na qual os dados gravados por threads diferentes aparecem em ordem não determinística. Da mesma forma, chamadas não sincronizadas para funções de entrada que leem do mesmo arquivo podem ler dados em ordem não determinística. O uso não sincronizado de E/S, de modo que cada thread acesse um arquivo diferente, produz os mesmos resultados que a execução serial das funções de E/S.
1.4 Conformidade
Uma implementação da API do OpenMP C/C++ é compatível com OpenMP se reconhecer e preservar a semântica de todos os elementos dessa especificação, conforme disposto nos Capítulos 1, 2, 3, 4 e no Apêndice C. Os apêndices A, B, D, E e F são apenas para fins de informações e não fazem parte da especificação. As implementações que incluem apenas um subconjunto da API não são compatíveis com OpenMP.
A API C e C++ do OpenMP é uma extensão para a linguagem base com suporte de uma implementação. Se a linguagem base não oferecer suporte a um constructo de idioma ou extensão que aparece neste documento, a implementação do OpenMP não será necessária para dar suporte a ele.
Todas as funções de biblioteca C e C++ padrão e funções internas (ou seja, funções das quais o compilador tem conhecimento específico) devem ser thread-safe. O uso não sincronizado de funções thread-safe por threads diferentes dentro de uma região paralela não produz um comportamento indefinido. No entanto, o comportamento pode não ser o mesmo do que em uma região serial. (Uma função de geração de número aleatório é um exemplo.)
A API do OpenMP C/C++ especifica que determinado comportamento é definido pela implementação. Uma implementação OpenMP em conformidade é necessária para definir e documentar seu comportamento nesses casos. Para obter uma lista de comportamentos definidos pela implementação, consulte o apêndice E.
1.5 Referências normativas
ISO/IEC 9899:1999, Tecnologia da Informação - Linguagens de Programação - C. Essa especificação da API do OpenMP refere-se ao ISO/IEC 9899:1999 como C99.
ISO/IEC 9899:1990, Tecnologia da Informação - Linguagens de Programação - C. Essa especificação da API do OpenMP refere-se ao ISO/IEC 9899:1990 como C90.
ISO/IEC 14882:1998, Tecnologia da informação - Linguagens de programação - C++. Essa especificação da API OpenMP refere-se ao ISO/IEC 14882:1998 como C++.
Quando essa especificação da API OpenMP se refere a C, a referência é feita ao idioma base com suporte da implementação.