С. антоновпараллельноепрограммированиесиспользованиемтехнологииopenMP
Скачать 0.55 Mb.
|
#pragma omp sections [ опция [[,] опция ]...] 48 Фортран: !$omp sections [ опция [[,] опция ]...] < блок секций > !$omp end sections [nowait] Эта директива определяет набор независимых секций кода, каждая из кото- рых выполняется своей нитью. Возможные опции: private( список ) – задаёт список переменных, для которых порожда- ется локальная копия в каждой нити; начальное значение локальных копий переменных из списка не определено; firstprivate( список ) – задаёт список переменных, для которых по- рождается локальная копия в каждой нити; локальные копии перемен- ных инициализируются значениями этих переменных в нити-мастере; lastprivate( список ) – переменным, перечисленным в списке, при- сваивается результат, полученный в последней секции; reduction( оператор : список ) – задаёт оператор и список общих пе- ременных; для каждой переменной создаются локальные копии в каж- дой нити; локальные копии инициализируются соответственно типу оператора (для аддитивных операций – 0 или его аналоги, для мульти- пликативных операций – 1 или её аналоги); над локальными копиями переменных после завершения всех секций выполняется заданный опе- ратор; оператор это: для языка Си – + , * , - , & , | , ^ , && , || , для языка Фортран – + , * , - , .and. , .or. , .eqv. , .neqv. , max , min , iand , ior , ieor ; порядок выполнения операторов не определён, поэтому резуль- тат может отличаться от запуска к запуску; nowait – в конце блока секций происходит неявная барьерная синхро- низация параллельно работающих нитей: их дальнейшее выполнение происходит только тогда, когда все они достигнут данной точки; если в подобной задержке нет необходимости, опция nowait позволяет нитям, уже дошедшим до конца своих секций, продолжить выполнение без синхронизации с остальными. Директива section задаёт участок кода внутри секции sections для выпол- нения одной нитью. Си: #pragma omp section 49 Фортран: !$omp section Перед первым участком кода в блоке sections директива section не обяза- тельна. Какие именно нити будут задействованы для выполнения какой сек- ции, не специфицируется. Если количество нитей больше количества секций, то часть нитей для выполнения данного блока секций не будет задействова- на. Если количество нитей меньше количества секций, то некоторым (или всем) нитям достанется более одной секции. Пример 21 иллюстрирует применение директивы sections . Cначала три ни- ти, на которые распределились три секции section , выведут сообщение со своим номером, а потом все нити напечатают одинаковое сообщение со сво- им номером. #include #include int main(int argc, char *argv[]) { int n; #pragma omp parallel private(n) { n=omp_get_thread_num(); #pragma omp sections { #pragma omp section { printf(" Первая секция , процесс %d\n", n); } #pragma omp section { printf(" Вторая секция , процесс %d\n", n); } #pragma omp section { printf(" Третья секция , процесс %d\n", n); } } printf(" Параллельная область , процесс %d\n", n); } } Пример 21a. Директива sections наязыкеСи. 50 program example21b include "omp_lib.h" integer n !$omp parallel private(n) n=omp_get_thread_num() !$omp sections !$omp section print *, " Первая секция , процесс ", n !$omp section print *, " Вторая секция , процесс ", n !$omp section print *, " Третья секция , процесс ", n !$omp end sections print *, " Параллельная область , процесс ", n !$omp end parallel end Пример 21b. Директива sections наязыкеФортран. Пример 22 демонстрирует использование опции lastprivate . В данном примере опция lastprivate используется вместе с директивой sections Переменная n объявлена как lastprivate переменная. Три нити, выпол- няющие секции section , присваивают своей локальной копии n разные зна- чения. По выходе из области sections значение n из последней секции при- сваивается локальным копиям во всех нитях, поэтому все нити напечатают число 3 . Это же значение сохранится для переменной n и в последовательной области. 51 #include #include int main(int argc, char *argv[]) { int n=0; #pragma omp parallel { #pragma omp sections lastprivate(n) { #pragma omp section { n=1; } #pragma omp section { n=2; } #pragma omp section { n=3; } } printf(" Значение n на нити %d: %d\n", omp_get_thread_num(), n); } printf(" Значение n в последовательной области : %d\n", n); } Пример 22a. Опция lastprivate наязыкеСи. program example22b include "omp_lib.h" integer n n=0 !$omp parallel !$omp sections lastprivate(n) !$omp section n=1; !$omp section n=2; !$omp section n=3; !$omp end sections print *, " Значение n на нити ", & omp_get_thread_num(), ": ", n !$omp end parallel print *, " Значение n в последовательной области : ", n end Пример 22b. Опция lastprivate наязыкеФортран. 52 Директива workshare Директива workshare ... end workshare используется для задания конеч- ного (неитеративного) параллелизма программ, написанных на языке Фор- тран. !$omp workshare < блок операторов > !$omp end workshare [nowait] Блок между директивами определяет набор независимых операторов, каждый из которых выполняется своей нитью. В этот набор могут входить: присваи- вания массивов и скалярных переменных, операторы и конструкции FORALL , WHERE , директивы atomic , critical , parallel . При выполнении этого набо- ра команд каждая такая операция выдаётся на исполнение отдельной нити (распределение операций по нитям зависит от реализации), при этом система поддержки автоматически вставляет все необходимые по семантике синхро- низации. Например, система обеспечивает, чтобы операция не начиналась до тех пор, пока не будут полностью готовы все её аргументы. В конце конст- рукции предполагается неявная синхронизация работы нитей. Если в подоб- ной синхронизации нет необходимости, то может быть использована опция nowait Пример 23 иллюстрирует применение директивы workshare . Сначала два исходных массива инициализируются, затем в параллельной области задаёт- ся область workshare , в которой производятся три массивные операции. Системой гарантируется, что третья операция не начнётся до того момента, пока не завершатся первые две. program example23 real a(10), b(10), c(10), d(10), e(10) integer i do i=1, 10 a(i) = i b(i) = 2*i end do !$omp parallel shared(a, b, c, d, e) !$omp workshare c=a+b d=b-a e=c+2*d !$omp end workshare !$omp end parallel print *, " Результирующий массив : ", e end Пример 23. Директива workshare. 53 Задачи ( tasks ) Директива task ( task ... end task ) применяется для выделения отдель- ной независимой задачи. Си: #pragma omp task [ опция [[,] опция ]...] Фортран: !$omp task [ опция [[,] опция ]...] < код задачи > !$omp end task Текущая нить выделяет в качестве задачи ассоциированный с директивой блок операторов. Задача может выполняться немедленно после создания или быть отложенной на неопределённое время и выполняться по частям. Размер таких частей, а также порядок выполнения частей разных отложенных задач определяется реализацией. Возможные опции: if( условие ) — порождение новой задачи только при выполнении не- которого условия; если условие не выполняется, то задача будет вы- полнена текущей нитью и немедленно; untied — опция означает, что в случае откладывания задача может быть продолжена любой нитью из числа выполняющих данную парал- лельную область; если данная опция не указана, то задача может быть продолжена только породившей её нитью; default(private|firstprivate|shared|none) – всем переменным в задаче, которым явно не назначен класс, будет назначен класс private , firstprivate или shared соответственно; none означает, что всем пе- ременным в задаче класс должен быть назначен явно; в языке Си зада- ются только варианты shared или none ; private( список ) – задаёт список переменных, для которых порожда- ется локальная копия в каждой нити; начальное значение локальных копий переменных из списка не определено; firstprivate( список ) – задаёт список переменных, для которых по- рождается локальная копия в каждой нити; локальные копии перемен- ных инициализируются значениями этих переменных в нити-мастере; shared( список ) – задаёт список переменных, общих для всех нитей. 54 Для гарантированного завершения в точке вызова всех запущенных задач ис- пользуется директива taskwait Си: #pragma omp taskwait Фортран: !$omp taskwait Нить, выполнившая данную директиву, приостанавливается до тех пор, пока не будут завершены все ранее запущенные данной нитью независимые зада- чи. Задания • Могут ли функции omp_get_thread_num() и omp_get_num_threads() вернуть одинаковые значения на нескольких нитях одной параллель- ной области? • Можно ли распределить между нитями итерации цикла без использо- вания директивы for ( do ... [end do] )? • Можно ли одной директивой распределить между нитями итерации сразу нескольких циклов? • Возможно ли, что при статическом распределении итераций цикла ни- тям достанется разное количество итераций? • Могут ли при повторном запуске программы итерации распределяемо- го цикла достаться другим нитям? Если да, то при каких способах рас- пределения итераций? • Для чего может быть полезно указывать параметр chunk при способе распределения итераций guided ? • Можно ли реализовать параллельные секции без использования дирек- тив sections ( sections ... end sections ) и section ? • Как при выходе из параллельных секций разослать значение некоторой локальной переменной всем нитям, выполняющим данную параллель- ную область? • В каких случаях может пригодиться механизм задач? • Напишите параллельную программу, реализующую скалярное произ- ведение двух векторов. • Напишите параллельную программу, реализующую поиск максималь- ного значения вектора. 55 Синхронизация Целый набор директив в OpenMP предназначен для синхронизации работы нитей. Барьер Самый распространенный способ синхронизации в OpenMP – барьер. Он оформляется с помощью директивы barrier Си: #pragma omp barrier Фортран: !$omp barrier Нити, выполняющие текущую параллельную область, дойдя до этой дирек- тивы, останавливаются и ждут, пока все нити не дойдут до этой точки про- граммы, после чего разблокируются и продолжают работать дальше. Кроме того, для разблокировки необходимо, чтобы все синхронизируемые нити за- вершили все порождённые ими задачи ( task ). Пример 24 демонстрирует применение директивы barrier . Директива bar- rier используется для упорядочивания вывода от работающих нитей. Выда- чи с разных нитей " Сообщение 1" и " Сообщение 2" могут перемежаться в произвольном порядке, а выдача " Сообщение 3" со всех нитей придёт строго после двух предыдущих выдач. #include #include int main(int argc, char *argv[]) { #pragma omp parallel { printf(" Сообщение 1\n"); printf(" Сообщение 2\n"); #pragma omp barrier printf(" Сообщение 3\n"); } } Пример 24a. Директива barrier наязыкеСи. 56 program example24b include "omp_lib.h" !$omp parallel print *, " Сообщение 1" print *, " Сообщение 2" !$omp barrier print *, " Сообщение 3" !$omp end parallel end Пример 24b. Директива barrier наязыкеФортран. Директива ordered Директивы ordered ( ordered ... end ordered ) определяют блок внутри тела цикла, который должен выполняться в том порядке, в котором итерации идут в последовательном цикле. Си: #pragma omp ordered Фортран: !$omp ordered < блок операторов > !$omp end ordered Блок операторов относится к самому внутреннему из объемлющих циклов, а в параллельном цикле должна быть задана опция ordered . Нить, выполняю- щая первую итерацию цикла, выполняет операции данного блока. Нить, вы- полняющая любую следующую итерацию, должна сначала дождаться вы- полнения всех операций блока всеми нитями, выполняющими предыдущие итерации. Может использоваться, например, для упорядочения вывода от па- раллельных нитей. Пример 25 иллюстрирует применение директивы ordered и опции ordered Цикл for ( do ) помечен как ordered . Внутри тела цикла идут две выдачи – одна вне блока ordered , а вторая – внутри него. В результате первая выдача получается неупорядоченной, а вторая идёт в строгом порядке по возраста- нию номера итерации. 57 #include #include int main(int argc, char *argv[]) { int i, n; #pragma omp parallel private (i, n) { n=omp_get_thread_num(); #pragma omp for ordered for (i=0; i<5; i++) { printf(" Нить %d, итерация %d\n", n, i); #pragma omp ordered { printf("ordered: Нить %d, итерация %d\n", n, i); } } } } Пример 25a. Директива ordered иопция ordered наязыкеСи. program example25b include "omp_lib.h" integer i, n; !$omp parallel private (i, n) n=omp_get_thread_num(); !$omp do ordered do i=0, 4 print *, " Нить ", n, ", итерация ", i !$omp ordered print *, "ordered: Нить ", n, ", итерация ", i !$omp end ordered end do !$omp end parallel end Пример 25b. Директива ordered иопция ordered наязыкеФортран. Критические секции С помощью директив critical ( critical ... end critical ) оформляется критическая секция программы. Си: #pragma omp critical [(< имя _ критической _ секции >)] Фортран: !$omp critical [( имя )] < код критической секции > !$omp end critical [( имя )] 58 В каждый момент времени в критической секции может находиться не более одной нити. Если критическая секция уже выполняется какой-либо нитью, то все другие нити, выполнившие директиву для секции с данным именем, бу- дут заблокированы, пока вошедшая нить не закончит выполнение данной критической секции. Как только работавшая нить выйдет из критической секции, одна из заблокированных на входе нитей войдет в неё. Если на входе в критическую секцию стояло несколько нитей, то случайным образом выби- рается одна из них, а остальные заблокированные нити продолжают ожида- ние. Все неименованные критические секции условно ассоциируются с одним и тем же именем. Все критические секции, имеющие одно и тоже имя, рас- сматриваются единой секцией, даже если находятся в разных параллельных областях. Побочные входы и выходы из критической секции запрещены. Пример 26 иллюстрирует применение директивы |