Paralelismo de loop no OpenMP
Parâmetros
Cláusula | Parâmetro |
---|---|
privado |
Lista separada por vírgulas de variáveis privadas |
firstprivate |
Como private , mas inicializado com o valor da variável antes de entrar no loop |
lastprivate |
Como private , mas a variável obterá o valor correspondente à última iteração do loop na saída |
redução |
operador de redução : lista separada por vírgulas de variáveis de redução correspondentes |
horário |
static , dynamic , guided , auto ou runtime com um tamanho de bloco opcional após uma vírgula para os 3 primeiros |
colapso |
Número de loops perfeitamente aninhados para recolher e paralelizar juntos |
pedido |
Diz que algumas partes do loop precisarão ser mantidas em ordem (essas partes serão identificadas especificamente com algumas cláusulas ordenadas dentro do corpo do loop) |
agora |
Remova a barreira implícita existente por padrão no final da construção do loop |
O significado da cláusula schedule
é o seguinte:
static[,chunk]
: Distribua estaticamente (o que significa que a distribuição é feita antes de entrar no loop) as iterações do loop em lotes de tamanhochunk
de forma round-robin. Sechunk
não for especificado, então os pedaços são os mais uniformes possíveis e cada thread recebe no máximo um deles.dynamic[,chunk]
: Distribua as iterações de loop entre os encadeamentos por lotes de tamanhochunk
com uma política de ordem de chegada, até que nenhum lote permaneça. Se não for especificado,chunk
é definido como 1guided[,chunk]
: comodynamic
, mas com lotes cujos tamanhos ficam cada vez menores, até 1auto
: Deixe o compilador e/ou a biblioteca de tempo de execução decidir o que é mais adequadoruntime
: Adiar a decisão em tempo de execução por meio da variável de ambienteOMP_SCHEDULE
. Se em tempo de execução a variável de ambiente não estiver definida, o agendamento padrão será usado
O padrão para schedule
é definição de implementação. Em muitos ambientes é estático
, mas também pode ser dinâmico
ou pode muito bem ser automático
. Portanto, tenha cuidado para que sua implementação não dependa implicitamente dela sem defini-la explicitamente.
Nos exemplos acima, usamos a forma fundida parallel for
ou parallel do
. No entanto, a construção do loop pode ser usada sem fundi-la com a diretiva parallel
, na forma de uma diretiva autônoma #pragma omp for [...]
ou !$omp do [...]
dentro de um região paralela
.
Somente para a versão Fortran, a(s) variável(is) de índice de loop(s) do(s) loop(s) paralisado(s) é(são) sempre privado
por padrão. Portanto, não há necessidade de declará-los explicitamente como privados
(embora isso não seja um erro).
Para a versão C e C++, os índices de loop são como quaisquer outras variáveis. Portanto, se seu escopo se estender para fora do(s) loop(s) paralelizado(s) (ou seja, se eles não forem declarados como for ( int i = ...)
mas sim como int i; ... for ( i = ... )
então eles precisam ser declarados como privados
.
Exemplo típico em C
#include <stdio.h>
#include <math.h>
#include <omp.h>
#define N 1000000
int main() {
double sum = 0;
double tbegin = omp_get_wtime();
#pragma omp parallel for reduction( +: sum )
for ( int i = 0; i < N; i++ ) {
sum += cos( i );
}
double wtime = omp_get_wtime() - tbegin;
printf( "Computing %d cosines and summing them with %d threads took %fs\n",
N, omp_get_max_threads(), wtime );
return sum;
}
Neste exemplo, apenas calculamos 1 milhão de cossenos e somamos seus valores em paralelo. Também cronometramos a execução para ver se a paralelização tem algum efeito no desempenho. Finalmente, como medimos o tempo, temos que ter certeza de que o compilador não otimizará o trabalho que fizemos, então fingimos usar o resultado apenas retornando-o.
Mesmo exemplo em Fortran
program typical_loop
use omp_lib
implicit none
integer, parameter :: N = 1000000, kd = kind( 1.d0 )
real( kind = kd ) :: sum, tbegin, wtime
integer :: i
sum = 0
tbegin = omp_get_wtime()
!$omp parallel do reduction( +: sum )
do i = 1, N
sum = sum + cos( 1.d0 * i )
end do
!$omp end parallel do
wtime = omp_get_wtime() - tbegin
print "( 'Computing ', i7, ' cosines and summing them with ', i2, &
& ' threads took ', f6.4,'s' )", N, omp_get_max_threads(), wtime
if ( sum > N ) then
print *, "we only pretend using sum"
end if
end program typical_loop
Aqui novamente calculamos e acumulamos 1 milhão de cossenos. Cronometramos o loop e, para evitar a otimização indesejada do compilador, fingimos usar o resultado.
Compilando e executando os exemplos
Em uma máquina Linux de 8 núcleos usando o GCC versão 4.4, os códigos C podem ser compilados e executados da seguinte maneira:
$ gcc -std=c99 -O3 -fopenmp loop.c -o loopc -lm
$ OMP_NUM_THREADS=1 ./loopc
Computing 1000000 cosines and summing them with 1 threads took 0.095832s
$ OMP_NUM_THREADS=2 ./loopc
Computing 1000000 cosines and summing them with 2 threads took 0.047637s
$ OMP_NUM_THREADS=4 ./loopc
Computing 1000000 cosines and summing them with 4 threads took 0.024498s
$ OMP_NUM_THREADS=8 ./loopc
Computing 1000000 cosines and summing them with 8 threads took 0.011785s
Para a versão Fortran, fornece:
$ gfortran -O3 -fopenmp loop.f90 -o loopf
$ OMP_NUM_THREADS=1 ./loopf
Computing 1000000 cosines and summing them with 1 threads took 0.0915s
$ OMP_NUM_THREADS=2 ./loopf
Computing 1000000 cosines and summing them with 2 threads took 0.0472s
$ OMP_NUM_THREADS=4 ./loopf
Computing 1000000 cosines and summing them with 4 threads took 0.0236s
$ OMP_NUM_THREADS=8 ./loopf
Computing 1000000 cosines and summing them with 8 threads took 0.0118s
Adição de dois vetores usando OpenMP paralelo para construção
void parallelAddition (unsigned N, const double *A, const double *B, double *C)
{
unsigned i;
#pragma omp parallel for shared (A,B,C,N) private(i) schedule(static)
for (i = 0; i < N; ++i)
{
C[i] = A[i] + B[i];
}
}
Este exemplo adiciona dois vetores (A
e B
em C
) gerando uma equipe de threads (especificada pela variável de ambiente OMP_NUM_THREADS
, por exemplo) e atribuindo a cada thread um pedaço de trabalho (neste exemplo, atribuído estaticamente através da expressão schedule(static)
).
Veja a seção de comentários com respeito à opcionalidade private(i)
.