Partilhar via


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:

https://www.openmp.org

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 pelo omp 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, uma private, firstprivate, lastprivateou cláusula reduction, ou o uso da variável como uma for variável de controle de loop em um for loop imediatamente seguindo uma diretiva for ou parallel 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 de throw, embora uma chamada para exit 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 bloco try é 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.

1.6 Organização