Важно отметить, что неудачное решение проблем загрузки, синхронизации и балансировки параллельных потоков может свести на нет все усилия по разработке параллельной версии программы. В результате параллельная версия программы может оказаться не быстрее, а существенно медленнее последовательной версии программы. В связи с этим при разработке параллельной версии программы проблемам загрузки, синхронизации и балансировки процессов следует уделять самое пристальное внимание. В случае разбалансировки, когда не все процессоры завершают свою работу одновременно, эффективность использования многопроцессорных параллельных вычислительных систем резко снижается и может не оправдывать затрат на приобретение таких дорогостоящих систем.
Рассмотрению процессов балансировки процессов в вычислительных системах посвящено много литературы (см., например, работы [3.1-3.14]). В настоящее время можно выделить две основных группы методов решения проблем балансировки: статические и динамические. В основе статических методов лежит принцип геометрического параллелизма, когда вычислительный процесс представляется в виде взвешенного графа, а затем этот граф разбивается на эквивалентные части [3.1-3.9]. Для решения таких задач используются различные спектральные и эвристические подходы, а также их комбинации. Отметим, что для построения графов, описывающих вычислительный процесс, важно иметь и учитывать как можно больше априорной информации о процессе, поскольку в этом случае повышается точность описания.
В методах динамической балансировки априорная информация не используется. В них процессы загружаются в освободившиеся процессоры по мере освобождения последних [3.10]. При этом более равномерная загрузка процессоров получается при достаточно большом количестве независимых процессов. Решение о загрузке процессоров может приниматься по-разному: либо в одном отдельном управляющем процессе, либо в разных управляющих процессах, каждый из которых ассоциирован с вычислительным процессом. Последний подход известен также как метод коллективного решения. Этот подход развивается и разработан в Институте математического моделирования РАН [3.11]. Апробация динамической балансировки, основанной на идеях коллективного решения, показала эффективность подхода при решении различных классов вычислительных задач математической физики.
Отметим также, что в библиотеке
Проблема синхронизации параллельных потоков важна не только для параллельного программирования с использованием OpenMP, но и для всего параллельного программирования в целом. Проблема состоит в том, что любой структурный параллельный блок по определению имеет одну точку выхода, за которой обычно находится последовательный структурный блок. Вычисления в последовательном блоке, как правило, могут быть продолжены, если завершены все процессы в параллельном структурном блоке и их результаты корректно переданы в последовательный блок. Именно для обеспечения такой корректной передачи данных и необходима процедура синхронизации параллельных потоков.
В предшествующей лекции при изучении директив работы с циклами проблема синхронизации уже затрагивалась. Во-первых, было указано, что эта процедура является весьма трудоемкой и сопоставима с трудоемкостью инициализации параллельных потоков (т. е. эквивалентна примерно трудоемкости 1000 операций). Поэтому желательно пользоваться синхронизацией как можно реже.
Во-вторых, было отмечено, что неявно (по умолчанию) синхронизация параллельных процессов обеспечивается при выполнении циклов в параллельном режиме. Была упомянута директива для устранения неявной синхронизации при завершении циклов. Однако пользоваться этой директивой следует весьма и весьма аккуратно, предварительно проанализировав порядок работы программы и убедившись, что отмена синхронизации не приведет к порче данных и непредсказуемым результатам.
Механизм работы синхронизации можно описать следующим образом. При инициализации набора параллельных процессов в программе устанавливается контрольная точка (аналогичная контрольной точке в отладчике), в которой программа ожидает завершения всех порожденных параллельных процессов. Отметим, что пока все параллельные процессы свою работу не завершили, программа не может продолжить работу за точкой синхронизации. А поскольку все современные высокопроизводительные процессоры являются процессорами конвейерного типа, становится понятной и высокая трудоемкость процедуры синхронизации. В самом деле, пока не завершены все параллельные процессы, программа не может начать подготовку загрузки конвейеров процессоров. Вот это-то и ведет к большим потерям при синхронизации процессов, аналогичных потерям при работе условных операторов в обычной последовательной программе.
Всего в OpenMP существует шесть типов синхронизации:
critical,atomic,barrier ,master,ordered,flush.Далее подробно рассмотрим эти типы.
Этот тип синхронизации определяет переменную в левой части оператора присваивания, которая должна корректно обновляться несколькими нитями. В этом случае происходит предотвращение прерывания доступа, чтения и записи данных, находящихся в общей памяти, со стороны других потоков.
Для задания этого типа синхронизации в OpenMP в программах, написанных на языке C/C++, используется прагма
#pragma omp atomic <операторы программы>
В программах, написанных на языке Fortran, задание синхронизации типа atomic осуществляется с помощью следующего предложения OpenMP:
c$omp atomic <операторы программы>
Отметим, что синхронизация atomic является альтернативой директивы . Применяется эта синхронизация только для операторов, следующих непосредственно за определяющей ее директивой. Синхронизация atomic - очень дорогая операция с точки зрения трудоемкости выполнения программы. Она выполняется автоматически по умолчанию при завершении циклов в параллельном режиме. Для того чтобы ее исключить, следует использовать директиву .
Пример установки синхронизации типа atomic приведен во фрагменте программы в примере 3.1.
integer, dimension (8) :: a, index
data index/ 1, 1, 2, 3, 1, 4, 1, 5 /
с$omp parallel private (I), shared (a, index)
c$omp do
do I =1, 8
с$omp atomic
a(index(I)) = a(index(I)) + index(I)
enddo
с$omp end parallel
В приведенном примере в параллельном режиме синхронизируются только вычисления оператора
a(index(I)) = a(index(I)) + index(I)
Этот тип синхронизации используется для описания структурных блоков, выполняющихся только в одном потоке из всего набора параллельных потоков.
Для задания синхронизации типа critical в OpenMP в программах, написанных на языке C/C++, используется прагма
#pragma omp critical [ name ] <структурный блок программы>
В программах, написанных на языке Fortran, задание синхронизации типа critical осуществляется с помощью следующего предложения OpenMP:
c$omp critical [ name ] < структурный блок программы > c$omp end critical [ name ]
Здесь name - имя критической секции ( critical section ). Разные критические секции независимы, если они имеют разные имена. Не поименованные критические секции относятся к одной и той же секции.
Пример использования синхронизации типа critical приведен во фрагменте программы (пример 3.2). В этом примере определены две критических секции name1 и name2, каждая из которых выполняется только в одном из параллельных потоков. Этот тип синхронизации очень важен для достижения пиковой производительности программ.
integer :: cnt1, cnt2
с$omp parallel private (i)
с$omp shared (cnt1, cnt2)
с$omp do
do i = 1, n
... do work ...
if (condition1) then
с$omp critical (name1)
cnt1 = cnt1 + 1
с$omp end critical (name1)
else
с$omp critical (name1)
cnt1 = cnt1 - 1
с$omp end critical (name1)
endif
if (condition2) then
с$omp critical (name2)
cnt2 = cnt2+1
с$omp end critical (name2)
endif
enddo
с$omp end parallel
Синхронизация типа устанавливает режим ожидания завершения работы всех запущенных в программе параллельных потоков при достижении точки .
Для задания синхронизации типа в OpenMP в программах, написанных на языке C/C++, используется прагма
#pragma omp barrier
В программах, написанных на языке Fortran, задание синхронизации типа осуществляется с помощью следующего предложения OpenMP:
c$ omp barrier
Пример использования синхронизации типа приведен во фрагменте программы (пример 3.3). В этом примере синхронизируется выполнение блока операторов <assignment>. Следующий блок <dependent work> начинает свою работу во всех параллельных потоках лишь только после того, как во всех параллельных потоках будут завершено выполнение блока <assignment>.
Отметим, что неявно синхронизация типа по умолчанию устанавливается в конце циклов. Для ее отмены можно воспользоваться директивой OpenMP . Вопросы применения этой директивы подробно рассматривались в предыдущей лекции в разделе, посвященном операторам циклов.
с$omp parallel
с$omp do
do i = 1, n
<assignment>
с$omp barrier
<dependent work>
enddo
с$omp end parallel
Здесь же отметим, что на языке Fortran определение директивы выглядит так:
c$omp end do nowait
а на языке C/C++ - так:
#pragma omp for nowait
Синхронизация типа master используется для определения структурного блока программы, который будет выполняться исключительно в главном потоке (параллельном потоке с нулевым номером) из всего набора параллельных потоков. Этот структурный блок следует за директивой
#pragma omp master
в программах, написанных на языке C/C++. В программах на языке Fortran синхронизация типа master устанавливается следующим образом:
c$omp master <структурный блок> c$omp end master
В примере 3.4 приведен пример фрагмента программы, иллюстрирующий использование синхронизации типа master. В этом примере операторы print и read выполняются исключительно в главном потоке.
! $omp parallel shared (c, scale)
! $omp private (j, myid)
myid = omp_get_thread_num ( )
! $omp master
print *, 'T: ', myid, ' enter scale'
read *, scale
! $omp end master
! $omp barrier
! $omp do
do j = 1, N
c (j) = scale * c (j)
enddo
! $omp end do
! $omp end parallel
Синхронизация типа ordered используется для определения потоков в параллельной области программы, которые выполняются в порядке, соответствующем последовательной версии программы.
с$omp parallel default (shared) private (I, J)
с$omp do ordered
do I = 1, N
do J = 1, M
Z(I) = Z(I) + X (I, J) * Y(J, I)
enddo
с$omp ordered
if (I<21) then
print*, 'Z (',I,') = ', Z (I)
endif
с$omp end ordered
enddo
Результаты работы программы:
Z (1) = 1007.167786 Z (2) = 1032.933350 Z (3) = 1033.125610 Z (4) = 1009.944641 Z (5) = 1016.547302 Z (6) = 1005.789124 Z (7) = 1025.048584 Z (8) = 1003.904358 Z (9) = 995.5405273 Z (10) = 991.2892456 Z (11) = 1011.334167 Z (12) = 1010.631897 Z (13) = 1009.581848 Z (14) = 976.2397461 Z (15) = 978.1119385 Z (16) = 977.7111816 Z (17) = 971.2011719 Z (18) = 998.4275513 Z (19) = 1018.487000 Z (20) = 978.0640259
В программах на языке C/C++ синхронизация типа ordered описывается следующим образом:
#pragma omp ordered <структурный блок>
а в программах, написанных на языке Fortran, синхронизация типа ordered устанавливается так:
c$omp ordered <структурный блок> c$omp end ordered
В примере 3.5 приведен пример фрагмента программы, иллюстрирующий применение синхронизации типа ordered.
Видно, что в приведенном примере вывод результатов элементов массива Z осуществляется в порядке возрастания индексов элементов массива, как в обычной последовательной программе.
Синхронизация типа flush используется для обновления значений локальных переменных, перечисленных в качестве аргументов этой команды, в оперативной памяти. После выполнения этой директивы все переменные, перечисленные в этой директиве, имеют одно и то же значение для всех параллельных потоков.
В программах на языке C/C++ синхронизация типа flush описывается следующим образом:
#pragma omp flush(var1, [var2, [..., varN ]])
На языке Fortran эта директива синхронизации типа flush выглядит так:
c$omp flush var1, [var2, [..., varN ]])
Здесь var1, var2, ..., varN - список переменных, значения которых сохраняются в оперативной памяти в момент выполнения директории flush.
Пример фрагмента программы, иллюстрирующий использование синхронизации типа flush, приведен в примере 3.6. В этом примере значения переменной done записываются в оперативную память для того, чтобы все другие процессы имели доступ к актуальным значениям этой переменной.
program flush
integer, parameter :: M=1600000
integer, dimension (M) :: c
integer :: stop, sum, tid
integer, dimension (0:1) :: done
integer, external :: omp_get_thread_num
call omp_set_num_threads (2)
c = 1
c (345) = 9
!$omp parallel default (private) shared (done, c, stop)
tid=omp_get_ thread_num ( )
done (tid) = 0
if (tid == 0) then
neigh = 1
else
neigh = 0
end if
!$omp barrier
if (tid == 0) then
do j = 1, M
if (c j) == 9) stop = j
enddo
endif
done (tid) = 1
!$omp flush (done)
do while (done(neigh) .eq. 0)
!$omp flush (done)
enddo
if (tid == 1) then
sum=0
do j = 1, stop - 1
sum = sum + c (j)
enddo
endif
!$omp end parallel
end program flush
Проблема загрузки параллельных потоков является важной проблемой не только для параллельного программирования с использованием OpenMP, но и для всего параллельного программирования в целом. Эта проблема тесно связана с проблемой балансировки загрузки процессоров параллельных высокопроизводительных вычислительных систем, а также с проблемой повышения эффективности работы параллельных программ. Понятно, что для высокопроизводительной
Для распределения работы между процессами в OpenMP имеется директива schedule с параметрами, позволяющими задавать различные режимы загрузки процессоров. Ниже приведен общий вид предложения schedule в OpenMP.
schedule( type [ , chunk ] )
Здесь type - параметр, определяющий тип загрузки, а - параметр, который определяет порции данных, пересылаемых между процессами (по умолчанию значение параметра равно 1 ).
В OpenMP параметр type принимает одно из следующих значений:
static,dynamic,guided,runtime.В этом случае вся совокупность загружаемых процессов разбивается на равные порции размера , и эти порции последовательно распределяются между процессорами (или потоками, которые затем и выполняются на этих процессорах) с первого до последнего и т. д.
В качестве примера использования загрузки типа static рассмотрим фрагмент программы на языке Fortran, приведенный в примере 3.7.
с$omp do shared (x) private (i)
с$omp schedule (static, 1000)
do i = 1, 12000
... work ...
enddo
Схема распределения загрузки процессов по процессорам (или потокам ( threads ), которые затем выполняются на процессорах), соответствующая этому примеру, приведена на рис.3.1. Как видно из этой схемы, все 12000 процессов разбиты на 12 порций по 1000 процессов ( ). Загрузка порций в потоки ( threads ) происходит последовательно. Сначала порции в порядке нумерации загружаются в первый, второй, третий и четвертый потоки, а затем загрузка производится опять в том же порядке, начиная с первого потока и т. д.
(рис 3.1) Схема загрузки процессоров для программы, приведенной в примере 3.7
В этом случае вся совокупность загружаемых процессов, как и в предыдущем варианте, разбивается на равные порции размера , но эти порции загружаются последовательно в освободившиеся потоки (процессоры).
Пример применения загрузки типа dynamic приведен в следующем фрагменте программы (пример 3.8).
с$omp do shared (x) private (i)
с$omp schedule (dynamic, 1000)
do i = 1, 10000
... work ...
enddo
В этом случае вся совокупность загружаемых процессов разбивается на порции, размер которых определяется операционной системой динамически и не превышает размер . Загрузка порций происходит, как и в динамическом режиме, в первый освободившийся поток, затем следующий освободившийся поток и т. д.
Пример применения загрузки типа guided приведен в следующем фрагменте программы, изображенном на пример 3.9.
с$omp do shared (x) private (i)
с$omp schedule (guided, 55)
do i = 1, 12000
... work ...
enddo
В этом режиме обеспечивается достаточно сбалансированная загрузка потоков с небольшими задержками при завершении параллельной обработки.
В этом случае загрузка порций процессов по потокам (или процессорам) определяется значением переменной окружения OMP_SCHEDULE. Значение этой переменной проверяется перед каждой загрузкой процессов в потоки во время работы программы. По умолчанию оно static.
Два примера задания режимов загрузки типа runtime с помощью команд операционной системы Linux приведены ниже.
$ setenv OMP_SCHEDULE static,1000 $ setenv OMP_SCHEDULE dynamic
В первом примере через значение переменной окружения в качестве загрузки runtime задается режим static с размером порции равным 1000. Во втором же примере в качестве загрузки runtime задается режим dynamic с размером порции, по умолчанию заданным равным 1.
Важно отметить, что неудачное решение проблем загрузки, синхронизации и балансировки параллельных потоков может свести на нет все усилия по разработке параллельной версии программы. В результате параллельная версия программы может оказаться не быстрее, а существенно медленнее последовательной версии программы. В связи с этим при разработке параллельной версии программы проблемам загрузки, синхронизации и балансировки процессов следует уделять самое пристальное внимание. В случае разбалансировки, когда не все процессоры завершают свою работу одновременно, эффективность использования многопроцессорных параллельных вычислительных систем резко снижается и может не оправдывать затрат на приобретение таких дорогостоящих систем.
Рассмотрению процессов балансировки процессов в вычислительных системах посвящено много литературы (см., например, работы [3.1-3.14]). В настоящее время можно выделить две основных группы методов решения проблем балансировки: статические и динамические. В основе статических методов лежит принцип геометрического параллелизма, когда вычислительный процесс представляется в виде взвешенного графа, а затем этот граф разбивается на эквивалентные части [3.1-3.9]. Для решения таких задач используются различные спектральные и эвристические подходы, а также их комбинации. Отметим, что для построения графов, описывающих вычислительный процесс, важно иметь и учитывать как можно больше априорной информации о процессе, поскольку в этом случае повышается точность описания.
В методах динамической балансировки априорная информация не используется. В них процессы загружаются в освободившиеся процессоры по мере освобождения последних [3.10]. При этом более равномерная загрузка процессоров получается при достаточно большом количестве независимых процессов. Решение о загрузке процессоров может приниматься по-разному: либо в одном отдельном управляющем процессе, либо в разных управляющих процессах, каждый из которых ассоциирован с вычислительным процессом. Последний подход известен также как метод коллективного решения. Этот подход развивается и разработан в Институте математического моделирования РАН [3.11]. Апробация динамической балансировки, основанной на идеях коллективного решения, показала эффективность подхода при решении различных классов вычислительных задач математической физики.
Отметим также, что в библиотеке
Проблема синхронизации параллельных потоков важна не только для параллельного программирования с использованием OpenMP, но и для всего параллельного программирования в целом. Проблема состоит в том, что любой структурный параллельный блок по определению имеет одну точку выхода, за которой обычно находится последовательный структурный блок. Вычисления в последовательном блоке, как правило, могут быть продолжены, если завершены все процессы в параллельном структурном блоке и их результаты корректно переданы в последовательный блок. Именно для обеспечения такой корректной передачи данных и необходима процедура синхронизации параллельных потоков.
В предшествующей лекции при изучении директив работы с циклами проблема синхронизации уже затрагивалась. Во-первых, было указано, что эта процедура является весьма трудоемкой и сопоставима с трудоемкостью инициализации параллельных потоков (т. е. эквивалентна примерно трудоемкости 1000 операций). Поэтому желательно пользоваться синхронизацией как можно реже.
Во-вторых, было отмечено, что неявно (по умолчанию) синхронизация параллельных процессов обеспечивается при выполнении циклов в параллельном режиме. Была упомянута директива для устранения неявной синхронизации при завершении циклов. Однако пользоваться этой директивой следует весьма и весьма аккуратно, предварительно проанализировав порядок работы программы и убедившись, что отмена синхронизации не приведет к порче данных и непредсказуемым результатам.
Механизм работы синхронизации можно описать следующим образом. При инициализации набора параллельных процессов в программе устанавливается контрольная точка (аналогичная контрольной точке в отладчике), в которой программа ожидает завершения всех порожденных параллельных процессов. Отметим, что пока все параллельные процессы свою работу не завершили, программа не может продолжить работу за точкой синхронизации. А поскольку все современные высокопроизводительные процессоры являются процессорами конвейерного типа, становится понятной и высокая трудоемкость процедуры синхронизации. В самом деле, пока не завершены все параллельные процессы, программа не может начать подготовку загрузки конвейеров процессоров. Вот это-то и ведет к большим потерям при синхронизации процессов, аналогичных потерям при работе условных операторов в обычной последовательной программе.
Всего в OpenMP существует шесть типов синхронизации:
critical,atomic,barrier ,master,ordered,flush.Далее подробно рассмотрим эти типы.
Этот тип синхронизации определяет переменную в левой части оператора присваивания, которая должна корректно обновляться несколькими нитями. В этом случае происходит предотвращение прерывания доступа, чтения и записи данных, находящихся в общей памяти, со стороны других потоков.
Для задания этого типа синхронизации в OpenMP в программах, написанных на языке C/C++, используется прагма
#pragma omp atomic <операторы программы>
В программах, написанных на языке Fortran, задание синхронизации типа atomic осуществляется с помощью следующего предложения OpenMP:
c$omp atomic <операторы программы>
Отметим, что синхронизация atomic является альтернативой директивы . Применяется эта синхронизация только для операторов, следующих непосредственно за определяющей ее директивой. Синхронизация atomic - очень дорогая операция с точки зрения трудоемкости выполнения программы. Она выполняется автоматически по умолчанию при завершении циклов в параллельном режиме. Для того чтобы ее исключить, следует использовать директиву .
Пример установки синхронизации типа atomic приведен во фрагменте программы в примере 3.1.
integer, dimension (8) :: a, index
data index/ 1, 1, 2, 3, 1, 4, 1, 5 /
с$omp parallel private (I), shared (a, index)
c$omp do
do I =1, 8
с$omp atomic
a(index(I)) = a(index(I)) + index(I)
enddo
с$omp end parallel
В приведенном примере в параллельном режиме синхронизируются только вычисления оператора
a(index(I)) = a(index(I)) + index(I)
Этот тип синхронизации используется для описания структурных блоков, выполняющихся только в одном потоке из всего набора параллельных потоков.
Для задания синхронизации типа critical в OpenMP в программах, написанных на языке C/C++, используется прагма
#pragma omp critical [ name ] <структурный блок программы>
В программах, написанных на языке Fortran, задание синхронизации типа critical осуществляется с помощью следующего предложения OpenMP:
c$omp critical [ name ] < структурный блок программы > c$omp end critical [ name ]
Здесь name - имя критической секции ( critical section ). Разные критические секции независимы, если они имеют разные имена. Не поименованные критические секции относятся к одной и той же секции.
Пример использования синхронизации типа critical приведен во фрагменте программы (пример 3.2). В этом примере определены две критических секции name1 и name2, каждая из которых выполняется только в одном из параллельных потоков. Этот тип синхронизации очень важен для достижения пиковой производительности программ.
integer :: cnt1, cnt2
с$omp parallel private (i)
с$omp shared (cnt1, cnt2)
с$omp do
do i = 1, n
... do work ...
if (condition1) then
с$omp critical (name1)
cnt1 = cnt1 + 1
с$omp end critical (name1)
else
с$omp critical (name1)
cnt1 = cnt1 - 1
с$omp end critical (name1)
endif
if (condition2) then
с$omp critical (name2)
cnt2 = cnt2+1
с$omp end critical (name2)
endif
enddo
с$omp end parallel
Синхронизация типа устанавливает режим ожидания завершения работы всех запущенных в программе параллельных потоков при достижении точки .
Для задания синхронизации типа в OpenMP в программах, написанных на языке C/C++, используется прагма
#pragma omp barrier
В программах, написанных на языке Fortran, задание синхронизации типа осуществляется с помощью следующего предложения OpenMP:
c$ omp barrier
Пример использования синхронизации типа приведен во фрагменте программы (пример 3.3). В этом примере синхронизируется выполнение блока операторов <assignment>. Следующий блок <dependent work> начинает свою работу во всех параллельных потоках лишь только после того, как во всех параллельных потоках будут завершено выполнение блока <assignment>.
Отметим, что неявно синхронизация типа по умолчанию устанавливается в конце циклов. Для ее отмены можно воспользоваться директивой OpenMP . Вопросы применения этой директивы подробно рассматривались в предыдущей лекции в разделе, посвященном операторам циклов.
с$omp parallel
с$omp do
do i = 1, n
<assignment>
с$omp barrier
<dependent work>
enddo
с$omp end parallel
Здесь же отметим, что на языке Fortran определение директивы выглядит так:
c$omp end do nowait
а на языке C/C++ - так:
#pragma omp for nowait
Синхронизация типа master используется для определения структурного блока программы, который будет выполняться исключительно в главном потоке (параллельном потоке с нулевым номером) из всего набора параллельных потоков. Этот структурный блок следует за директивой
#pragma omp master
в программах, написанных на языке C/C++. В программах на языке Fortran синхронизация типа master устанавливается следующим образом:
c$omp master <структурный блок> c$omp end master
В примере 3.4 приведен пример фрагмента программы, иллюстрирующий использование синхронизации типа master. В этом примере операторы print и read выполняются исключительно в главном потоке.
! $omp parallel shared (c, scale)
! $omp private (j, myid)
myid = omp_get_thread_num ( )
! $omp master
print *, 'T: ', myid, ' enter scale'
read *, scale
! $omp end master
! $omp barrier
! $omp do
do j = 1, N
c (j) = scale * c (j)
enddo
! $omp end do
! $omp end parallel
Синхронизация типа ordered используется для определения потоков в параллельной области программы, которые выполняются в порядке, соответствующем последовательной версии программы.
с$omp parallel default (shared) private (I, J)
с$omp do ordered
do I = 1, N
do J = 1, M
Z(I) = Z(I) + X (I, J) * Y(J, I)
enddo
с$omp ordered
if (I<21) then
print*, 'Z (',I,') = ', Z (I)
endif
с$omp end ordered
enddo
Результаты работы программы:
Z (1) = 1007.167786 Z (2) = 1032.933350 Z (3) = 1033.125610 Z (4) = 1009.944641 Z (5) = 1016.547302 Z (6) = 1005.789124 Z (7) = 1025.048584 Z (8) = 1003.904358 Z (9) = 995.5405273 Z (10) = 991.2892456 Z (11) = 1011.334167 Z (12) = 1010.631897 Z (13) = 1009.581848 Z (14) = 976.2397461 Z (15) = 978.1119385 Z (16) = 977.7111816 Z (17) = 971.2011719 Z (18) = 998.4275513 Z (19) = 1018.487000 Z (20) = 978.0640259
В программах на языке C/C++ синхронизация типа ordered описывается следующим образом:
#pragma omp ordered <структурный блок>
а в программах, написанных на языке Fortran, синхронизация типа ordered устанавливается так:
c$omp ordered <структурный блок> c$omp end ordered
В примере 3.5 приведен пример фрагмента программы, иллюстрирующий применение синхронизации типа ordered.
Видно, что в приведенном примере вывод результатов элементов массива Z осуществляется в порядке возрастания индексов элементов массива, как в обычной последовательной программе.
Синхронизация типа flush используется для обновления значений локальных переменных, перечисленных в качестве аргументов этой команды, в оперативной памяти. После выполнения этой директивы все переменные, перечисленные в этой директиве, имеют одно и то же значение для всех параллельных потоков.
В программах на языке C/C++ синхронизация типа flush описывается следующим образом:
#pragma omp flush(var1, [var2, [..., varN ]])
На языке Fortran эта директива синхронизации типа flush выглядит так:
c$omp flush var1, [var2, [..., varN ]])
Здесь var1, var2, ..., varN - список переменных, значения которых сохраняются в оперативной памяти в момент выполнения директории flush.
Пример фрагмента программы, иллюстрирующий использование синхронизации типа flush, приведен в примере 3.6. В этом примере значения переменной done записываются в оперативную память для того, чтобы все другие процессы имели доступ к актуальным значениям этой переменной.
program flush
integer, parameter :: M=1600000
integer, dimension (M) :: c
integer :: stop, sum, tid
integer, dimension (0:1) :: done
integer, external :: omp_get_thread_num
call omp_set_num_threads (2)
c = 1
c (345) = 9
!$omp parallel default (private) shared (done, c, stop)
tid=omp_get_ thread_num ( )
done (tid) = 0
if (tid == 0) then
neigh = 1
else
neigh = 0
end if
!$omp barrier
if (tid == 0) then
do j = 1, M
if (c j) == 9) stop = j
enddo
endif
done (tid) = 1
!$omp flush (done)
do while (done(neigh) .eq. 0)
!$omp flush (done)
enddo
if (tid == 1) then
sum=0
do j = 1, stop - 1
sum = sum + c (j)
enddo
endif
!$omp end parallel
end program flush
Проблема загрузки параллельных потоков является важной проблемой не только для параллельного программирования с использованием OpenMP, но и для всего параллельного программирования в целом. Эта проблема тесно связана с проблемой балансировки загрузки процессоров параллельных высокопроизводительных вычислительных систем, а также с проблемой повышения эффективности работы параллельных программ. Понятно, что для высокопроизводительной
Для распределения работы между процессами в OpenMP имеется директива schedule с параметрами, позволяющими задавать различные режимы загрузки процессоров. Ниже приведен общий вид предложения schedule в OpenMP.
schedule( type [ , chunk ] )
Здесь type - параметр, определяющий тип загрузки, а - параметр, который определяет порции данных, пересылаемых между процессами (по умолчанию значение параметра равно 1 ).
В OpenMP параметр type принимает одно из следующих значений:
static,dynamic,guided,runtime.В этом случае вся совокупность загружаемых процессов разбивается на равные порции размера , и эти порции последовательно распределяются между процессорами (или потоками, которые затем и выполняются на этих процессорах) с первого до последнего и т. д.
В качестве примера использования загрузки типа static рассмотрим фрагмент программы на языке Fortran, приведенный в примере 3.7.
с$omp do shared (x) private (i)
с$omp schedule (static, 1000)
do i = 1, 12000
... work ...
enddo
Схема распределения загрузки процессов по процессорам (или потокам ( threads ), которые затем выполняются на процессорах), соответствующая этому примеру, приведена на рис.3.1. Как видно из этой схемы, все 12000 процессов разбиты на 12 порций по 1000 процессов ( ). Загрузка порций в потоки ( threads ) происходит последовательно. Сначала порции в порядке нумерации загружаются в первый, второй, третий и четвертый потоки, а затем загрузка производится опять в том же порядке, начиная с первого потока и т. д.
(рис 3.1) Схема загрузки процессоров для программы, приведенной в примере 3.7
В этом случае вся совокупность загружаемых процессов, как и в предыдущем варианте, разбивается на равные порции размера , но эти порции загружаются последовательно в освободившиеся потоки (процессоры).
Пример применения загрузки типа dynamic приведен в следующем фрагменте программы (пример 3.8).
с$omp do shared (x) private (i)
с$omp schedule (dynamic, 1000)
do i = 1, 10000
... work ...
enddo
В этом случае вся совокупность загружаемых процессов разбивается на порции, размер которых определяется операционной системой динамически и не превышает размер . Загрузка порций происходит, как и в динамическом режиме, в первый освободившийся поток, затем следующий освободившийся поток и т. д.
Пример применения загрузки типа guided приведен в следующем фрагменте программы, изображенном на пример 3.9.
с$omp do shared (x) private (i)
с$omp schedule (guided, 55)
do i = 1, 12000
... work ...
enddo
В этом режиме обеспечивается достаточно сбалансированная загрузка потоков с небольшими задержками при завершении параллельной обработки.
В этом случае загрузка порций процессов по потокам (или процессорам) определяется значением переменной окружения OMP_SCHEDULE. Значение этой переменной проверяется перед каждой загрузкой процессов в потоки во время работы программы. По умолчанию оно static.
Два примера задания режимов загрузки типа runtime с помощью команд операционной системы Linux приведены ниже.
$ setenv OMP_SCHEDULE static,1000 $ setenv OMP_SCHEDULE dynamic
В первом примере через значение переменной окружения в качестве загрузки runtime задается режим static с размером порции равным 1000. Во втором же примере в качестве загрузки runtime задается режим dynamic с размером порции, по умолчанию заданным равным 1.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.