Россия, г. Санкт-Петербург |
Загрузка и синхронизация в OpenMP
Синхронизация типа master
Синхронизация типа 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 parallel3.4. Пример синхронизации типа master
Синхронизация типа ordered
Синхронизация типа 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 enddo3.5. Пример программы с синхронизацией типа ordered
Результаты работы программы:
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
Синхронизация типа 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 flush3.6. Пример синхронизации типа flush
Загрузка процессов в OpenMP. Директива schedule
Проблема загрузки параллельных потоков является важной проблемой не только для параллельного программирования с использованием OpenMP, но и для всего параллельного программирования в целом. Эта проблема тесно связана с проблемой балансировки загрузки процессоров параллельных высокопроизводительных вычислительных систем, а также с проблемой повышения эффективности работы параллельных программ. Понятно, что для высокопроизводительной параллельной вычислительной системы успешное решение проблемы балансировки загрузки процессоров является ключом к решению задачи повышения эффективности вычислительной системы в целом. Как известно, процессы по-разному могут использовать вычислительные возможности процессоров. Так, на стадии интенсивных арифметических вычислений коэффициент загрузки процессоров обычно близок к 100%. На стадии интенсивных операций ввода/вывода коэффициент загрузки процессоров меняется в диапазоне от нескольких процентов до десятков процентов. Кроме того, процессоры могут простаивать и в том случа е, когда процессы, переданные им, уже обработаны, а другие процессоры все еще продолжают обрабатывать процессы той же задачи. Для исключения простоев или уменьшения времени простоев применяют различные методы балансировки процессов. Существующие в OpenMP различные методы загрузки процессов также могут быть применены для улучшения балансировки работы параллельных вычислительных систем.
Для распределения работы между процессами в OpenMP имеется директива schedule с параметрами, позволяющими задавать различные режимы загрузки процессоров. Ниже приведен общий вид предложения schedule в OpenMP.
schedule( type [ , chunk ] )
Здесь type - параметр, определяющий тип загрузки, а chunk - параметр, который определяет порции данных, пересылаемых между процессами (по умолчанию значение параметра chunk равно 1 ).
В OpenMP параметр type принимает одно из следующих значений:
- static,
- dynamic,
- guided,
- runtime.
Загрузка типа static
В этом случае вся совокупность загружаемых процессов разбивается на равные порции размера chunk, и эти порции последовательно распределяются между процессорами (или потоками, которые затем и выполняются на этих процессорах) с первого до последнего и т. д.
В качестве примера использования загрузки типа static рассмотрим фрагмент программы на языке Fortran, приведенный в примере 3.7.
с$omp do shared (x) private (i) с$omp& schedule (static, 1000) do i = 1, 12000 ... work ... enddo3.7. Пример загрузки schedule(static, chunk=1000)
Схема распределения загрузки процессов по процессорам (или потокам ( threads ), которые затем выполняются на процессорах), соответствующая этому примеру, приведена на рис.3.1. Как видно из этой схемы, все 12000 процессов разбиты на 12 порций по 1000 процессов ( chunk=1000 ). Загрузка порций в потоки ( threads ) происходит последовательно. Сначала порции в порядке нумерации загружаются в первый, второй, третий и четвертый потоки, а затем загрузка производится опять в том же порядке, начиная с первого потока и т. д.
Загрузка типа dynamic
В этом случае вся совокупность загружаемых процессов, как и в предыдущем варианте, разбивается на равные порции размера chunk, но эти порции загружаются последовательно в освободившиеся потоки (процессоры).
Пример применения загрузки типа dynamic приведен в следующем фрагменте программы (пример 3.8).
с$omp do shared (x) private (i) с$omp& schedule (dynamic, 1000) do i = 1, 10000 ... work ... enddo3.8. Пример загрузки schedule(dynamic, chunk=1000)
Загрузка типа guided
В этом случае вся совокупность загружаемых процессов разбивается на порции, размер которых определяется операционной системой динамически и не превышает размер chunk. Загрузка порций происходит, как и в динамическом режиме, в первый освободившийся поток, затем следующий освободившийся поток и т. д.
Пример применения загрузки типа guided приведен в следующем фрагменте программы, изображенном на пример 3.9.
с$omp do shared (x) private (i) с$omp& schedule (guided, 55) do i = 1, 12000 ... work ... enddo3.9. Пример загрузки schedule(guided, chunk=55)
В этом режиме обеспечивается достаточно сбалансированная загрузка потоков с небольшими задержками при завершении параллельной обработки.
Загрузка типа runtime
В этом случае загрузка порций процессов по потокам (или процессорам) определяется значением переменной окружения OMP_SCHEDULE. Значение этой переменной проверяется перед каждой загрузкой процессов в потоки во время работы программы. По умолчанию оно static.
Два примера задания режимов загрузки типа runtime с помощью команд операционной системы Linux приведены ниже.
$ setenv OMP_SCHEDULE static,1000 $ setenv OMP_SCHEDULE dynamic
В первом примере через значение переменной окружения в качестве загрузки runtime задается режим static с размером порции равным 1000. Во втором же примере в качестве загрузки runtime задается режим dynamic с размером порции, по умолчанию заданным равным 1.