openmp
Параллельность петли в OpenMP
Поиск…
параметры
пункт | параметр |
---|---|
private | Список частных переменных, разделенных запятыми |
firstprivate | Как private , но инициализируется значением переменной перед входом в цикл |
lastprivate | Как private , но переменная получит значение, соответствующее последней итерации цикла при выходе |
reduction | оператор сокращения : список соответствующих редукционных переменных, разделенных запятыми |
schedule | static , dynamic , guided , auto или runtime с дополнительным размером блока после комы для 3-х бывших |
collapse | Количество идеально вложенных циклов для развала и параллелизации |
ordered | Сообщает, что некоторые части цикла должны быть сохранены в порядке (эти части будут конкретно идентифицированы с некоторыми ordered предложениями внутри тела цикла) |
nowait | Удалите скрытый барьер, существующий по умолчанию в конце конструкции цикла |
замечания
Смысл положения о schedule
заключается в следующем:
-
static[,chunk]
: распределить статически (что означает, что дистрибутив выполняется перед входом в цикл) итерации цикла в пакетном размереchunk
в циклическом режиме. Еслиchunk
не указан, то куски как можно более ровны, и каждый поток получает не более одного из них. -
dynamic[,chunk]
: распределить итерации цикла между потоками пакетами размеромchunk
с политикой с первым пришел-первым, пока не останется партия. Если не указано,chunk
установлен в 1 -
guided[,chunk]
: какdynamic
но с партиями, размеры которых становятся все меньше и меньше, до 1 -
auto
: Позвольте компилятору и / или библиотеке времени выполнения решить, что лучше всего подходит -
runtime
: принять решение во время выполнения сOMP_SCHEDULE
переменной средыOMP_SCHEDULE
. Если во время выполнения переменная среды не определена, будет использоваться планирование по умолчанию
По умолчанию для schedule
используется определение реализации . Во многих средах это static
, но также может быть dynamic
или вполне может быть auto
. Поэтому будьте осторожны, чтобы ваша реализация не косвенно опиралась на нее без явной настройки.
В приведенных выше примерах мы использовали сплавленную форму, parallel for
или parallel do
. Однако конструкцию цикла можно использовать, не сливая ее с parallel
директивой, в виде #pragma omp for [...]
или !$omp do [...]
автономной директивы в parallel
области.
Только для версии Fortran индексированная переменная цикла цикла (s) всегда является private
по умолчанию. Поэтому нет необходимости явно объявлять их private
(хотя делать это не является ошибкой).
Для версий C и C ++ индексы цикла аналогичны любым другим переменным. Поэтому, если их область действия выходит за пределы параллельного цикла (s) (то есть, если они не объявлены как for ( int i = ...)
а скорее как int i; ... for ( i = ... )
тогда они должны быть объявлены private
.
Типичный пример в 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;
}
В этом примере мы просто вычислим 1 миллион косинусов и суммируем их значения параллельно. Мы также выполняем время, чтобы увидеть, влияет ли распараллеливание на производительность. Наконец, поскольку мы измеряем время, мы должны убедиться, что компилятор не будет оптимизировать работу, которую мы сделали, поэтому мы делаем вид, что используем результат, просто вернув его.
Тот же пример в Фортране
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
Здесь снова мы вычисляем и накапливаем 1 миллион косинусов. Мы время цикла и, чтобы избежать нежелательной оптимизации компилятора от нее, мы делаем вид, что используем результат.
Компиляция и запуск примеров
На машине с 8 ядрами, использующей GCC версии 4.4, коды C могут быть скомпилированы и выполняться следующим образом:
$ 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
Для версии Fortran она дает:
$ 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
Добавление двух векторов с использованием OpenMP parallel для построения
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];
}
}
Этот пример добавляет два вектора ( A
и B
в C
), OMP_NUM_THREADS
группу потоков ( OMP_NUM_THREADS
указанную переменной OMP_NUM_THREADS
OMP_NUM_THREADS) и назначая каждому потоку кусок работы (в этом примере статически назначается по schedule(static)
выражение).
См. Раздел замечаний относительно private(i)
варианта.