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

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


Скачать 0.55 Mb.
НазваниеС. антоновпараллельноепрограммированиесиспользованиемтехнологииopenMP
Дата03.04.2022
Размер0.55 Mb.
Формат файлаpdf
Имя файлаOpenMP.pdf
ТипУчебное пособие
#438994
страница4 из 8
1   2   3   4   5   6   7   8
shared
). Исключение составляют переменные, являющиеся счетчиками итераций в цикле, по очевидным при- чинам. Переменные, порождённые внутри параллельной области, по умолча- нию являются локальными (
private
). Явно назначить класс переменных по умолчанию можно с помощью опции
default
. Не рекомендуется постоянно полагаться на правила по умолчанию, для большей надёжности лучше всегда явно описывать классы используемых переменных, указывая в директивах
OpenMP опции
private
,
shared
,
firstprivate
,
lastprivate
,
reduction
Пример 12 демонстрирует использование опции
private
. В данном примере переменная
n
объявлена как локальная переменная в параллельной области.
Это значит, что каждая нить будет работать со своей копией переменной
n
, при этом в начале параллельной области на каждой нити переменная
n
не бу- дет инициализирована. В ходе выполнения программы значение переменной
n
будет выведено в четырёх разных местах. Первый раз значение
n
будет вы- ведено в последовательной области, сразу после присваивания переменной
n

30 значения
1
. Второй раз все нити выведут значение своей копии переменной
n
в начале параллельной области, неинициализированное значение может за- висеть от реализации. Далее все нити выведут свой порядковый номер, полу- ченный с помощью функции
omp_get_thread_num()
и присвоенный пере- менной
n
. После завершения параллельной области будет ещё раз выведено значение переменной
n
, которое окажется равным
1
(не изменилось во время выполнения параллельной области).
#include
#include
int main(int argc, char *argv[])
{
int n=1;
printf("n
в последовательной области
(
начало
): %d\n", n);
#pragma omp parallel private(n)
{
printf("
Значение
n
на нити
(
на входе
): %d\n", n);
/*
Присвоим переменной
n
номер текущей нити
*/
n=omp_get_thread_num();
printf("
Значение
n
на нити
(
на выходе
): %d\n", n);
}
printf("n
в последовательной области
(
конец
): %d\n", n);
}
Пример 12a. Опция private наязыкеСи.
program example12b
include "omp_lib.h"
integer n
n=1
print *, "n
в последовательной области
(
начало
): ", n
!$omp parallel private(n)
print *, "
Значение
n
на нити
(
на входе
): ", n
C
Присвоим переменной
n
номер текущей нити
n=omp_get_thread_num()
print *, "
Значение
n
на нити
(
на выходе
): ", n
!$omp end parallel
print *, "n
в последовательной области
(
конец
): ", n
end
Пример 12b. Опция private наязыкеФортран.
Пример 13 демонстрирует использование опции
shared
. Массив
m
объявлен общим для всех нитей. В начале последовательной области массив
m
запол- няется нулями и выводится на печать. В параллельной области каждая нить находит элемент, номер которого совпадает с порядковым номером нити в общем массиве, и присваивает этому элементу значение
1
. Далее, в последо- вательной области печатается изменённый массив
m

31
#include
#include
int main(int argc, char *argv[])
{
int i, m[10];
printf("
Массив
m
в начале
:\n");
/*
Заполним массив
m
нулями и
напечатаем его
*/
for (i=0; i<10; i++){
m[i]=0;
printf("%d\n", m[i]);
}
#pragma omp parallel shared(m)
{
/*
Присвоим
1
элементу массива
m,
номер которого совпадает с
номером текущий нити
*/
m[omp_get_thread_num()]=1;
}
/*
Ещё
раз напечатаем массив
*/
printf("
Массив
m
в конце
:\n");
for (i=0; i<10; i++) printf("%d\n", m[i]);
}
Пример 13a. Опция shared наязыкеСи.
program example13b
include "omp_lib.h"
integer i, m(10)
print *, "
Массив
m
в начале
:"
C
Заполним массив
m
нулями и
напечатаем его
do i=1, 10
m(i)=0
print *, m(i)
end do
!$omp parallel shared(m)
C
Присвоим
1
элементу массива
m,
номер которого
C
совпадает с
номером текущий нити
m(omp_get_thread_num()+1)=1
!$omp end parallel
C
Ещё
раз напечатаем массив
*/
print *, "
Массив
m
в конце
:"
do i=1, 10
print *, m(i)
end do
end
Пример 13b. Опция shared наязыкеФортран.
В языке Си статические (
static
) переменные, опредёленные в параллельной области программы, являются общими (
shared
). Динамически выделенная память также является общей, однако указатель на неё может быть как об- щим, так и локальным.

32
В языке Фортран по умолчанию общими (
shared
) являются элементы
COMMON-блоков.
Отдельные правила определяют назначение классов переменных при входе и выходе из параллельной области или параллельного цикла при использова- нии опций
reduction
,
firstprivate
,
lastprivate
,
copyin
Пример 14 демонстрирует использование опции
firstprivate
. Переменная
n
объявлена как
firstprivate
в параллельной области. Значение
n
будет выведено в четырёх разных местах. Первый раз значение
n
будет выведено в последовательной области сразу после инициализации. Второй раз все нити выведут значение своей копии переменной
n
в начале параллельной области, и это значение будет равно
1
Далее, с помощью функции
omp_get_thread_num()
все нити присвоят переменной
n
свой порядковый номер и ещё раз выведут значение
n
. В последовательной области будет ещё раз выведено значение
n
, которое снова окажется равным
1
#include
#include
int main(int argc, char *argv[])
{
int n=1;
printf("
Значение
n
в начале
: %d\n", n);
#pragma omp parallel firstprivate(n)
{
printf("
Значение
n
на нити
(
на входе
): %d\n", n);
/*
Присвоим переменной
n
номер текущей нити
*/
n=omp_get_thread_num();
printf("
Значение
n
на нити
(
на выходе
): %d\n", n);
}
printf("
Значение
n
в конце
: %d\n", n);
}
Пример 14a. Опция firstprivate наязыкеСи.
program example14b
include "omp_lib.h"
integer n
n=1
print *, "
Значение
n
в начале
: ", n
!$omp parallel firstprivate(n)
print *, "
Значение
n
на нити
(
на входе
): ", n
C
Присвоим переменной
n
номер текущей нити
n=omp_get_thread_num()
print *, "
Значение
n
на нити
(
на выходе
): ", n
!$omp end parallel
print *, "
Значение
n
в конце
: ", n
end
Пример 14b. Опция firstprivate наязыкеФортран.

33
Директива
threadprivate
указывает, что переменные из списка должны быть размножены с тем, чтобы каждая нить имела свою локальную копию.
Си:
#pragma omp threadprivate(
список
)
Фортран:
!$omp threadprivate(
список
)
Директива
threadprivate
может позволить сделать локальные копии для статических переменных языка Си и COMMON-блоков языка Фортран, кото- рые по умолчанию являются общими. Для корректного использования ло- кальных копий глобальных объектов нужно гарантировать, что они исполь- зуются в разных частях программы одними и теми же нитями. Если на локальные копии ссылаются в разных параллельных областях, то для сохра- нения их значений необходимо, чтобы не было объемлющих параллельных областей, количество нитей в обеих областях совпадало, а переменная
OMP_DYNAMIC
была установлена в
false
с начала первой области до начала второй. Переменные, объявленные как
threadprivate
, не могут использо- ваться в опциях директив OpenMP, кроме
copyin
,
copyprivate
,
schedule
,
num_threads
,
if
Пример 15 демонстрирует использование директивы
threadprivate
. Гло- бальная переменная
n
объявлена как
threadprivate
переменная. Значение переменной n выводится в четырёх разных местах. Первый раз все нити вы- ведут значение своей копии переменной
n
в начале параллельной области, и это значение будет равно
1
на нити-мастере и
0
на остальных нитях. Далее с помощью функции
omp_get_thread_num()
все нити присвоят переменной
n
свой порядковый номер и выведут это значение. Затем в последовательной области будет ещё раз выведено значение переменной
n
, которое окажется равным порядковому номеру нити-мастера, то есть
0
. В последний раз значе- ние переменной
n
выводится в новой параллельной области, причём значение каждой локальной копии должно сохраниться.

34
#include
#include
int n;
#pragma omp threadprivate(n)
int main(int argc, char *argv[])
{
int num;
n=1;
#pragma omp parallel private (num)
{
num=omp_get_thread_num();
printf("
Значение
n
на нити
%d (
на входе
): %d\n", num, n);
/*
Присвоим переменной
n
номер текущей нити
*/
n=omp_get_thread_num();
printf("
Значение
n
на нити
%d (
на выходе
): %d\n", num, n);
}
printf("
Значение
n (
середина
): %d\n", n);
#pragma omp parallel private (num)
{
num=omp_get_thread_num();
printf("
Значение
n
на нити
%d (
ещё
раз
): %d\n", num, n);
}
}
Пример 15a. Директива threadprivate наязыкеСи.
program example15b
include "omp_lib.h"
common/nnn/n
integer n, num;
!$omp threadprivate(/nnn/)
n=1;
!$omp parallel private (num)
num=omp_get_thread_num()
print *, "
Значение
n
на нити
", num, " (
на входе
): ", n
C
Присвоим переменной
n
номер текущей нити
n=omp_get_thread_num();
print *, "
Значение
n
на нити
", num, " (
на выходе
): ", n
!$omp end parallel
print *, "
Значение
n (
середина
): ", n
!$omp parallel private (num)
num=omp_get_thread_num()
print *, "
Значение
n
на нити
", num, " (
ещё
раз
): ", n
!$omp end parallel
end
Пример 15b. Директива threadprivate наязыкеФортран.
Если необходимо переменную, объявленную как
threadprivate
, инициали- зировать значением размножаемой переменной из нити-мастера, то на входе в параллельную область можно использовать опцию
copyin
. Если значение локальной переменной или переменной, объявленной как
threadprivate
, необходимо переслать от одной нити всем, работающим в данной параллель-

35 ной области, для этого можно использовать опцию
copyprivate
директивы
single
Пример 16 демонстрирует использование опции
copyin
. Глобальная пере- менная
n
определена как
threadprivate
. Применение опции
copyin
позво- ляет инициализировать локальные копии переменной
n
начальным значени- ем нити-мастера. Все нити выведут значение
n
, равное
1
#include
int n;
#pragma omp threadprivate(n)
int main(int argc, char *argv[])
{
n=1;
#pragma omp parallel copyin(n)
{
printf("
Значение
n: %d\n", n);
}
}
Пример 16a. Опция copyin наязыкеСи.
program example16b
common/nnn/n
integer n
!$omp threadprivate(/nnn/)
n=1;
!$omp parallel copyin(n)
print *, "
Значение
n: ", n
!$omp end parallel
end
Пример 16b. Опция copyin наязыкеФортран.
Задания

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

Что произойдёт, если несколько нитей одновременно обратятся к об- щей переменной?

Может ли произойти конфликт, если несколько нитей одновременно обратятся к одной и той же локальной переменной?

Каким образом при входе в параллельную область разослать всем по- рождаемым нитям значение некоторой переменной?

Можно ли сохранить значения локальных копий общих переменных после завершения параллельной области? Если да, то что необходимо для их использования?

В чём отличие опции
copyin
от опции
firstprivate
?

36
Распределение работы
OpenMP предлагает несколько вариантов распределения работы между за- пущенными нитями. Конструкции распределения работ в OpenMP не порож- дают новых нитей.
Низкоуровневое распараллеливание
Все нити в параллельной области нумеруются последовательными целыми числами от
0
до
N-1
, где
N
— количество нитей, выполняющих данную об- ласть.
Можно программировать на самом низком уровне, распределяя работу с по- мощью функций
omp_get_thread_num()
и
omp_get_num_threads()
, воз- вращающих номер нити и общее количество порождённых нитей в текущей параллельной области, соответственно.
Вызов функции
omp_get_thread_num()
позволяет нити получить свой уни- кальный номер в текущей параллельной области.
Си:
int omp_get_thread_num(void);
Фортран:
integer function omp_get_thread_num()
Вызов функции
omp_get_num_threads()
позволяет нити получить количест- во нитей в текущей параллельной области.
Си:
int omp_get_num_threads(void);
Фортран:
integer function omp_get_num_threads()
Пример 17 демонстрирует работу функций
omp_get_num_threads()
и
omp_get_thread_num()
. Нить, порядковый номер которой равен
0
, напеча- тает общее количество порождённых нитей, а остальные нити напечатают свой порядковый номер.

37
#include
#include
int main(int argc, char *argv[])
{
int count, num;
#pragma omp parallel
{
count=omp_get_num_threads();
num=omp_get_thread_num();
if (num == 0) printf("
Всего нитей
: %d\n", count);
else printf("
Нить номер
%d\n", num);
}
}
Пример 17a. Функции omp_get_num_threads() и omp_get_thread_num() наязы-
кеСи.
program example17b
include "omp_lib.h"
integer count, num
!$omp parallel
count=omp_get_num_threads()
num=omp_get_thread_num()
if (num .eq. 0) then
print *, "
Всего нитей
: ", count
else
print *, "
Нить номер
", num
end if
!$omp end parallel
end
Пример 17b. Функции omp_get_num_threads() и omp_get_thread_num() наязы-
кеФортран.
Использование функций
omp_get_thread_num()
и
omp_get_num_threads()
позволяет назначать каждой нити свой кусок кода для выполнения, и таким образом распределять работу между нитями в стиле технологии MPI [8]. Од- нако использование этого стиля программирования в OpenMP далеко не все- гда оправдано – программист в этом случае должен явно организовывать синхронизацию доступа к общим данным. Другие способы распределения работ в OpenMP обеспечивают значительную часть этой работы автоматиче- ски.
Параллельные циклы
Если в параллельной области встретился оператор цикла, то, согласно обще- му правилу, он будет выполнен всеми нитями текущей группы, то есть каж- дая нить выполнит все итерации данного цикла. Для распределения итераций цикла между различными нитями можно использовать директиву
1   2   3   4   5   6   7   8


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