OpenMP

OpenMP

logotipo
Dados básicos

desenvolvedor Lista de compiladores compatíveis
Versão atual  5.1
(novembro de 2020)
Versão preliminar atual -
sistema operacional Linux , Unix , Microsoft Windows NT
linguagem de programação C , C ++
categoria API
Licença desconhecido (aberto)
Falando alemão não
openmp.org/

OpenMP (Open Multi-Processing) é um conjunto desde 1997 por vários hardware - e compilador fabricantes desenvolveram Programming Interface (API) para a programação de memória compartilhada em C ++ , C e Fortran em multiprocessador - computadores .

OpenMP paraleliza programas no nível de loops que são executados em threads , e isso difere de outras abordagens (por exemplo, MPI ), em que processos inteiros são executados em paralelo e interagem trocando mensagens.

O padrão OpenMP define diretivas de compilador especiais para essa finalidade, que então os instruem, por exemplo, B. distribuir o processamento de um loop for por vários threads ou processadores. Como alternativa, existem funções de biblioteca e variáveis ​​de ambiente para programação OpenMP.

OpenMP é destinado ao uso em sistemas com memória principal compartilhada (máquinas de "memória compartilhada") (os chamados sistemas UMA e NUMA ), enquanto outras abordagens, como interface de passagem de mensagem , PVM são mais propensos a ser usados ​​em multicomputadores ("memória distribuída "máquinas) colocar. Em supercomputadores modernos, OpenMP e MPI (Message Passing Interface) são freqüentemente usados ​​juntos. Os processos OpenMP, que são trocados via MPI, são executados em clientes individuais de memória compartilhada.

Uma propriedade do OpenMP é que (com algumas exceções) os programas também são executados corretamente se o compilador não conhece as instruções do OpenMP (veja o exemplo abaixo) e as avalia como comentários (ou seja, as ignora). A razão para isso é que uma divisão de forloop para várias threads com OpenMP também pode ser processada sequencialmente com uma única thread.

Principais ingredientes

Os principais componentes do OpenMP são construções para geração de threads, distribuição de carga em várias threads , gerenciamento do escopo dos dados, sincronização, rotinas de tempo de execução e variáveis ​​de ambiente. A geração de thread: omp parallel divide o programa (a thread original) em várias threads de forma que a parte do programa contida na construção seja processada em paralelo. O encadeamento original é chamado de “encadeamento mestre” e possui o ID “0”.

Exemplo: Saídas “Hello, World!” Várias vezes usando vários threads (cada thread gera uma saída).

#include <stdio.h>

int main() {
#pragma omp parallel
    puts("Hallo, Welt!\n");

    return 0;
}

As construções de balanceamento de carga determinam como a carga de trabalho independente e simultânea é distribuída entre threads paralelos. Omp for e omp do divide loop é executado (se possível) uniformemente em todos os threads (divisão de área, "particionamento de dados"). As seções distribuem partes do programa sucessivas, mas independentes, por vários threads (divisão de função, "particionamento de função").

Exemplo: Inicializa uma grande tabela ("array") em paralelo, com cada thread inicializando uma parte (divisão de área).

#define N 100000

int main() {
    int a[N];

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

    return 0;
}

A administração de uma área de validade para dados pode ser influenciada por diferentes programações. Com a programação de memória compartilhada, a maioria dos dados é inicialmente visível em todos os threads. Alguns programas requerem dados privados, ou seja, apenas visíveis para um segmento, e a troca explícita de valores entre as seções sequenciais e paralelas. As chamadas cláusulas de dados são usadas para isso no OpenMP . O tipo compartilhado descreve que os dados são visíveis e podem ser alterados por todos os threads. Eles estão no mesmo local de memória para todos os threads. Sem mais informações, os dados são dados comuns. A única exceção a isso são as variáveis ​​de loop . Com private , cada thread tem suas próprias cópias desses dados, que não são inicializadas. Os valores não são preservados fora da seção paralela. O tipo privado pode ser subdividido em primeiro- privado e último-privado , que também podem ser combinados. No primeiro caso, os dados são privados, com a diferença de que são inicializados com o último valor antes da seção paralela. Lastprivate difere porque o thread que executa a última iteração copia o valor da seção paralela.

Há também o tipo thread-privado para dados globais, que é, entretanto, tratado como privado na seção do programa paralelo. O valor global é preservado na seção paralela. Copyin é análogo a firstprivate para dados privados , mas para dados privados de thread que não são inicializados. Com o Copyin , o valor global é explicitamente transferido para os dados privados. Uma cópia não é necessária, pois o valor global é preservado. Com a redução de tipo , os dados são privados, mas são resumidos (reduzidos) a um valor global no final. Por exemplo, a soma de todos os elementos de uma matriz pode ser determinada em paralelo.

Várias construções são usadas para sincronizar os threads, como: B. Seção crítica , em que a seção de programa incluída é executada por todos os threads, mas nunca ao mesmo tempo ou Barreira que marca uma barreira, onde cada thread espera até que todos os outros threads no grupo também tenham atingido a barreira. O comando atômico é análogo à seção crítica , mas com uma nota para o compilador usar funções especiais de hardware. O compilador não está vinculado a esta nota; ele pode ignorá-la. Faz sentido usar o atomic para atualização exclusiva de dados. Flush marca um ponto de sincronização no qual uma imagem de memória consistente deve ser criada. Os dados privados são gravados de volta na memória principal. Simples significa que a parte incluída do programa só é executada pela thread que a atinge primeiro, o que implica uma barreira no final do bloco e, portanto, é equivalente a uma barreira em um determinado ponto. O mestre é análogo ao único, com a diferença de que a parte do programa incluída é executada pelo thread mestre e nenhuma barreira está implícita no final do bloco.

Nesses processos, as rotinas de tempo de execução são usadas para determinar, por exemplo, o número de threads durante o tempo de execução e para determinar se o programa está atualmente no estado paralelo ou sequencial .

Nesse contexto, as variáveis ​​de ambiente fornecem informações como o ID do encadeamento. A execução de programas OpenMP pode ser alterada alterando especificamente certas variáveis ​​de ambiente. Por exemplo, o número de threads e a paralelização do loop podem ser influenciados no tempo de execução.

Código de amostra

O código a seguir ilustra a execução paralela de um loop for usando OpenMP. Dependendo do número de threads envolvidos, o loop é dividido em pequenas seções, cada uma atribuída a um thread. Isso garante que todos os threads computem ao mesmo tempo.

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

int main() {
    omp_set_num_threads(4);

#pragma omp parallel for
    for (int i = 0; i < 4; ++i) {
        const int id = omp_get_thread_num();

        printf("Hello World from thread %d\n", id);

        // Nur im Master-Thread ausführen
        if (id == 0)
            printf("There are %d threads\n", omp_get_num_threads());
    }

    return 0;
}

Ao compilar, você deve dizer ao compilador que ele deve seguir as instruções do pragma e incluir as bibliotecas necessárias para as funções omp. Isso funciona com gccou clangpor meio da opção -fopenmp.

% gcc -fopenmp example.c -o example
% ./example
Hello World from thread 3
Hello World from thread 0
Hello World from thread 1
Hello World from thread 2
There are 4 threads

Em vez de especificar o número de threads no programa, isso também pode ser especificado em tempo de execução. Para fazer isso, defina a variável de ambiente OMP_NUM_THREADScom o valor desejado.

% OMP_NUM_THREADS=4 ./example
Hello World from thread 3
Hello World from thread 0
Hello World from thread 1
Hello World from thread 2
There are 4 threads

Implementação

O OpenMP está integrado na maioria dos compiladores.

  • Microsoft Visual C ++ 2005, 2008 e 2010 (Professional, Team System, Premium e Ultimate Edition),
  • Intel Parallel Studio para diferentes processadores (OpenMP 3.1 da versão 13),
  • GCC da versão 4.2 (OpenMP 4.0 da versão 5.0),
  • Clang / LLVM (OpenMP 3.1 da versão 3.6.1),
  • Compilador e ferramentas do Oracle Solaris Studio para Solaris OS (UltraSPARC e x86 / x64) e Linux,
  • Compiladores Fortran, C e C ++ do Portland Group (OpenMP 2.5),
  • Gfortran ,
  • Compilador IBM XL C / C ++,
  • Compilador Nanos
  • Pelles C (OpenMP 3.1 da versão 8)

Links da web

Evidência individual

  1. https://gcc.gnu.org/gcc-5/changes.html