Главная страница

С. антоновпараллельноепрограммированиесиспользованиемтехнологииopenMP


Скачать 0.55 Mb.
НазваниеС. антоновпараллельноепрограммированиесиспользованиемтехнологииopenMP
Дата03.04.2022
Размер0.55 Mb.
Формат файлаpdf
Имя файлаOpenMP.pdf
ТипУчебное пособие
#438994
страница5 из 8
1   2   3   4   5   6   7   8
for
(
do
... [end do]
).

38
Си:
#pragma omp for [
опция
[[,]
опция
]...]
Фортран:
!$omp do [
опция
[[,]
опция
]...]
<
блок циклов
do>
[!$omp end do [nowait]]
Эта директива относится к идущему следом за данной директивой блоку, включающему операторы
for
(
do
).
Возможные опции:

private(
список
)
– задаёт список переменных, для которых порожда- ется локальная копия в каждой нити; начальное значение локальных копий переменных из списка не определено;

firstprivate(
список
)
– задаёт список переменных, для которых по- рождается локальная копия в каждой нити; локальные копии перемен- ных инициализируются значениями этих переменных в нити-мастере;

lastprivate(
список
)
– переменным, перечисленным в списке, при- сваивается результат с последнего витка цикла;

reduction(
оператор
:
список
)
– задаёт оператор и список общих пе- ременных; для каждой переменной создаются локальные копии в каж- дой нити; локальные копии инициализируются соответственно типу оператора (для аддитивных операций –
0
или его аналоги, для мульти- пликативных операций –
1
или её аналоги); над локальными копиями переменных после завершения всех итераций цикла выполняется за- данный оператор; оператор это: для языка Си –
+
,
*
,
-
,
&
,
|
,
^
,
&&
,
||
, для языка Фортран –
+
,
*
,
-
,
.and.
,
.or.
,
.eqv.
,
.neqv.
,
max
,
min
,
iand
,
ior
,
ieor
; порядок выполнения операторов не определён, поэто- му результат может отличаться от запуска к запуску;

schedule(type[, chunk])
– опция задаёт, каким образом итерации цикла распределяются между нитями;

collapse(n)
— опция указывает, что
n
последовательных тесновло- женных циклов ассоциируется с данной директивой; для циклов обра- зуется общее пространство итераций, которое делится между нитями; если опция
collapse
не задана, то директива относится только к одно- му непосредственно следующему за ней циклу;

39

ordered
– опция, говорящая о том, что в цикле могут встречаться ди- рективы
ordered
; в этом случае определяется блок внутри тела цикла, который должен выполняться в том порядке, в котором итерации идут в последовательном цикле;

nowait
– в конце параллельного цикла происходит неявная барьерная синхронизация параллельно работающих нитей: их дальнейшее выпол- нение происходит только тогда, когда все они достигнут данной точки; если в подобной задержке нет необходимости, опция
nowait
позволяет нитям, уже дошедшим до конца цикла, продолжить выполнение без синхронизации с остальными. Если директива
end do
в явном виде не указана, то в конце параллельного цикла синхронизация все равно бу- дет выполнена.
Если в программе на языке Фортран не указывается директива
end do
, то она предполагается в конце цикла
do
На вид параллельных циклов накладываются достаточно жёсткие ограниче- ния. В частности, предполагается, что корректная программа не должна зави- сеть от того, какая именно нить какую итерацию параллельного цикла вы- полнит. Нельзя использовать побочный выход из параллельного цикла.
Размер блока итераций, указанный в опции
schedule
, не должен изменяться в рамках цикла.
Формат параллельных циклов на языке Си упрощённо можно представить следующим образом:
for([
целочисленный тип
] i =
инвариант цикла
;
i {<,>,=,<=,>=}
инвариант цикла
;
i {+,-}=
инвариант цикла
)
Эти требования введены для того, чтобы OpenMP мог при входе в цикл точно определить число итераций.
Если директива параллельного выполнения стоит перед гнездом циклов, за- вершающихся одним оператором, то директива действует только на самый внешний цикл.
Итеративная переменная распределяемого цикла по смыслу должна быть ло- кальной, поэтому в случае, если она специфицирована общей, то она неявно делается локальной при входе в цикл. После завершения цикла значение ите- ративной переменной цикла не определено, если она не указана в опции
lastprivate

40
Пример 18 демонстрирует использование директивы
for
(
do
). В последова- тельной области инициализируются три исходных массива
A
,
B
,
C
. В парал- лельной области данные массивы объявлены общими. Вспомогательные пе- ременные
i
и
n
объявлены локальными. Каждая нить присвоит переменной
n
свой порядковый номер. Далее с помощью директивы
for
(
do
) определяется цикл, итерации которого будут распределены между существующими нитя- ми. На каждой
i
-ой итерации данный цикл сложит
i
-ые элементы массивов
A
и
B
и результат запишет в
i
-ый элемент массива
C
. Также на каждой итера- ции будет напечатан номер нити, выполнившей данную итерацию.
#include
#include
int main(int argc, char *argv[])
{
int A[10], B[10], C[10], i, n;
/*
Заполним исходные массивы
*/
for (i=0; i<10; i++){ A[i]=i; B[i]=2*i; C[i]=0; }
#pragma omp parallel shared(A, B, C) private(i, n)
{
/*
Получим номер текущей нити
*/
n=omp_get_thread_num();
#pragma omp for
for (i=0; i<10; i++)
{
C[i]=A[i]+B[i];
printf("
Нить
%d
сложила элементы с
номером
%d\n",
n, i);
}
}
}
Пример 18a. Директива for наязыкеСи.

41
program example18b
include "omp_lib.h"
integer A(10), B(10), C(10), i, n
C
Заполним исходные массивы
do i=1, 10
A(i)=i
B(i)=2*i
C(i)=0
end do
!$omp parallel shared(A, B, C) private(i, n)
C
Получим номер текущей нити
n=omp_get_thread_num()
!$omp do
do i=1, 10
C(i)=A(i)+B(i)
print *, "
Нить
", n, "
сложила элементы с
номером
", i
end do
!$omp end parallel
end
Пример 18b. Директива do наязыкеФортран.
В опции
schedule
параметр
type
задаёт следующий тип распределения ите- раций:

static
– блочно-циклическое распределение итераций цикла; размер блока –
chunk
. Первый блок из
chunk
итераций выполняет нулевая нить, второй блок — следующая и т.д. до последней нити, затем рас- пределение снова начинается с нулевой нити. Если значение
chunk
не указано, то всё множество итераций делится на непрерывные куски примерно одинакового размера (конкретный способ зависит от реали- зации), и полученные порции итераций распределяются между нитями.

dynamic
– динамическое распределение итераций с фиксированным размером блока: сначала каждая нить получает
chunk
итераций (по умолчанию
chunk=1
), та нить, которая заканчивает выполнение своей порции итераций, получает первую свободную порцию из
chunk
ите- раций. Освободившиеся нити получают новые порции итераций до тех пор, пока все порции не будут исчерпаны. Последняя порция может содержать меньше итераций, чем все остальные.

guided
– динамическое распределение итераций, при котором размер порции уменьшается с некоторого начального значения до величины
chunk
(по умолчанию
chunk=1
) пропорционально количеству ещё не распределённых итераций, делённому на количество нитей, выпол- няющих цикл. Размер первоначально выделяемого блока зависит от реализации. В ряде случаев такое распределение позволяет аккуратнее

42 разделить работу и сбалансировать загрузку нитей. Количество итера- ций в последней порции может оказаться меньше значения
chunk

auto
– способ распределения итераций выбирается компилятором и/или системой выполнения. Параметр
chunk
при этом не задаётся.

runtime
– способ распределения итераций выбирается во время работы программы по значению переменной среды
OMP_SCHEDULE
. Параметр
chunk
при этом не задаётся.
Пример 19 демонстрирует использование опции
schedule
с параметрами
(static)
,
(static, 1)
,
(static, 2)
,
(dynamic)
,
(dynamic, 2)
,
(guided)
,
(guided, 2)
. В параллельной области выполняется цикл, итерации которого будут распределены между существующими нитями. На каждой итерации будет напечатано, какая нить выполнила данную итерацию. В тело цикла вставлена также задержка, имитирующая некоторые вычисления.
#include
#include
int main(int argc, char *argv[])
{
int i;
#pragma omp parallel private(i)
{
#pragma omp for schedule (static)
//#pragma omp for schedule (static, 1)
//#pragma omp for schedule (static, 2)
//#pragma omp for schedule (dynamic)
//#pragma omp for schedule (dynamic, 2)
//#pragma omp for schedule (guided)
//#pragma omp for schedule (guided, 2)
for (i=0; i<10; i++)
{
printf("
Нить
%d
выполнила итерацию
%d\n",
omp_get_thread_num(), i);
sleep(1);
}
}
}
Пример 19a. Опция schedule наязыкеСи.

43
program example19b
include "omp_lib.h"
integer i
!$omp parallel private(i)
!$omp do schedule (static)
C!$omp do schedule (static, 1)
C!$omp do schedule (static, 2)
C!$omp do schedule (dynamic)
C!$omp do schedule (dynamic, 2)
C!$omp do for schedule (guided)
C!$omp do for schedule (guided, 2)
do i=0, 9
print *, "
Нить
", omp_get_thread_num(),
& "
выполнила итерацию
", i
call sleep(1)
end do
!$omp end parallel
end
Пример 19b. Опция schedule наязыкеФортран.
Результаты выполнения примера 19 с различными типами распределения итераций приведены в таблице 1. Столбцы соответствуют различным типам распределений, а строки – номеру итерации. В ячейках таблицы указаны но- мера нити, выполнявшей соответствующую итерацию. Во всех случаях для выполнения параллельного цикла использовалось 4 нити. Для динамических способов распределения итераций (
dynamic
,
guided
) конкретное распреде- ление между нитями может отличаться от запуска к запуску.
i
static static,
1
static,
2
dynamic dynamic,
2
guided guided,
2
0 0
0
0
0
0
0
0
1 0
1
0
1
0
2
0
2 0
2
1
2
1
1
1
3 1
3
1
3
1
3
1
4 1
0
2
1
2
1
2
5 1
1
2
3
2
2
2
6 2
2
3
2
3
3
3
7 2
3
3
0
3
0
3
8 3
0
0
1
3
0
0
9 3
1
0
0
3
3
0
Таблица 1. Распределениеитерацийпонитям.

44
В таблице 1 видна разница между распределением итераций при использова- нии различных вариантов. К наибольшему дисбалансу привели варианты распределения
(static, 2)
,
(dynamic, 2)
и
(guided, 2)
. Во всех этих случаях одной из нитей достаётся на две итерации больше, чем остальным. В других случаях эта разница несколько сглаживается.
Пример 20 демонстрирует использование опции
schedule
с параметрами
(static, 6)
,
(dynamic, 6)
,
(guided, 6)
. В параллельной области выпол- няется цикл, итерации которого будут распределены между существующими нитями. На каждой итерации будет напечатано, какая нить выполнила дан- ную итерацию. В тело цикла вставлена также задержка, имитирующая неко- торые вычисления.
#include
#include
int main(int argc, char *argv[])
{
int i;
#pragma omp parallel private(i)
{
#pragma omp for schedule (static, 6)
//#pragma omp for schedule (dynamic, 6)
//#pragma omp for schedule (guided, 6)
for (i=0; i<200; i++)
{
printf("
Нить
%d
выполнила итерацию
%d\n",
omp_get_thread_num(), i);
sleep(1);
}
}
}
Пример 20a. Опция schedule наязыкеСи.
program example20b
include "omp_lib.h"
integer i
!$omp parallel private(i)
!$omp do schedule (static, 6)
C!$omp do schedule (dynamic, 6)
C!$omp do schedule (guided, 6)
do i=0, 200
print *, "
Нить
", omp_get_thread_num(),
& "
выполнила итерацию
", i
call sleep(1)
end do
!$omp end parallel
end
Пример 20b. Опция schedule наязыкеФортран.

45
В результате выполнения примера 20 с тремя разными вариантами директи- вы
for
получаются следующие распределения итераций (рис. 1a – рис. 1c).
Рис.1a. Распределениеитерацийпонитямдля (static, 6)
Рис.1b. Распределениеитерацийпонитямдля (dynamic, 6)

46
Рис.1c. Распределениеитерацийпонитямдля (guided, 6)
На рисунках видна регулярность распределения порций по
6
итераций при указании
(static, 6)
, более динамичная картина распределения таких же порций при указании
(dynamic, 6)
и распределение уменьшающимися пор- циями при указании
(guided, 6)
. В последнем случае размер порций уменьшался с
24
в самом начале цикла до
6
в конце.
Значение по умолчанию переменной
OMP_SCHEDULE
зависит от реализации.
Если переменная задана неправильно, то поведение программы при задании опции
runtime
также зависит от реализации.
Задать значение переменной
OMP_SCHEDULE
в Linux в командной оболочке bash можно при помощи команды следующего вида:
export OMP_SCHEDULE="dynamic,1"
Изменить значение переменной
OMP_SCHEDULE
из программы можно с помо- щью вызова функции
omp_set_schedule()
Си:
void omp_set_schedule(omp_sched_t type, int chunk);

47
Фортран:
subroutine omp_set_schedule(type, chunk)
integer (kind=omp_sched_kind) type
integer chunk
Допустимые значения констант описаны в файле
omp.h
(
omp_lib.h
). Как минимум, они должны включать для языка Си следующие варианты:
typedef enum omp_sched_t {
omp_sched_static = 1,
omp_sched_dynamic = 2,
omp_sched_guided = 3,
omp_sched_auto = 4
} omp_sched_t;
Для языка Фортран должны быть заданы как минимум следующие варианты:
integer(kind=omp_sched_kind), parameter :: omp_sched_static = 1
integer(kind=omp_sched_kind), parameter :: omp_sched_dynamic = 2
integer(kind=omp_sched_kind), parameter :: omp_sched_guided = 3
integer(kind=omp_sched_kind), parameter :: omp_sched_auto = 4
При помощи вызова функции
omp_get_schedule()
пользователь может уз- нать текущее значение переменной
OMP_SCHEDULE
Си:
void omp_get_schedule(omp_sched_t* type, int* chunk);
Фортран:
subroutine omp_get_schedule(type, chunk);
integer (kind=omp_sched_kind) type
integer chunk
При распараллеливании цикла программист должен убедиться в том, что итерации данного цикла не имеют информационных зависимостей [5]. Если цикл не содержит зависимостей, его итерации можно выполнять в любом по- рядке, в том числе параллельно. Соблюдение этого важного требования ком- пилятор не проверяет, вся ответственность лежит на программисте. Если дать указание компилятору распараллелить цикл, содержащий зависимости, ком- пилятор это сделает, но результат работы программы может оказаться некор- ректным.
Параллельные секции
Директива
sections
(
sections ... end sections
) используется для зада- ния конечного (неитеративного) параллелизма.
Си:
1   2   3   4   5   6   7   8


написать администратору сайта