Compartilhar via


3. Funções da biblioteca em tempo de execução

Esta seção descreve as funções de biblioteca em tempo de execução do OpenMP C e C++. O cabeçalho <omp.h> declara dois tipos, várias funções que podem ser usadas para controlar e consultar o ambiente de execução paralela e funções de bloqueio que podem ser usadas para sincronizar o acesso aos dados.

O tipo omp_lock_t é um tipo de objeto capaz de representar que um bloqueio está disponível ou que um thread possui um bloqueio. Esses bloqueios são chamados de bloqueios simples.

O tipo omp_nest_lock_t é um tipo de objeto capaz de representar se um bloqueio está disponível ou a identidade do thread que possui o bloqueio e uma contagem de aninhamento (descrito abaixo). Esses bloqueios são chamados de bloqueios empilháveis.

As funções de biblioteca são funções externas com a vinculação "C".

As descrições neste capítulo são divididas nos seguintes tópicos:

3.1 Funções do ambiente de execução

As funções descritas nesta seção afetam e monitoram threads, processadores e o ambiente paralelo:

3.1.1 Função omp_set_num_threads

A função omp_set_num_threads define o número padrão de threads a serem usados para regiões paralelas posteriores que não especificam uma cláusula num_threads. A formato funciona da seguinte maneira:

#include <omp.h>
void omp_set_num_threads(int num_threads);

O valor do parâmetro num_threads deve ser um inteiro positivo. Seu efeito depende se o ajuste dinâmico do número de threads está habilitado. Para um conjunto abrangente de regras sobre a interação entre a variável de ambiente omp_set_num_threads e o ajuste dinâmico de threads, consulte a seção 2.3.

Essa função tem os efeitos descritos acima quando chamados de uma parte do programa em que a função omp_in_parallel retorna zero. Se for chamado de uma parte do programa em que a função omp_in_parallel retorna um valor diferente de zero, o comportamento dessa função é indefinido.

Essa chamada tem precedência sobre a variável de ambiente OMP_NUM_THREADS. O valor padrão para o número de threads, que pode ser estabelecido chamando omp_set_num_threads ou definindo a variável de ambiente OMP_NUM_THREADS, pode ser substituído explicitamente em uma única diretiva parallel especificando a cláusula num_threads.

Para obter mais informações, confira omp_set_dynamic.

Referências-cruzadas

3.1.2 Função omp_get_num_threads

A função omp_get_num_threads retorna o número de threads atualmente na equipe executando a região paralela da qual é chamada. A formato funciona da seguinte maneira:

#include <omp.h>
int omp_get_num_threads(void);

A cláusula num_threads, a função omp_set_num_threads e a variável de ambiente OMP_NUM_THREADS controlam o número de threads em uma equipe.

Se o número de threads não tiver sido definido explicitamente pelo usuário, o padrão será definido pela implementação. Essa função associa-se à diretiva de fechamento parallel mais próxima. Se chamado de uma parte serial de um programa ou de uma região paralela aninhada serializada, essa função retornará 1.

Para obter mais informações, confira omp_set_dynamic.

Referências-cruzadas

3.1.3 Função omp_get_max_threads

A função omp_get_max_threads retorna um inteiro que tem a garantia de ser pelo menos tão grande quanto o número de threads que seriam usados para formar uma equipe se uma região paralela sem uma cláusula num_threads fosse vista naquele ponto do código. A formato funciona da seguinte maneira:

#include <omp.h>
int omp_get_max_threads(void);

O seguinte expressa um limite inferior no valor de omp_get_max_threads:

threads-used-for-next-team<= omp_get_max_threads

Observe que, se outra região paralela usar a cláusula num_threads para solicitar um número específico de threads, a garantia no limite inferior do resultado de omp_get_max_threads não será mais mantida.

O valor retornado da função omp_get_max_threads pode ser usado para alocar dinamicamente armazenamento suficiente para todos os threads na equipe formada na próxima região paralela.

Referências-cruzadas

3.1.4 Função omp_get_thread_num

A função omp_get_thread_num retorna o número do thread, dentro de sua equipe, do thread que executa a função. O número do thread está entre 0 e omp_get_num_threads()-1, inclusive. O thread mestre da equipe é o thread 0.

A formato funciona da seguinte maneira:

#include <omp.h>
int omp_get_thread_num(void);

Se chamado de uma região serial, omp_get_thread_num retorna 0. Se for chamada de dentro de uma região paralela aninhada serializada, essa função retornará 0.

Referências-cruzadas

3.1.5 Função omp_get_num_procs

A função omp_get_num_procs retorna o número de processadores que estão disponíveis para o programa no momento em que a função é chamada. A formato funciona da seguinte maneira:

#include <omp.h>
int omp_get_num_procs(void);

3.1.6 Função omp_in_parallel

A função omp_in_parallel retornará um valor diferente de zero se for chamada dentro da extensão dinâmica de uma região paralela em execução em paralelo; caso contrário, retornará 0. A formato funciona da seguinte maneira:

#include <omp.h>
int omp_in_parallel(void);

Essa função retorna um valor diferente de zero quando chamado de dentro de uma região em execução em paralelo, incluindo regiões aninhadas que são serializadas.

3.1.7 Função omp_set_dynamic

A função omp_set_dynamic habilita ou desabilita o ajuste dinâmico do número de threads disponíveis para execução de regiões paralelas. A formato funciona da seguinte maneira:

#include <omp.h>
void omp_set_dynamic(int dynamic_threads);

Se dynamic_threads for avaliado como um valor diferente de zero, o número de threads usados para executar regiões paralelas futuras poderá ser ajustado automaticamente pelo ambiente em tempo de execução para usar melhor os recursos do sistema. Como consequência, o número de threads especificados pelo usuário é a contagem máxima de threads. O número de threads na equipe que executa uma região paralela permanece fixo durante a região paralela e é relatado pela função omp_get_num_threads.

Se dynamic_threads for avaliado como 0, o ajuste dinâmico será desabilitado.

Essa função tem os efeitos descritos acima quando chamados de uma parte do programa em que a função omp_in_parallel retorna zero. Se for chamado de uma parte do programa em que a função omp_in_parallel retorna um valor diferente de zero, o comportamento dessa função é indefinido.

Uma chamada para omp_set_dynamic tem precedência sobre a variável de ambiente OMP_DYNAMIC.

O padrão para o ajuste dinâmico de threads é definido pela implementação. Como resultado, os códigos de usuário que dependem de um número específico de threads para execução correta devem desabilitar explicitamente threads dinâmicos. As implementações não são necessárias para fornecer a capacidade de ajustar dinamicamente o número de threads, mas elas são necessárias para fornecer a interface para dar suporte à portabilidade em todas as plataformas.

Específico da Microsoft

O suporte atual de omp_get_dynamic e omp_set_dynamic é o seguinte:

O parâmetro de entrada para omp_set_dynamic não afeta a política de threading e não altera o número de threads. omp_get_num_threads sempre retorna o número definido pelo usuário, se ele estiver definido ou o número de thread padrão. Na implementação atual da Microsoft, omp_set_dynamic(0) desativa o threading dinâmico para que o conjunto de threads existente possa ser reutilizado para a região paralela a seguir. omp_set_dynamic(1) ativa o threading dinâmico descartando o conjunto existente de threads e criando um novo conjunto para a próxima região paralela. O número de threads no novo conjunto é o mesmo que o conjunto antigo e se baseia no valor retornado de omp_get_num_threads. Portanto, para obter o melhor desempenho, use omp_set_dynamic(0) para reutilizar os threads existentes.

Referências-cruzadas

3.1.8 Função omp_get_dynamic

A função omp_get_dynamic retornará um valor diferente de zero se o ajuste dinâmico de threads estiver habilitado e retornar 0 caso contrário. A formato funciona da seguinte maneira:

#include <omp.h>
int omp_get_dynamic(void);

Se a implementação não implementar o ajuste dinâmico do número de threads, essa função sempre retornará 0. Para obter mais informações, confira omp_set_dynamic.

Referências-cruzadas

  • Para obter uma descrição do ajuste dinâmico do thread, consulte omp_set_dynamic.

3.1.9 Função omp_set_nested

A função omp_set_nested habilita ou desabilita o paralelismo aninhado. A formato funciona da seguinte maneira:

#include <omp.h>
void omp_set_nested(int nested);

Se aninhado for avaliado como 0, o paralelismo aninhado será desabilitado, que é o padrão, e as regiões paralelas aninhadas serão serializadas e executadas pelo thread atual. Caso contrário, o paralelismo aninhado será habilitado e regiões paralelas aninhadas poderão implantar threads adicionais para formar equipes aninhadas.

Essa função tem os efeitos descritos acima quando chamados de uma parte do programa em que a função omp_in_parallel retorna zero. Se for chamado de uma parte do programa em que a função omp_in_parallel retorna um valor diferente de zero, o comportamento dessa função é indefinido.

Essa chamada tem precedência sobre a variável de ambiente OMP_NESTED.

Quando o paralelismo aninhado é habilitado, o número de threads usados para executar regiões paralelas aninhadas é definido pela implementação. Como resultado, implementações em conformidade com OpenMP têm permissão para serializar regiões paralelas aninhadas mesmo quando o paralelismo aninhado está habilitado.

Referências-cruzadas

3.1.10 Função omp_get_nested

A função omp_get_nested retornará um valor diferente de zero se o paralelismo aninhado estiver habilitado e 0 se estiver desabilitado. Para obter mais informações sobre o paralelismo aninhado, veja omp_set_nested. A formato funciona da seguinte maneira:

#include <omp.h>
int omp_get_nested(void);

Se uma implementação não implementar paralelismo aninhado, essa função sempre retornará 0.

3.2 Funções de bloqueio

As funções descritas nesta seção manipulam bloqueios usados para sincronização.

Para as funções a seguir, a variável de bloqueio deve ter o tipo omp_lock_t. Essa variável só deve ser acessada por meio dessas funções. Todas as funções de bloqueio exigem um argumento que tenha um ponteiro para o tipo omp_lock_t.

Para as funções a seguir, a variável de bloqueio deve ter o tipo omp_nest_lock_t. Essa variável só deve ser acessada por meio dessas funções. Todas as funções de bloqueio exigem um argumento que tenha um ponteiro para o tipo omp_nest_lock_t.

As funções de bloqueio OpenMP acessam a variável de bloqueio de forma que sempre leiam e atualizem o valor mais atual da variável de bloqueio. Portanto, não é necessário que um programa OpenMP inclua diretivas explícitas flush para garantir que o valor da variável de bloqueio seja consistente entre diferentes threads. (Pode haver a necessidade de diretivas flush para tornar os valores de outras variáveis consistentes.)

3.2.1 Funções omp_init_lock e omp_init_nest_lock

Essas funções fornecem o único meio de inicializar um bloqueio. Cada função inicializa o bloqueio associado ao bloqueio de parâmetro para uso em chamadas futuras. A formato funciona da seguinte maneira:

#include <omp.h>
void omp_init_lock(omp_lock_t *lock);
void omp_init_nest_lock(omp_nest_lock_t *lock);

O estado inicial é desbloqueado (ou seja, nenhum thread possui o bloqueio). Para um bloqueio aninhável, a contagem inicial de aninhamento é zero. Não é compatível chamar qualquer uma dessas rotinas com uma variável de bloqueio que já foi inicializada.

3.2.2 Funções omp_destroy_lock e omp_destroy_nest_lock

Essas funções garantem que o bloqueio de variável apontado para bloqueio não seja inicializado. A formato funciona da seguinte maneira:

#include <omp.h>
void omp_destroy_lock(omp_lock_t *lock);
void omp_destroy_nest_lock(omp_nest_lock_t *lock);

Não é compatível chamar qualquer uma dessas rotinas com uma variável de bloqueio que já foi desbloqueada ou teve a inicialização cancelada.

3.2.3 Funções omp_set_lock e omp_set_nest_lock

Cada uma dessas funções bloqueia o thread executando a função até que o bloqueio especificado esteja disponível e, em seguida, define o bloqueio. Um bloqueio simples estará disponível se estiver desbloqueado. Um bloqueio aninhável estará disponível se ele estiver desbloqueado ou se ele já pertencer ao thread que executa a função. A formato funciona da seguinte maneira:

#include <omp.h>
void omp_set_lock(omp_lock_t *lock);
void omp_set_nest_lock(omp_nest_lock_t *lock);

Para um bloqueio simples, o argumento para a função omp_set_lock deve apontar para uma variável de bloqueio inicializada. A propriedade do bloqueio é concedida ao thread que executa a função.

Para um bloqueio aninhável, o argumento para a função omp_set_nest_lock deve apontar para uma variável de bloqueio inicializada. A contagem de aninhamento é incrementada e o thread é concedido ou mantém a propriedade do bloqueio.

3.2.4 Funções omp_unset_lock e omp_unset_nest_lock

Essas funções fornecem os meios de liberar a propriedade de um bloqueio. A formato funciona da seguinte maneira:

#include <omp.h>
void omp_unset_lock(omp_lock_t *lock);
void omp_unset_nest_lock(omp_nest_lock_t *lock);

O argumento para cada uma dessas funções deve apontar para uma variável de bloqueio inicializada pertencente ao thread que executa a função. O comportamento será indefinido se o thread não possuir esse bloqueio.

Para um bloqueio simples, a função omp_unset_lock libera o thread executando a função a partir da propriedade do bloqueio.

Para um bloqueio aninhável, a função omp_unset_nest_lock decrementa a contagem de aninhamento e libera o thread executando a função da propriedade do bloqueio se a contagem resultante for zero.

3.2.5 Funções omp_test_lock e omp_test_nest_lock

Essas funções tentam definir um bloqueio, mas não bloqueiam a execução do thread. A formato funciona da seguinte maneira:

#include <omp.h>
int omp_test_lock(omp_lock_t *lock);
int omp_test_nest_lock(omp_nest_lock_t *lock);

O argumento deve apontar para uma variável de bloqueio inicializada. Essas funções tentam definir um bloqueio da mesma maneira que omp_set_lock e omp_set_nest_lock, exceto que não bloqueiam a execução do thread.

Para um bloqueio simples, a função omp_test_lock retornará um valor diferente de zero se o bloqueio for definido com êxito; caso contrário, retornará zero.

Para um bloqueio aninhável, a função omp_test_nest_lock retornará a nova contagem de aninhamento se o bloqueio for definido com êxito; caso contrário, retornará zero.

3.3 Rotinas de tempo

As funções descritas nesta seção dão suporte a um temporizador portátil do relógio de parede:

  • A função omp_get_wtime retorna o tempo decorrido do relógio de parede.
  • A função omp_get_wtick retorna segundos entre tiques de relógio sucessivos.

3.3.1 Função omp_get_wtime

A função omp_get_wtime retorna um valor de ponto flutuante de precisão dupla igual ao tempo decorrido do relógio de parede em segundos desde algum "tempo no passado". O "tempo no passado" real é arbitrário, mas é garantido que não seja alterado durante a execução do programa de aplicativo. A formato funciona da seguinte maneira:

#include <omp.h>
double omp_get_wtime(void);

Espera-se que a função seja usada para medir tempos decorridos, conforme mostrado no exemplo a seguir:

double start;
double end;
start = omp_get_wtime();
... work to be timed ...
end = omp_get_wtime();
printf_s("Work took %f sec. time.\n", end-start);

Os tempos retornados são "tempos por thread", o que significa que eles não são necessários para serem globalmente consistentes em todos os threads que participam de um aplicativo.

3.3.2 Função omp_get_wtick

A função omp_get_wtick retorna um valor de ponto flutuante de precisão dupla igual ao número de segundos entre tiques de relógio sucessivos. A formato funciona da seguinte maneira:

#include <omp.h>
double omp_get_wtick(void);