openmp
Bucle paralelismo en OpenMP
Buscar..
Parámetros
Cláusula | Parámetro |
---|---|
private | Lista de variables privadas separadas por comas |
firstprivate | Como private , pero inicializado con el valor de la variable antes de ingresar al bucle |
lastprivate | Como private , pero la variable obtendrá el valor correspondiente a la última iteración del bucle al salir |
reduction | operador de reducción : lista separada por comas de las variables de reducción correspondientes |
schedule | static , dynamic , guided , auto o en runtime con un tamaño de fragmento opcional después de un coma para los 3 primeros |
collapse | Número de bucles perfectamente anidados para colapsar y paralelizar juntos |
ordered | Indica que algunas partes del bucle deberán mantenerse en orden (estas partes se identificarán específicamente con algunas cláusulas ordered dentro del cuerpo del bucle) |
nowait | Elimine la barrera implícita existente de forma predeterminada al final de la construcción del bucle |
Observaciones
El significado de la cláusula de schedule
es el siguiente:
-
static[,chunk]
: distribuye estáticamente (lo que significa que la distribución se realiza antes de ingresar al bucle) las iteraciones de bucle en lotes de tamaño dechunk
de forma rotatoria. Si no se especifica elchunk
, entonces los trozos son lo más parejos posible y cada hilo obtiene como máximo uno de ellos. -
dynamic[,chunk]
: distribuye las iteraciones de bucle entre los hilos por lotes de tamaño dechunk
con una política de primer orden de llegada, hasta que no quede ningún lote. Si no se especifica, elchunk
se establece en 1 -
guided[,chunk]
: comodynamic
pero con lotes cuyos tamaños se hacen cada vez más pequeños, hasta 1 -
auto
: deje que el compilador y / o la biblioteca de tiempo de ejecución decidan cuál es la más adecuada -
runtime
: difiera la decisión en tiempo de ejecución mediante la variable de entornoOMP_SCHEDULE
. Si en el tiempo de ejecución la variable de entorno no está definida, se utilizará la programación predeterminada
El valor predeterminado para la schedule
es la implementación definida . En muchos entornos es static
, pero también puede ser dynamic
o bien podría ser auto
. Por lo tanto, tenga cuidado de que su implementación no dependa implícitamente de ella sin establecerla explícitamente.
En los ejemplos anteriores, utilizamos la forma fusionada parallel for
o parallel do
. Sin embargo, la construcción de bucle se puede usar sin fusionarla con la directiva parallel
, en forma de #pragma omp for [...]
o !$omp do [...]
directiva independiente dentro de una región parallel
.
Solo para la versión de Fortran, las variables de índice de bucle del (de los) bucle (s) paralizado (es) son siempre private
por defecto. Por lo tanto, no hay necesidad de declararlos explícitamente como private
(aunque hacerlo no es un error).
Para la versión C y C ++, los índices de bucle son como cualquier otra variable. Por lo tanto, si su alcance se extiende fuera del (los) bucle (s) paralelizado (es decir, si no se declaran como for ( int i = ...)
sino como int i; ... for ( i = ... )
entonces Tienen que ser declarados private
.
Ejemplo típico en 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;
}
En este ejemplo, solo calculamos 1 millón de cosenos y sumamos sus valores en paralelo. También cronometramos la ejecución para ver si la paralelización tiene algún efecto en el rendimiento. Finalmente, ya que medimos el tiempo, debemos asegurarnos de que el compilador no optimizará el trabajo que hemos realizado, por lo que pretendemos usar el resultado simplemente devolviéndolo.
Mismo ejemplo en 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
Aquí nuevamente calculamos y acumulamos 1 millón de cosenos. Calculamos el bucle y, para evitar la optimización del compilador no deseado, se pretende utilizar el resultado.
Compilando y ejecutando los ejemplos.
En una máquina Linux de 8 cores que usa GCC versión 4.4, los códigos C se pueden compilar y ejecutar de la siguiente manera:
$ 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 la versión Fortran, da:
$ 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
Adición de dos vectores usando OpenMP paralelo para la construcción
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 ejemplo agrega dos vectores ( A
y B
en C
) generando un equipo de subprocesos (especificados por la variable de OMP_NUM_THREADS
OMP_NUM_THREADS, por ejemplo) y asignando a cada subproceso una parte del trabajo (en este ejemplo, asignado de forma estática a través de la schedule(static)
expresión).
Ver la sección de comentarios con respecto a la opcionalidad private(i)
.