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

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


Скачать 0.55 Mb.
НазваниеС. антоновпараллельноепрограммированиесиспользованиемтехнологииopenMP
Дата03.04.2022
Размер0.55 Mb.
Формат файлаpdf
Имя файлаOpenMP.pdf
ТипУчебное пособие
#438994
страница2 из 8
1   2   3   4   5   6   7   8
!$OMP
,
C$OMP
или
*$OMP
, а в языке Си — указаниями препроцессору, начинающимися с
#pragma omp
. Ключевое слово
omp
ис- пользуется для того, чтобы исключить случайные совпадения имён директив
OpenMP с другими именами.
Формат директивы на Си/Си++:
#pragma omp directive-name [
опция
[[,]
опция
]...]
Формат директивы на Фортране:
!$OMP directive-name [
опция
[[,]
опция
]...]
С
$OMP directive-name [
опция
[[,]
опция
]...]
*$OMP directive-name [
опция
[[,]
опция
]...]
Объектом действия большинства директив является один оператор или блок, перед которым расположена директива в исходном тексте программы. В
OpenMP такие операторы или блоки называются ассоциированными с дирек- тивой. Ассоциированный блок должен иметь одну точку входа в начале и од- ну точку выхода в конце. Порядок опций в описании директивы несуществе- нен, в одной директиве большинство опций может встречаться несколько раз. После некоторых опций может следовать список переменных (для Фор- трана также и имён COMMON-блоков, заключённых в слеши), разделяемых запятыми.
Все директивы OpenMP можно разделить на 3 категории: определение па- раллельной области, распределение работы, синхронизация. Каждая дирек- тива может иметь несколько дополнительных атрибутов – опций(clause). От- дельно специфицируются опции для назначения классов переменных, которые могут быть атрибутами различных директив.
Чтобы задействовать функциибиблиотеки OpenMP периодавыполнения (ис- полняющей среды), в программу нужно включить заголовочный файл
omp.h
(для программ на языке Фортран – файл
omp_lib.h
или модуль
omp_lib
).
Если вы используете в приложении только OpenMP-директивы, включать этот файл не требуется. Функции назначения параметров имеют приоритет над соответствующими переменными окружения.

11
Все функции, используемые в OpenMP, начинаются с префикса
omp_
. Если пользователь не будет использовать в программе имён, начинающихся с та- кого префикса, то конфликтов с объектами OpenMP заведомо не будет. В языке Си, кроме того, является существенным регистр символов в названиях функций. Названия функций OpenMP записываются строчными буквами.
Для того чтобы программа, использующая функции OpenMP, могла оста- ваться корректной для обычного компилятора, можно прилинковать специ- альную библиотеку, которая определит для каждой функции соответствую- щую «заглушку» (stub). Например, в компиляторе Intel соответствующая библиотека подключается заданием ключа
–openmp-stubs
Выполнение программы
После получения выполняемого файла необходимо запустить его на требуе- мом количестве процессоров. Для этого обычно нужно задать количество ни- тей, выполняющих параллельные области программы, определив значение переменной среды
OMP_NUM_THREADS
. Например, в Linux в командной обо- лочке bash это можно сделать при помощи следующей команды:
export OMP_NUM_THREADS=n
После запуска начинает работать одна нить, а внутри параллельных областей одна и та же программа будет выполняться всем набором нитей. Стандарт- ный вывод программы в зависимости от системы будет выдаваться на терми- нал или записываться в файл с предопределенным именем.
Замер времени
В OpenMP предусмотрены функции для работы с системным таймером.
Функция
omp_get_wtime()
возвращает в вызвавшей нити астрономическое время в секундах (вещественное число двойной точности), прошедшее с не- которого момента в прошлом.
Си:
double omp_get_wtime(void);
Фортран:
double precision function omp_get_wtime()
Если некоторый участок программы окружить вызовами данной функции, то разность возвращаемых значений покажет время работы данного участка.
Гарантируется, что момент времени, используемый в качестве точки отсчета,

12 не будет изменён за время существования процесса. Таймеры разных нитей могут быть не синхронизированы и выдавать различные значения.
Функция
omp_get_wtick()
возвращает в вызвавшей нити разрешение тайме- ра в секундах. Это время можно рассматривать как меру точности таймера.
Си:
double omp_get_wtick(void);
Фортран:
double precision function omp_get_wtick()
Пример 2 иллюстрирует применение функций
omp_get_wtime()
и
omp_get_wtick()
для работы с таймерами в OpenMP. В данном примере производится замер начального времени, затем сразу замер конечного време- ни. Разность времён даёт время на замер времени. Кроме того, измеряется точность системного таймера.
#include
#include
int main(int argc, char *argv[])
{
double start_time, end_time, tick;
start_time = omp_get_wtime();
end_time = omp_get_wtime();
tick = omp_get_wtick();
printf("
Время на замер времени
%lf\n", end_time-start_time);
printf("
Точность таймера
%lf\n", tick);
}
Пример 2a. РаботассистемнымитаймераминаязыкеСи.
program example2b
include "omp_lib.h"
double precision start_time, end_time, tick
start_time = omp_get_wtime()
end_time = omp_get_wtime()
tick = omp_get_wtick()
print *, "
Время на замер времени
", end_time-start_time
print *, "
Точность таймера
", tick
end
Пример 2b. РаботассистемнымитаймераминаязыкеФортран.
Задания

Определите, какую версию стандарта OpenMP поддерживает компиля- тор на доступной системе.

Откомпилируйте любую последовательную программу с включением опций поддержки технологии OpenMP и запустите с использованием

13 нескольких нитей. Сколько нитей будет реально исполнять операторы данной программы?

Может ли программа на OpenMP состоять только из параллельных об- ластей? Только из последовательных областей?

Чем отличается нить-мастер от всех остальных нитей?

При помощи функций OpenMP попробуйте определить время, необхо- димое для работы функции
omp_get_wtick()
. Хватает ли для этого точности системного таймера?

14
Параллельные и
последовательные области
В момент запуска программы порождается единственная нить-мастер или
«основная» нить, которая начинает выполнение программы с первого опера- тора. Основная нить и только она исполняет все последовательные области программы. При входе в параллельную область порождаются дополнитель- ные нити.
Директива
parallel
Параллельная область задаётся при помощи директивы
parallel
(
parallel
... end parallel
).
Си:
#pragma omp parallel [
опция
[[,]
опция
]...]
Фортран:
!$omp parallel [
опция
[[,]
опция
]...]
<
код параллельной области
>
!$omp end parallel
Возможные опции:

if(
условие
)
– выполнение параллельной области по условию. Вхож- дение в параллельную область осуществляется только при выполнении некоторого условия. Если условие не выполнено, то директива не сра- батывает и продолжается обработка программы в прежнем режиме;

num_threads (
целочисленное выражение
)
– явное задание количест- ва нитей, которые будут выполнять параллельную область; по умолча- нию выбирается последнее значение, установленное с помощью функ- ции
omp_set_num_threads()
, или значение переменной
OMP_NUM_THREADS
;

default(private|firstprivate|shared|none)
– всем переменным в параллельной области, которым явно не назначен класс, будет назначен класс
private
,
firstprivate
или
shared
соответственно;
none
озна- чает, что всем переменным в параллельной области класс должен быть назначен явно; в языке Си задаются только варианты
shared
или
none
;

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

15

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

shared(
список
)
– задаёт список переменных, общих для всех нитей;

copyin(
список
)
– задаёт список переменных, объявленных как
threadprivate
, которые при входе в параллельную область инициали- зируются значениями соответствующих переменных в нити-мастере;

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

16
#include
int main(int argc, char *argv[])
{
printf("
Последовательная область
1\n");
#pragma omp parallel
{
printf("
Параллельная область
\n");
}
printf("
Последовательная область
2\n");
}
Пример 3a. ПараллельнаяобластьнаязыкеСи.
program example3b
print *, "
Последовательная область
1"
!$omp parallel
print *, "
Параллельная область
"
!$omp end parallel
print *, "
Последовательная область
2"
end
Пример 3b. ПараллельнаяобластьнаязыкеФортран.
Пример 4 демонстрирует применение опции
reduction
. В данном примере производится подсчет общего количества порождённых нитей. Каждая нить инициализирует локальную копию переменной
count
значением
0
. Далее, каждая нить увеличивает значение собственной копии переменной
count
на единицу и выводит полученное число. На выходе из параллельной области происходит суммирование значений переменных
count
по всем нитям, и по- лученная величина становится новым значением переменной
count
в после- довательной области.
#include
int main(int argc, char *argv[])
{
int count = 0;
#pragma omp parallel reduction (+: count)
{
count++;
printf("
Текущее значение
count: %d\n", count);
}
printf("
Число нитей
: %d\n", count);
}
Пример 4a. Опция reduction наязыкеСи.

17
program example4b
integer count
count=0
!$omp parallel reduction (+: count)
count=count+1
print *, "
Текущее значение
count: ", count
!$omp end parallel
print *, "
Число нитей
: ", count
end
Пример 4b. Опция reduction наязыкеФортран.
Сокращённая запись
Если внутри параллельной области содержится только один параллельный цикл, одна конструкция
sections
или одна конструкция
workshare
, то мож- но использовать укороченную запись:
parallel for
(
parallel do
для язы- ка Фортран),
parallel sections
или
parallel workshare
. При этом до- пустимо указание всех опций этих директив, за исключением опции
nowait
Переменные среды и
вспомогательные функции
Перед запуском программы количество нитей, выполняющих параллельную область, можно задать, определив значение переменной среды
OMP_NUM_THREADS
. Например, в Linux в командной оболочке bash это можно сделать при помощи следующей команды:
export OMP_NUM_THREADS=n
Значение по умолчанию переменной
OMP_NUM_THREADS
зависит от реализа- ции. Из программы её можно изменить с помощью вызова функции
omp_set_num_threads()
Си:
void omp_set_num_threads(int num);
Фортран:
subroutine omp_set_num_threads(num)
integer num
Пример 5 демонстрирует применение функции
omp_set_num_threads()
и опции
num_threads
. Перед первой параллельной областью вызовом функции
omp_set_num_threads(2)
выставляется количество нитей, равное
2
. Но к первой параллельной области применяется опция
num_threads(3)
, которая указывает, что данную область следует выполнять тремя нитями. Следова- тельно, сообщение
"
Параллельная область
1"
будет выведено тремя нитя- ми. Ко второй параллельной области опция
num_threads
не применяется, по-

18 этому действует значение, установленное функцией
omp_set_num_threads(2)
, и сообщение
"
Параллельная область
2"
будет выведено двумя нитями.
#include
#include
int main(int argc, char *argv[])
{
omp_set_num_threads(2);
#pragma omp parallel num_threads(3)
{
printf("
Параллельная область
1\n");
}
#pragma omp parallel
{
printf("
Параллельная область
2\n");
}
}
Пример 5a. Функция omp_set_num_threads() иопция num_threads наязыкеСи.
program example5b
include "omp_lib.h"
call omp_set_num_threads(2);
!$omp parallel num_threads(3)
print *, "
Параллельная область
1"
!$omp end parallel
!$omp parallel
print *, "
Параллельная область
2"
!$omp end parallel
end
Пример 5b. Функция omp_set_num_threads() иопция num_threads наязыке
Фортран.
В некоторых случаях система может динамически изменять количество ни- тей, используемых для выполнения параллельной области, например, для оп- тимизации использования ресурсов системы. Это разрешено делать, если пе- ременная среды
OMP_DYNAMIC
установлена в
true
. Например, в Linux в командной оболочке bash её можно установить при помощи следующей ко- манды:
export OMP_DYNAMIC=true
В системах с динамическим изменением количества нитей значение по умол- чанию не определено, иначе значение по умолчанию:
false
Переменную
OMP_DYNAMIC
можно установить с помощью функции
omp_set_dynamic()

19
Си:
void omp_set_dynamic(int num);
Фортран:
subroutine omp_set_dynamic(num)
logical num
На языке Си в качестве значения параметра функции
omp_set_dynamic()
за- даётся
0
или
1
, а на языке Фортран –
.FALSE.
или
.TRUE.
Если система не поддерживает динамическое изменение количества нитей, то при вызове функции
omp_set_dynamic()
значение переменной
OMP_DYNAMIC
не изме- нится.
Узнать значение переменной
OMP_DYNAMIC
можно при помощи функции
omp_get_dynamic()
Си:
int omp_get_dynamic(void);
Фортран:
logical function omp_get_dynamic()
Пример 6 демонстрирует применение функций
omp_set_dynamic()
и
1   2   3   4   5   6   7   8


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