Параллельное программирование с использованием OpenMP. Антонов (2009) (Параллельное программирование с использованием OpenMP. Антонов (2009).pdf), страница 9
Описание файла
PDF-файл из архива "Параллельное программирование с использованием OpenMP. Антонов (2009).pdf", который расположен в категории "". Всё это находится в предмете "суперкомпьютерное моделирование и технологии" из 11 семестр (3 семестр магистратуры), которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 9 страницы из PDF
Директива barrier используется для упорядочивания вывода от работающих нитей. Выдачи с разных нитей "Сообщение 1" и "Сообщение 2" могут перемежаться впроизвольном порядке, а выдача "Сообщение 3" со всех нитей придёт строгопосле двух предыдущих выдач.#include <stdio.h>#include <omp.h>int main(int argc, char{#pragma omp parallel{printf("Сообщениеprintf("Сообщение#pragma omp barrierprintf("Сообщение}}*argv[])1\n");2\n");3\n");Пример 24a.
Директива barrier на языке Си.55program example24binclude "omp_lib.h"!$omp parallelprint *, "Сообщение 1"print *, "Сообщение 2"!$omp barrierprint *, "Сообщение 3"!$omp end parallelendПример 24b. Директива barrier на языке Фортран.Директива orderedДирективы ordered (ordered ... end ordered) определяют блок внутритела цикла, который должен выполняться в том порядке, в котором итерацииидут в последовательном цикле.Си:#pragma omp orderedФортран:!$omp ordered<блок операторов>!$omp end orderedБлок операторов относится к самому внутреннему из объемлющих циклов, ав параллельном цикле должна быть задана опция ordered. Нить, выполняющая первую итерацию цикла, выполняет операции данного блока. Нить, выполняющая любую следующую итерацию, должна сначала дождаться выполнения всех операций блока всеми нитями, выполняющими предыдущиеитерации.
Может использоваться, например, для упорядочения вывода от параллельных нитей.Пример 25 иллюстрирует применение директивы ordered и опции ordered.Цикл for (do) помечен как ordered. Внутри тела цикла идут две выдачи –одна вне блока ordered, а вторая – внутри него. В результате первая выдачаполучается неупорядоченной, а вторая идёт в строгом порядке по возрастанию номера итерации.56#include <stdio.h>#include <omp.h>int main(int argc, char *argv[]){int i, n;#pragma omp parallel private (i, n){n=omp_get_thread_num();#pragma omp for orderedfor (i=0; i<5; i++){printf("Нить %d, итерация %d\n", n, i);#pragma omp ordered{printf("ordered: Нить %d, итерация %d\n", n, i);}}}}Пример 25a.
Директива ordered и опция ordered на языке Си.!$omp!$omp!$omp!$omp!$ompprogram example25binclude "omp_lib.h"integer i, n;parallel private (i, n)n=omp_get_thread_num();do ordereddo i=0, 4print *, "Нить ", n, ", итерация ", iorderedprint *, "ordered: Нить ", n, ", итерация ", iend orderedend doend parallelendПример 25b. Директива ordered и опция ordered на языке Фортран.Критические секцииС помощью директив critical (critical ... end critical) оформляетсякритическая секция программы.Си:#pragma omp critical [(<имя_критической_секции>)]Фортран:!$omp critical [(имя)]<код критической секции>!$omp end critical [(имя)]57В каждый момент времени в критической секции может находиться не болееодной нити. Если критическая секция уже выполняется какой-либо нитью, товсе другие нити, выполнившие директиву для секции с данным именем, будут заблокированы, пока вошедшая нить не закончит выполнение даннойкритической секции.
Как только работавшая нить выйдет из критическойсекции, одна из заблокированных на входе нитей войдет в неё. Если на входев критическую секцию стояло несколько нитей, то случайным образом выбирается одна из них, а остальные заблокированные нити продолжают ожидание.Все неименованные критические секции условно ассоциируются с одним итем же именем. Все критические секции, имеющие одно и тоже имя, рассматриваются единой секцией, даже если находятся в разных параллельныхобластях. Побочные входы и выходы из критической секции запрещены.Пример 26 иллюстрирует применение директивы critical.
Переменная nобъявлена вне параллельной области, поэтому по умолчанию является общей. Критическая секция позволяет разграничить доступ к переменной n.Каждая нить по очереди присвоит n свой номер и затем напечатает полученное значение.#include <stdio.h>#include <omp.h>int main(int argc, char *argv[]){int n;#pragma omp parallel{#pragma omp critical{n=omp_get_thread_num();printf("Нить %d\n", n);}}}Пример 26a. Директива critical на языке Си.58!$omp!$omp!$omp!$ompprogram example26binclude "omp_lib.h"integer nparallelcriticaln=omp_get_thread_num()print *, "Нить ", nend criticalend parallelendПример 26b. Директива critical на языке Фортран.Если бы в примере 26 не была указана директива critical, результат выполнения программы был бы непредсказуем.
С директивой critical порядок вывода результатов может быть произвольным, но это всегда будет набор одних и тех же чисел от 0 до OMP_NUM_THREADS-1. Конечно, подобногоже результата можно было бы добиться другими способами, например, объявив переменную n локальной, тогда каждая нить работала бы со своей копиейэтой переменной. Однако в исполнении этих фрагментов разница существенная.Если есть критическая секция, то в каждый момент времени фрагмент будетобрабатываться лишь какой-либо одной нитью.
Остальные нити, даже еслиони уже подошли к данной точке программы и готовы к работе, будут ожидать своей очереди. Если критической секции нет, то все нити могут одновременно выполнить данный участок кода. С одной стороны, критическиесекции предоставляют удобный механизм для работы с общими переменными.
Но с другой стороны, пользоваться им нужно осмотрительно, посколькукритические секции добавляют последовательные участки кода в параллельную программу, что может снизить её эффективность.Директива atomicЧастым случаем использования критических секций на практике являетсяобновление общих переменных. Например, если переменная sum являетсяобщей и оператор вида sum=sum+expr находится в параллельной областипрограммы, то при одновременном выполнении данного оператора несколькими нитями можно получить некорректный результат. Чтобы избежать такой ситуации можно воспользоваться механизмом критических секций илиспециально предусмотренной для таких случаев директивой atomic.Си:#pragma omp atomic59Фортран:!$omp atomicДанная директива относится к идущему непосредственно за ней операторуприсваивания (на используемые в котором конструкции накладываются достаточно понятные ограничения), гарантируя корректную работу с общей переменной, стоящей в его левой части.
На время выполнения оператора блокируется доступ к данной переменной всем запущенным в данный моментнитям, кроме нити, выполняющей операцию. Атомарной является только работа с переменной в левой части оператора присваивания, при этом вычисления в правой части не обязаны быть атомарными.Пример 27 иллюстрирует применение директивы atomic.
В данном примерепроизводится подсчет общего количества порожденных нитей. Для этого каждая нить увеличивает на единицу значение переменной count. Для того,чтобы предотвратить одновременное изменение несколькими нитями значения переменной, стоящей в левой части оператора присваивания, используется директива atomic.#include <stdio.h>#include <omp.h>int main(int argc, char *argv[]){int count = 0;#pragma omp parallel{#pragma omp atomiccount++;}printf("Число нитей: %d\n", count);}Пример 27a. Директива atomic на языке Си.program example27binclude "omp_lib.h"integer countcount = 0!$omp parallel!$omp atomiccount=count+1!$omp end parallelprint *, "Число нитей: ", countendПример 27b.
Директива atomic на языке Фортран.60Замки'(Один из вариантов синхронизации в OpenMP реализуется через механизмзамков (locks). В качестве замков используются общие целочисленные переменные (размер должен быть достаточным для хранения адреса). Данные переменные должны использоваться только как параметры примитивов синхронизации.Замок может находиться в одном из трёх состояний: неинициализированный,разблокированный или заблокированный. Разблокированный замок можетбыть захвачен некоторой нитью.
При этом он переходит в заблокированноесостояние. Нить, захватившая замок, и только она может его освободить, после чего замок возвращается в разблокированное состояние.Есть два типа замков: простые замки и множественные замки. Множественный замок может многократно захватываться одной нитью перед его освобождением, в то время как простой замок может быть захвачен только однажды.
Для множественного замка вводится понятие коэффициентазахваченности (nesting count). Изначально он устанавливается в ноль, прикаждом следующем захватывании увеличивается на единицу, а при каждомосвобождении уменьшается на единицу. Множественный замок считаетсяразблокированным, если его коэффициент захваченности равен нулю.Для инициализации простого или множественного замка используются соответственно функции omp_init_lock() и omp_init_nest_lock().Си:void omp_init_lock(omp_lock_t *lock);void omp_init_nest_lock(omp_nest_lock_t *lock);Фортран:subroutine omp_init_lock(svar)integer (kind=omp_lock_kind) svarsubroutine omp_init_nest_lock(nvar)integer (kind=omp_nest_lock_kind) nvarПосле выполнения функции замок переводится в разблокированное состояние.
Для множественного замка коэффициент захваченности устанавливаетсяв ноль.Функции omp_destroy_lock() и omp_destroy_nest_lock() используютсядля переведения простого или множественного замка в неинициализированное состояние.61Си:void omp_destroy_lock(omp_lock_t *lock);void omp_destroy_nest_lock(omp_nest_lock_t *lock);Фортран:subroutine omp_destroy_lock(svar)integer (kind=omp_lock_kind) svarsubroutine omp_destroy_nest_lock(nvar)integer (kind=omp_nest_lock_kind) nvarДлязахватываниязамкаomp_set_nest_lock().Си:используютсяфункцииomp_set_lock()иvoid omp_set_lock(omp_lock_t *lock);void omp_set_nest_lock(omp_nest_lock_t *lock);Фортран:subroutine omp_set_lock(svar)integer (kind=omp_lock_kind) svarsubroutine omp_set_nest_lock(nvar)integer (kind=omp_nest_lock_kind) nvarВызвавшая эту функцию нить дожидается освобождения замка, а затем захватывает его.
Замок при этом переводится в заблокированное состояние.Если множественный замок уже захвачен данной нитью, то нить не блокируется, а коэффициент захваченности увеличивается на единицу.Для освобождения замка используются функции omp_unset_lock() иomp_unset_nest_lock().Си:void omp_unset_lock(omp_lock_t *lock);void omp_unset_nest_lock(omp_lock_t *lock);Фортран:subroutine omp_unset_lock(svar)integer (kind=omp_lock_kind) svarsubroutine omp_unset_nest_lock(nvar)integer (kind=omp_nest_lock_kind) nvarВызов этой функции освобождает простой замок, если он был захвачен вызвавшей нитью.