С. антоновпараллельноепрограммированиесиспользованиемтехнологииopenMP
Скачать 0.55 Mb.
|
omp_get_dynamic() . Сначала распечатывается значение, полученное функ- цией omp_get_dynamic() – это позволяет узнать значение переменной OMP_DYNAMIC по умолчанию. Затем при помощи функции omp_set_dynamic() переменная OMP_DYNAMIC устанавливается в true , что подтверждает выдача ещё один раз значения функции omp_get_dynamic() Затем порождается параллельная область, выполняемая заданным количест- вом нитей ( 128 ). В параллельной области печатается реальное число выпол- няющих её нитей. Директива master позволяет обеспечить печать только процессом-мастером. В системах с динамическим изменением числа нитей выданное значение может отличаться от заданного ( 128 ). 20 #include #include int main(int argc, char *argv[]) { printf(" Значение OMP_DYNAMIC: %d\n", omp_get_dynamic()); omp_set_dynamic(1); printf(" Значение OMP_DYNAMIC: %d\n", omp_get_dynamic()); #pragma omp parallel num_threads(128) { #pragma omp master { printf(" Параллельная область , %d нитей \n", omp_get_num_threads()); } } } Пример 6a. Функции omp_set_dynamic() и omp_get_dynamic() наязыкеСи. program example6b include "omp_lib.h" print *, " Значение OMP_DYNAMIC: ", omp_get_dynamic() call omp_set_dynamic(.TRUE.) print *, " Значение OMP_DYNAMIC: ", omp_get_dynamic() !$omp parallel num_threads(128) !$omp master print *, " Параллельная область ,", omp_get_num_threads(), & " нитей " !$omp end master !$omp end parallel end Пример 6b. Функции omp_set_dynamic() и omp_get_dynamic() наязыкеФор- тран. Функция omp_get_max_threads() возвращает максимально допустимое чис- ло нитей для использования в следующей параллельной области. Си: int omp_get_max_threads(void); Фортран: integer function omp_get_max_threads() Функция omp_get_num_procs() возвращает количество процессоров, дос- тупных для использования программе пользователя на момент вызова. Нуж- но учитывать, что количество доступных процессоров может динамически изменяться. Си: int omp_get_num_procs(void); 21 Фортран: integer function omp_get_num_procs() Параллельные области могут быть вложенными; по умолчанию вложенная параллельная область выполняется одной нитью. Это управляется установ- кой переменной среды OMP_NESTED . Например, в Linux в командной оболочке bash разрешить вложенный параллелизм можно при помощи следующей ко- манды: export OMP_NESTED=true Изменить значение переменной OMP_NESTED можно с помощью вызова функ- ции omp_set_nested() Си: void omp_set_nested(int nested) Фортран: subroutine omp_set_nested(nested) logical nested Функция omp_set_nested() разрешает или запрещает вложенный паралле- лизм. На языке Си в качестве значения параметра задаётся 0 или 1 , а на языке Фортран – .FALSE. или .TRUE. Если вложенный параллелизм разрешён, то каждая нить, в которой встретится описание параллельной области, породит для её выполнения новую группу нитей. Сама породившая нить станет в но- вой группе нитью-мастером. Если система не поддерживает вложенный па- раллелизм, данная функция не будет иметь эффекта. Пример 7 демонстрирует использование вложенных параллельных областей и функции omp_set_nested() . Вызов функции omp_set_nested() перед первой частью разрешает использование вложенных параллельных областей. Для определения номера нити в текущей параллельной секции используются вызовы функции omp_get_thread_num() . Каждая нить внешней параллель- ной области породит новые нити, каждая из которых напечатает свой номер вместе с номером породившей нити. Далее вызов omp_set_nested() запре- щает использование вложенных параллельных областей. Во второй части вложенная параллельная область будет выполняться без порождения новых нитей, что и видно по получаемой выдаче. 22 #include #include int main(int argc, char *argv[]) { int n; omp_set_nested(1); #pragma omp parallel private(n) { n=omp_get_thread_num(); #pragma omp parallel { printf(" Часть 1, нить %d - %d\n", n, omp_get_thread_num()); } } omp_set_nested(0); #pragma omp parallel private(n) { n=omp_get_thread_num(); #pragma omp parallel { printf(" Часть 2, нить %d - %d\n", n, omp_get_thread_num()); } } } Пример 7a. ВложенныепараллельныеобластинаязыкеСи. program example7b include "omp_lib.h" integer n call omp_set_nested(.TRUE.) !$omp parallel private(n) n=omp_get_thread_num() !$omp parallel print *, " Часть 1, нить ", n, " - ", & omp_get_thread_num() !$omp end parallel !$omp end parallel call omp_set_nested(.FALSE.) !$omp parallel private(n) n=omp_get_thread_num() !$omp parallel print *, " Часть 2, нить ", n, " - ", & omp_get_thread_num() !$omp end parallel !$omp end parallel end Пример 7b. ВложенныепараллельныеобластинаязыкеФортран. Узнать значение переменной OMP_NESTED можно при помощи функции omp_get_nested() 23 Си: int omp_get_nested(void); Фортран: logical function omp_get_nested() Функция omp_in_parallel() возвращает 1 ( .TRUE. для языка Фортран), ес- ли она была вызвана из активной параллельной области программы. Си: int omp_in_parallel(void); Фортран: logical function omp_in_parallel() Пример 8 иллюстрирует применение функции omp_in_parallel() . Функция mode демонстрирует изменение функциональности в зависимости от того, вызвана она из последовательной или из параллельной области. В последова- тельной области будет напечатано " Последовательная область " , а в парал- лельной – " Параллельная область " #include #include void mode(void){ if(omp_in_parallel()) printf(" Параллельная область \n"); else printf(" Последовательная область \n"); } int main(int argc, char *argv[]) { mode(); #pragma omp parallel { #pragma omp master { mode(); } } } Пример 8a. Функция omp_in_parallel() наязыкеСи. 24 program example8b call mode() !$omp parallel !$omp master call mode() !$omp end master !$omp end parallel end subroutine mode() include "omp_lib.h" if(omp_in_parallel()) then print *, " Параллельная область " else print *, " Последовательная область " end if end Пример 8b. Функция omp_in_parallel() наязыкеФортран. Директива single Если в параллельной области какой-либо участок кода должен быть выпол- нен лишь один раз, то его нужно выделить директивами single ( single ... end single ). Си: #pragma omp single [ опция [[,] опция ]...] Фортран: !$omp single [ опция [[,] опция ]...] < код для одной нити > !$omp end single [ опция [[,] опция ]...] Возможные опции: private( список ) – задаёт список переменных, для которых порожда- ется локальная копия в каждой нити; начальное значение локальных копий переменных из списка не определено; firstprivate( список ) – задаёт список переменных, для которых по- рождается локальная копия в каждой нити; локальные копии перемен- ных инициализируются значениями этих переменных в нити-мастере; copyprivate( список ) – после выполнения нити, содержащей конст- рукцию single , новые значения переменных списка будут доступны всем одноименным частным переменным ( private и firstprivate ), описанным в начале параллельной области и используемым всеми её нитями; опция не может использоваться совместно с опцией nowait ; 25 переменные списка не должны быть перечислены в опциях private и firstprivate данной директивы single ; nowait – после выполнения выделенного участка происходит неявная барьерная синхронизация параллельно работающих нитей: их даль- нейшее выполнение происходит только тогда, когда все они достигнут данной точки; если в подобной задержке нет необходимости, опция nowait позволяет нитям, уже дошедшим до конца участка, продолжить выполнение без синхронизации с остальными. В программах на языке Си все опции указываются у директивы single , а в программах на языке Фортран опции private и firstprivate относятся к директиве single , а опции copyprivate и nowait – к директиве end single Какая именно нить будет выполнять выделенный участок программы, не специфицируется. Одна нить будет выполнять данный фрагмент, а все ос- тальные нити будут ожидать завершения её работы, если только не указана опция nowait . Необходимость использования директивы single часто воз- никает при работе с общими переменными. Пример 9 иллюстрирует применение директивы single вместе с опцией nowait . Сначала все нити напечатают текст " Сообщение 1" , при этом одна нить (не обязательно нить-мастер) дополнительно напечатает текст " Одна нить " . Остальные нити, не дожидаясь завершения выполнения области sin- gle , напечатают текст " Сообщение 2" . Таким образом, первое появление " Сообщение 2" в выводе может встретиться как до текста " Одна нить " , так и после него. Если убрать опцию nowait , то по окончании области single произойдёт барьерная синхронизация, и ни одна выдача " Сообщение 2" не может появиться до выдачи " Одна нить " #include int main(int argc, char *argv[]) { #pragma omp parallel { printf(" Сообщение 1\n"); #pragma omp single nowait { printf(" Одна нить \n"); } printf(" Сообщение 2\n"); } } Пример 9a. Директива single иопция nowait наязыкеСи. 26 program example9b !$omp parallel print *, " Сообщение 1" !$omp single print *, " Одна нить " !$omp end single nowait print *, " Сообщение 2" !$omp end parallel end Пример 9b. Директива single иопция nowait наязыкеФортран. Пример 10 иллюстрирует применение опции copyprivate . В данном приме- ре переменная n объявлена в параллельной области как локальная. Каждая нить присвоит переменной n значение, равное своему порядковому номеру, и напечатает данное значение. В области single одна из нитей присвоит пере- менной n значение 100 , и на выходе из области это значение будет присвоено переменной n на всех нитях. В конце параллельной области значение n печа- тается ещё раз и на всех нитях оно равно 100 #include #include int main(int argc, char *argv[]) { int n; #pragma omp parallel private(n) { n=omp_get_thread_num(); printf(" Значение n ( начало ): %d\n", n); #pragma omp single copyprivate(n) { n=100; } printf(" Значение n ( конец ): %d\n", n); } } Пример 10a. Опция copyprivate наязыкеСи. program example10b include "omp_lib.h" integer n !$omp parallel private(n) n=omp_get_thread_num() print *, " Значение n ( начало ): ", n !$omp single n=100; !$omp end single copyprivate(n) print *, " Значение n ( конец ): ", n !$omp end parallel end Пример 10b. Опция copyprivate наязыкеФортран. 27 Директива master Директивы master ( master ... end master ) выделяют участок кода, кото- рый будет выполнен только нитью-мастером. Остальные нити просто про- пускают данный участок и продолжают работу с оператора, расположенного следом за ним. Неявной синхронизации данная директива не предполагает. Си: #pragma omp master Фортран: !$omp master < код для нити - мастера > !$omp end master Пример 11 демонстрирует применение директивы master . Переменная n яв- ляется локальной, то есть каждая нить работает со своим экземпляром. Сна- чала все нити присвоят переменной n значение 1 . Потом нить-мастер присво- ит переменной n значение 2 , и все нити напечатают значение n . Затем нить- мастер присвоит переменной n значение 3 , и снова все нити напечатают зна- чение n . Видно, что директиву master всегда выполняет одна и та же нить. В данном примере все нити выведут значение 1 , а нить-мастер сначала выведет значение 2 , а потом - значение 3 #include int main(int argc, char *argv[]) { int n; #pragma omp parallel private(n) { n=1; #pragma omp master { n=2; } printf(" Первое значение n: %d\n", n); #pragma omp barrier #pragma omp master { n=3; } printf(" Второе значение n: %d\n", n); } } Пример 11a. Директива master наязыкеСи. 28 program example11b integer n !$omp parallel private(n) n=1 !$omp master n=2 !$omp end master print *, " Первое значение n: ", n !$omp barrier !$omp master n=3; !$omp end master print *, " Второе значение n: ", n !$omp end parallel end Пример 11b. Директива master наязыкеФортран. Задания • Определите, какое максимальное количество нитей позволяет породить для выполнения параллельных областей программы ваша система. • В каких случаях может быть необходимо использование опции if ди- рективы parallel ? • Определите, сколько процессоров доступно в вашей системе для вы- полнения параллельной части программы, и займите каждый из дос- тупных процессоров выполнением одной нити в рамках общей парал- лельной области. • При помощи трёх уровней вложенных параллельных областей породи- те 8 нитей (на каждом уровне параллельную область должны испол- нять 2 нити). Посмотрите, как будет исполняться программа, если за- претить вложенные параллельные области. • Чем отличаются директивы single и master ? • Может ли нить-мастер выполнить область, ассоциированную с дирек- тивой single ? • Может ли нить с номером 1 выполнить область, ассоциированную с директивой master ? 29 Модель данных Модель данных в OpenMP предполагает наличие как общей для всех нитей области памяти, так и локальной области памяти для каждой нити. В OpenMP переменные в параллельных областях программы разделяются на два основных класса: • shared (общие; все нити видят одну и ту же переменную); • private (локальные, приватные; каждая нить видит свой экземпляр данной переменной). Общая переменная всегда существует лишь в одном экземпляре для всей об- ласти действия и доступна всем нитям под одним и тем же именем. Объявле- ние локальной переменной вызывает порождение своего экземпляра данной переменной (того же типа и размера) для каждой нити. Изменение нитью значения своей локальной переменной никак не влияет на изменение значе- ния этой же локальной переменной в других нитях. Если несколько переменных одновременно записывают значение общей пе- ременной без выполнения синхронизации или если как минимум одна нить читает значение общей переменной и как минимум одна нить записывает значение этой переменной без выполнения синхронизации, то возникает си- туация так называемой «гонкиданных» (data race), при которой результат выполнения программы непредсказуем. По умолчанию, все переменные, порождённые вне параллельной области, при входе в эту область остаются общими ( |