2. Директивы

Директивы основаны на #pragma директивах, определенных в стандартах C и C++. Компиляторы, поддерживающие API OpenMP C и C++, будут включать параметр командной строки, который активирует и разрешает интерпретацию всех директив компилятора OpenMP.

Формат директив 2.1

Синтаксис директивы OpenMP официально указывается грамматикой в приложении C и неформальным образом следующим образом:

#pragma omp directive-name  [clause[ [,] clause]...] new-line

Каждая директива начинается с #pragma omp, чтобы уменьшить вероятность конфликта с другими директивами pragma (не OpenMP или расширениями поставщика для OpenMP) с теми же именами. Остальная часть директивы следует соглашениям стандартов C и C++ для директив компилятора. В частности, пробелы можно использовать до и после #, а иногда их необходимо использовать для разделения слов в директиве. Токены, следующие за #pragma omp, после предварительной обработки подвергаются замене макросов.

Директивы чувствительны к регистру. Порядок отображения предложений в директивах не является значительным. Предложения по директивам могут повторяться при необходимости, при условии ограничений, перечисленных в описании каждого предложения. Если список переменных отображается в предложении, он должен указывать только переменные. Для каждой директивы можно указать только одно имя директивы. Например, следующая директива не допускается:

/* ERROR - multiple directive names not allowed */
#pragma omp parallel barrier

Директива OpenMP применяется не более чем к одной последующей инструкции, которая должна быть структурированным блоком.

2.2 Условная компиляция

Имя _OPENMP макроса определяется реализациями, совместимыми с OpenMP, как десятичная константа гггг, которая будет годом и месяцем утвержденной спецификации. Этот макрос не должен быть предметом директивы #define или предварительной директивы #undef обработки.

#ifdef _OPENMP
iam = omp_get_thread_num() + index;
#endif

Если поставщики определяют расширения для OpenMP, они могут указать дополнительные предопределенные макросы.

2.3 параллельная конструкция

Следующая директива определяет параллельный регион, который является областью программы, которая выполняется многими потоками параллельно. Эта директива является базовой конструкцией, которая запускает параллельное выполнение.

#pragma omp parallel [clause[ [, ]clause] ...] new-line   structured-block

Пункт является одним из следующих:

  • if( скалярное выражение)
  • private( список переменных)
  • firstprivate( список переменных)
  • default(shared | none)
  • shared( список переменных)
  • copyin( список переменных)
  • reduction( оператор:список переменных)
  • num_threads( целочисленное выражение)

Когда поток подходит к параллельной конструкции, создается команда потоков, если выполняется один из следующих случаев и он истинно:

  • Отсутствует условие if.
  • Выражение if оценивается как ненулевое значение.

Этот поток становится главным потоком команды с числом потоков 0, а все потоки в команде, включая главный поток, параллельно выполняют регион. Если значение if выражения равно нулю, регион сериализуется.

Чтобы определить количество запрошенных потоков, будут последовательно рассмотрены следующие правила. Первое правило, условие которого выполняется, будет применено:

  1. num_threads Если условие присутствует, то значение целочисленного выражения — количество запрошенных потоков.

  2. omp_set_num_threads Если функция библиотеки была вызвана, то значение аргумента в последнем выполненном вызове — это количество запрошенных потоков.

  3. Если определена переменная среды, то значение этой переменной OMP_NUM_THREADS среды — это количество запрошенных потоков.

  4. Если ни один из приведенных выше методов не используется, то количество запрошенных потоков определяется реализацией.

num_threads Если условие присутствует, оно заменяет количество потоков, запрашиваемых omp_set_num_threads функцией библиотеки или OMP_NUM_THREADS переменной среды, только для параллельного региона, к которому оно применяется. Более поздние параллельные регионы не затрагиваются этим.

Количество потоков, выполняющих параллельный регион, также зависит от того, включена ли динамическая корректировка количества потоков. Если динамическая корректировка отключена, запрошенное количество потоков будет выполнять параллельный регион. Если включена динамическая корректировка, запрашиваемое количество потоков — максимальное количество потоков, которые могут выполнять параллельный регион.

Если параллельная область встречается, когда динамическая корректировка числа потоков отключена, и запрошенное для параллельной области количество потоков превышает число, которое может предоставить система исполнения, поведение программы определяется в зависимости от реализации. Реализация может, например, прерывать выполнение программы или сериализовать параллельный регион.

omp_set_dynamic Функцию библиотеки и переменную среды можно использовать для включения и OMP_DYNAMIC отключения динамической корректировки количества потоков.

Количество физических процессоров, фактически размещающих потоки в любой момент времени, определяется реализацией. После создания количество потоков в команде остается постоянным в течение этого параллельного региона. Его можно изменить либо явным образом пользователем, либо автоматически средой выполнения из одной параллельной области в другую.

Инструкции, содержащиеся в динамическом контексте параллельного региона, выполняются каждым потоком, и каждый поток может выполнять блок инструкций, отличающийся от блоков других потоков. Директивы, возникающие вне лексической степени параллельного региона, называются потерянными директивами.

В конце параллельного региона существует подразумеваемый барьер. Только главный поток команды продолжает выполняться в конце параллельной области.

Если поток в команде, выполняющей параллельную область, наталкивается на другую параллельную конструкцию, он создает новую команду и становится её мастером. Вложенные параллельные регионы сериализуются по умолчанию. В результате по умолчанию вложенный параллельный регион выполняется командой, состоящей из одного потока. Поведение по умолчанию может быть изменено с помощью функции omp_set_nested библиотеки среды выполнения или переменной OMP_NESTEDсреды. Однако число потоков в команде, выполняющей вложенный параллельный регион, определяется реализацией.

Ограничения директивы parallel приведены следующим образом:

  • Не более одного if предложения может отображаться в директиве.

  • Не указано, происходят ли побочные эффекты внутри if выражения или num_threads выражения.

  • Выполнение throw в параллельном регионе должно привести к возобновлению выполнения в динамической степени одного структурированного блока, и его необходимо поймать тем же потоком, который вызвал исключение.

  • В директиве может быть только одно num_threads условие. Выражение num_threads вычисляется вне контекста параллельного региона и должно оцениваться как положительное целочисленное значение.

  • Порядок оценки if и num_threads условий не определен.

Перекрестные ссылки

Конструкции разделения работы

Конструкция разделения работы распределяет выполнение связанного выражения среди членов команды, которые с ней сталкиваются. Директивы общего доступа к работе не запускают новые потоки, и нет подразумеваемого барьера для входа в конструкцию общего доступа к работе.

Последовательность конструкций разделения работы и barrier директив, с которыми сталкиваются, должна быть одинаковой для каждого потока в команде.

OpenMP определяет следующие конструкции общего доступа к работе, и эти конструкции описаны в следующих разделах:

2.4.1 для конструкции

Директива for определяет конструкцию итеративного разделения труда, которая указывает, что итерации связанной петли будут выполняться параллельно. Итерации for цикла распределяются по потокам, которые уже существуют в команде, выполняющей параллельную конструкцию, к которой она привязывается. for Синтаксис конструкции выглядит следующим образом:

#pragma omp for [clause[[,] clause] ... ] new-line for-loop

Пункт является одним из следующих:

  • private( список переменных)
  • firstprivate( список переменных)
  • lastprivate( список переменных)
  • reduction( оператор:список переменных)
  • ordered
  • schedule( kind [,chunk_size])
  • nowait

Директива for устанавливает ограничения на структуру соответствующего for цикла. В частности, соответствующий for цикл должен иметь каноническую форму:

for ( init-expr;var logical-op b;incr-expr)

init-expr
Один из следующих:

  • var = фунт
  • целочисленный тип var = lb

incr-expr
Один из следующих:

  • ++ var
  • var++
  • -- var
  • Var--
  • var+=incr
  • var-=incr
  • var=var+incr
  • var=incr+var
  • var=var-incr

var
Переменная со знаком (целочисленная). Если эта переменная в противном случае была бы общей, она неявно становится приватной на время действия for. Не изменяйте эту переменную в тексте инструкции for . Если переменная не указана lastprivate, его значение после цикла не определено.

логическая операция
Один из следующих:

  • <
  • <=
  • >
  • >=

lb, b и incr
Инвариантные целочисленные выражения цикла. Во время оценки этих выражений синхронизация отсутствует, поэтому любые вычисляемые побочные эффекты создают неопределенные результаты.

Каноническая форма позволяет вычислять количество итераций цикла при входе в цикл. Это вычисление производится со значениями типа var, после интегральных преобразований. В частности, если значение blb-+incr не может быть представлено в этом типе, результат является неопределенным. Кроме того, если логическая операция< или <=, то incr-expr должен приводить к тому, чтобы var увеличиваться при каждой итерации цикла. Если логический оп равен > или >=, то incr-expr должен уменьшать var с каждой итерацией цикла.

Предложение schedule указывает, как итерации for цикла разделяются между потоками команды. Правильность программы не должна зависеть от того, какой поток выполняет определенную итерацию. Значение chunk_size, если указано, должно быть инвариантным целочисленным выражением цикла с положительным значением. Во время оценки этого выражения синхронизация отсутствует, поэтому любые оцененные побочные эффекты создают неопределенные результаты. Тип расписания может быть одним из следующих значений:

Таблица 2-1: schedule значения типа условия

значение Описание
статичный При schedule(static,указании chunk_size) итерации делятся на блоки размера, указанного chunk_size. Блоки статически назначаются потокам в команде в циклическом режиме в порядке числа потоков. Если chunk_size не указано, пространство итерации делится на блоки, которые приблизительно равны по размеру, при этом каждому потоку назначается по одному блоку.
динамический При schedule(dynamic,указании chunk_size) итерации делятся на ряд блоков, каждый из которых содержит chunk_size итерации. Каждому блоку назначается поток, который ожидает задания. Поток выполняет блок итерации, а затем ожидает следующего назначения, пока не будут назначены блоки. Последний блок, который будет назначен, может иметь меньшее количество итераций. Если chunk_size не задано, значение по умолчанию равно 1.
направляемый При schedule(guided,указании chunk_size) итерации назначаются потокам в блоках, размеры которых постепенно уменьшаются. Когда поток завершает назначенный блок итераций, ему динамически назначается еще один блок, пока не останется никаких. При значении параметра chunk_size, равном 1, размер каждого блока приблизительно равен количеству неназначенных итераций, делённому на количество потоков. Эти размеры почти экспоненциально снижаются до 1. Для chunk_size со значением k больше 1 размер уменьшается почти экспоненциально до k, за исключением того, что последний блок может иметь меньше итерации k. Если chunk_size не задано, значение по умолчанию равно 1.
среда выполнения Когда указано schedule(runtime), решение о планировании откладывается до времени выполнения. Тип расписания и размер блоков можно выбрать во время выполнения, задав переменную среды OMP_SCHEDULE. Если эта переменная среды не задана, то результирующее расписание определяется реализацией. При указании schedule(runtime) не следует указывать chunk_size.

В отсутствие явно определенного schedule условия значение schedule определяется реализацией.

Программа, совместимая с OpenMP, не должна полагаться на определенное расписание для правильного выполнения. Программа не должна полагаться на тип расписания, соответствующий точно описанию, приведенному выше, так как можно иметь варианты в реализации одного и того же типа расписания в разных компиляторах. Описания можно использовать для выбора расписания, подходящего для конкретной ситуации.

Условие ordered должно присутствовать при ordered связывании директив к for конструкции.

В конце конструкции for существует неявный барьер, если только не указано предложение nowait.

Ограничения директивы for приведены следующим образом:

  • Цикл for должен быть структурированным блоком, и, кроме того, его выполнение не должно быть завершено оператором break .

  • Значения контрольных выражений for цикла, связанного с for директивой, должны быть одинаковыми для всех потоков в команде.

  • Переменная for итерации цикла должна иметь целочисленный тип со знаком.

  • В директиве for может отображаться только одно schedule условие.

  • На директиве for может быть только одно условие ordered.

  • В директиве for может отображаться только одно nowait условие.

  • Не уточняется, возникают ли или как часто появляются побочные эффекты в выражениях chunk_size, lb, b или incr.

  • Значение выражения chunk_size должно быть одинаковым для всех потоков в команде.

Перекрестные ссылки

Структура разделов 2.4.2

Директива sections определяет неитеративную конструкцию коллективного использования ресурсов, которая задает набор конструкций, подлежащих распределению между потоками в команде. Каждый раздел выполняется один раз потоком в команде. Синтаксис sections директивы выглядит следующим образом:

#pragma omp sections [clause[[,] clause] ...] new-line
   {
   [#pragma omp section new-line]
      structured-block
   [#pragma omp section new-linestructured-block ]
...
}

Пункт является одним из следующих:

  • private( список переменных)
  • firstprivate( список переменных)
  • lastprivate( список переменных)
  • reduction( оператор:список переменных)
  • nowait

Перед каждым разделом section предшествует директива, хотя section директива является необязательной для первого раздела. Директивы section должны находиться в лексическом объёме директивы sections. Существует неявный барьер в конце конструкции sections, если nowait не указано.

Ограничения директивы sections приведены следующим образом:

  • Директива section не должна отображаться вне лексической степени директивы sections .

  • В директиве nowait может содержаться только одна sections клауза.

Перекрестные ссылки

  • private, firstprivate, lastprivate и reduction предложения (раздел 2.7.2)

2.4.3 Единичная конструкция

Директива single определяет конструкцию, указывающую, что связанный структурированный блок выполняется только одним потоком в команде (не обязательно главным потоком). Синтаксис single директивы выглядит следующим образом:

#pragma omp single [clause[[,] clause] ...] new-linestructured-block

Пункт является одним из следующих:

  • private( список переменных)
  • firstprivate( список переменных)
  • copyprivate( список переменных)
  • nowait

После конструкции single существует неявный барьер, если не указано nowait условие.

Ограничения директивы single приведены следующим образом:

  • В директиве nowait может содержаться только одна single клауза.
  • Предложение copyprivate не должно использоваться с предложением nowait .

Перекрестные ссылки

2.5 Объединенные конструкции параллельных распределений работы

Объединенные конструкции совместного использования работ — это сокращения для указания параллельного региона, содержащего только одну конструкцию совместного использования. Семантика этих директив аналогична явному указанию директивы parallel, за которой следует одна конструкция распределения задач.

В следующих разделах описываются объединенные параллельные конструкции разделения работы.

Параллель 2.5.1 для конструкции

Директива parallel for — это ярлык для parallel региона, содержащего только одну for директиву. Синтаксис parallel for директивы выглядит следующим образом:

#pragma omp parallel for [clause[[,] clause] ...] new-linefor-loop

Эта директива разрешает все пункты директивы parallel и директивы for, кроме пункта nowait, с одинаковым значением и ограничениями. Семантика аналогична явному указанию директивы parallel, за которой сразу следует директива for.

Перекрестные ссылки

Конструкция параллельных разделов 2.5.2

Директива parallel sections предоставляет упрощённую форму для указания региона parallel, имеющего только одну директиву sections. Семантика аналогична явному указанию директивы parallel, за которой сразу следует директива sections. Синтаксис parallel sections директивы выглядит следующим образом:

#pragma omp parallel sections  [clause[[,] clause] ...] new-line
   {
   [#pragma omp section new-line]
      structured-block
   [#pragma omp section new-linestructured-block  ]
   ...
}

Условие может быть одним из условий, принятых директивами parallel и sections, за исключением условия nowait.

Перекрестные ссылки

Директивы 2.6 Основной и синхронизация

В следующих разделах описано:

2.6.1 главный конструктор

Директива master определяет конструкцию, указывающую структурированный блок, выполняемый главным потоком команды. Синтаксис master директивы выглядит следующим образом:

#pragma omp master new-linestructured-block

Другие потоки в команде не выполняют связанный структурированный блок. Нет подразумеваемого барьера при входе или выходе из основной структуры.

Критическая конструкция 2.6.2

Директива critical определяет конструкцию, которая ограничивает выполнение связанного структурированного блока одним потоком за раз. Синтаксис critical директивы выглядит следующим образом:

#pragma omp critical [(name)]  new-linestructured-block

Необязательное имя может использоваться для идентификации критического региона. Идентификаторы, используемые для идентификации критического региона, имеют внешнюю связь и находятся в пространстве имен, отдельном от пространств имен, используемых метками, тегами, элементами и обычными идентификаторами.

Поток ожидает в начале критической секции, пока ни один другой поток не выполняет критическую секцию (в любой части программы) с тем же именем. Все неназванные директивы сопоставляются с тем же неопределенным critical именем.

Директива барьера 2.6.3

Директива barrier синхронизирует все потоки в команде. При обнаружении каждый поток в команде ожидает, пока все остальные не достигли этой точки. Синтаксис barrier директивы выглядит следующим образом:

#pragma omp barrier new-line

После того как все потоки в команде столкнулись с барьером, каждый поток в команде начинает выполнять инструкции после директивы барьера параллельно. Поскольку директива barrier не имеет оператора языка C в составе её синтаксиса, существуют некоторые ограничения на её размещение в программе. Дополнительные сведения о формальной грамматике см . в приложении C. В приведенном ниже примере показаны эти ограничения.

/* ERROR - The barrier directive cannot be the immediate
*          substatement of an if statement
*/
if (x!=0)
   #pragma omp barrier
...

/* OK - The barrier directive is enclosed in a
*      compound statement.
*/
if (x!=0) {
   #pragma omp barrier
}

Атомарная конструкция 2.6.4

Директива atomic гарантирует, что определенное расположение памяти обновляется атомарно, а не подвергает его возможности нескольких одновременных потоков записи. Синтаксис atomic директивы выглядит следующим образом:

#pragma omp atomic new-lineexpression-stmt

Операторная строка должна иметь одну из следующих форм:

  • x binop=expr
  • X++
  • ++ X
  • X--
  • -- X

В предыдущих выражениях:

  • x — это выражение lvalue с скалярным типом.

  • expr — это выражение с скалярным типом, и он не ссылается на объект, назначенный x.

  • binop не является перегруженным оператором и является одним из +, *, -, /, &, ^, |, << или >>.

Хотя в зависимости от реализации может определяться, заменяются ли все директивы atomic директивами critical с тем же уникальным именем, директива atomic позволяет лучше оптимизировать. Часто доступны аппаратные инструкции, которые могут выполнять атомарное обновление с минимальными затратами.

Только загрузка и хранение объекта, указанного x, являются атомарными; оценка экспра не атомарна. Чтобы избежать условий гонки, все обновления расположения параллельно должны быть защищены директивой atomic , за исключением тех, которые, как известно, свободны от условий гонки.

Ограничения директивы atomic приведены следующим образом:

  • Все атомарные ссылки на расположение хранилища x в программе должны иметь совместимый тип.

Примеры

extern float a[], *p = a, b;
/* Protect against races among multiple updates. */
#pragma omp atomic
a[index[i]] += b;
/* Protect against races with updates through a. */
#pragma omp atomic
p[i] -= 1.0f;

extern union {int n; float x;} u;
/* ERROR - References through incompatible types. */
#pragma omp atomic
u.n++;
#pragma omp atomic
u.x -= 1.0f;

Директива очистки 2.6.5

Директива flush, будь то явная или подразумеваемая, указывает точку последовательности 'межпоточная', в которой реализация требует обеспечить, чтобы все потоки в команде имели согласованное представление определённых объектов (указанных ниже) в памяти. Это означает, что предыдущие оценки выражений, ссылающихся на эти объекты, завершены и последующие оценки еще не начались. Например, компиляторы должны восстановить значения объектов из регистров в память, а оборудование может потребоваться удалить буферы записи в память и перезагрузить значения объектов из памяти.

Синтаксис flush директивы выглядит следующим образом:

#pragma omp flush [(variable-list)]  new-line

Если объекты, требующие синхронизации, могут быть назначены переменными, эти переменные можно указать в необязательном списке переменных. Если указатель присутствует в списке переменных, сам указатель удаляется, а не объект, на который ссылается указатель.

Директива flushбез списка переменных синхронизирует все общие объекты, кроме недоступных объектов с автоматическим сроком хранения. (Скорее всего, это больше накладных расходов, чем flush с переменным списком.) Директива flush без списка переменных подразумевается для следующих директив:

  • barrier
  • При входе и выходе из critical
  • При входе в ordered и выходе из него
  • При входе в и выходе из parallel
  • При выходе из for
  • На выходе из sections
  • На выходе из single
  • При входе и выходе из parallel for
  • При входе и выходе из parallel sections

Директива не подразумевается, если присутствует nowait условие. Следует отметить, что flush директива не подразумевается для любого из следующих вариантов:

  • При входе в for
  • При входе в или выходе из master
  • При входе в sections
  • При входе в single

Ссылка, которая обращается к значению объекта с типом, квалифицированным как volatile, ведёт себя так, как если бы на предыдущей точке последовательности существовала директива flush, указывающая этот объект. Ссылка, изменяющая значение объекта с переменным типом, ведет себя так, как если бы существовала flush директива, указывающая этот объект в последующей точке последовательности.

Поскольку директива flush не имеет оператора языка C в составе её синтаксиса, существуют некоторые ограничения на её размещение в программе. Дополнительные сведения о формальной грамматике см . в приложении C. В приведенном ниже примере показаны эти ограничения.

/* ERROR - The flush directive cannot be the immediate
*          substatement of an if statement.
*/
if (x!=0)
   #pragma omp flush (x)
...

/* OK - The flush directive is enclosed in a
*      compound statement
*/
if (x!=0) {
   #pragma omp flush (x)
}

Ограничения директивы flush приведены следующим образом:

  • Переменная, указанная в директиве flush , не должна иметь ссылочный тип.

Упорядоченная конструкция 2.6.6

Структурированный блок после ordered директивы выполняется в том порядке, в котором будут выполняться итерации в последовательном цикле. Синтаксис ordered директивы выглядит следующим образом:

#pragma omp ordered new-linestructured-block

Директива ordered должна находиться в динамической области действия for или parallel for конструкции. Директива или конструкция , с которой она связывается, должна иметь условие , указанное в разделе 2.4.1. В выполнении конструкции for или parallel for с предложением ordered, конструкции ordered выполняются строго в порядке, в котором они будут выполняться в последовательном выполнении цикла.

Ограничения директивы ordered приведены следующим образом:

  • Итерация цикла с for конструкцией не должна выполнять одну и ту же упорядоченную директиву несколько раз, и она не должна выполнять несколько ordered директив.

Среда данных 2.7

В этом разделе представлена директива и несколько предложений для управления средой данных во время выполнения параллельных регионов, как показано ниже.

  • Директива threadprivate предоставляется для создания переменных области файлов, пространства имен или статических переменных блочного блока, локальных для потока.

  • Условия, которые могут быть указаны в директивах для контролирования атрибутов совместного использования переменных на протяжении параллельных или рабочих конструкций, описаны в разделе 2.7.2.

Директива threadprivate 2.7.1

Директива threadprivate делает переменные в области видимости файла, области видимости пространства имен или области видимости статического блока, указанные в списке переменных, приватными для потока. Переменный список — это разделенный запятыми список переменных, у которых нет неполного типа. Синтаксис threadprivate директивы выглядит следующим образом:

#pragma omp threadprivate(variable-list) new-line

Каждая копия переменной threadprivate инициализируется один раз в неопределенной точке в программе до первой ссылки на нее, а также обычным образом (т. е. как главная копия будет инициализирована в последовательном выполнении программы). Обратите внимание, что если объект ссылается на явный инициализатор threadprivate переменной, а значение объекта изменяется до первой ссылки на копию переменной, то поведение не указано.

Как и в случае с любой частной переменной, поток не должен ссылаться на копию объекта другого threadprivate потока. Во время сериальных и главных регионов программы ссылки будут указывать на копию объекта в главном потоке.

После выполнения первого параллельного региона данные в threadprivate объектах гарантированно сохраняются, только если механизм динамических потоков отключен, а число потоков остается неизменным для всех параллельных регионов.

Ограничения для директивы threadprivate таковы:

  • threadprivate Директива для переменных области файлов или пространства имен должна отображаться вне любого определения или объявления, и должна лексически предшествовать всем ссылкам на любые переменные в списке.

  • Каждая переменная в списке переменных директивы на уровне файла или пространства имен должна ссылаться на объявление переменной на уровне файла или пространства имен, которое лексически предшествует директиве.

  • threadprivate Директива для переменных статического блока должна отображаться в области переменной, а не в вложенной области. Директива должна лексически предшествовать всем ссылкам на любые переменные в списке.

  • Каждая переменная в списке переменных директивы threadprivate, находящейся в области блока, должна ссылаться на объявление переменной в той же области, которое лексически предшествует директиве. Объявление переменной должно использовать описатель статического класса хранилища.

  • Если переменная указана в директиве в одной единице перевода, она должна быть указана в threadprivatethreadprivate директиве в каждой единице перевода, в которой она объявлена.

  • Переменная threadprivate не должна присутствовать ни в каком предложении, кроме предложения copyin, copyprivate, schedule, num_threads или if.

  • Адрес переменной threadprivate не является константой адресов.

  • Переменная threadprivate не должна иметь неполный тип или ссылочный тип.

  • Переменная threadprivate с типом класса, отличающимся от POD, должна иметь доступный и однозначный конструктор копирования, если она объявлена с явным инициализатором.

В следующем примере показано, как изменить переменную, которая отображается в инициализаторе, может вызвать неопределенное поведение, а также как избежать этой проблемы с помощью вспомогательного объекта и конструктора копирования.

int x = 1;
T a(x);
const T b_aux(x); /* Capture value of x = 1 */
T b(b_aux);
#pragma omp threadprivate(a, b)

void f(int n) {
   x++;
   #pragma omp parallel for
   /* In each thread:
   * Object a is constructed from x (with value 1 or 2?)
   * Object b is copy-constructed from b_aux
   */
   for (int i=0; i<n; i++) {
      g(a, b); /* Value of a is unspecified. */
   }
}

Перекрестные ссылки

Предложения атрибутов общего доступа к данным 2.7.2

Несколько директив принимают условия, которые позволяют пользователю управлять атрибутами распределения переменных в пределах области. Предложения атрибутов совместного использования применяются только к переменным в лексическом контексте директивы, где указано данное предложение. Не все приведенные ниже предложения разрешены для всех директив. Список условий, которые допустимы для конкретной директивы, описывается вместе с этой директивой.

Если переменная отображается при обнаружении параллельной или рабочей общей конструкции, а переменная не указана в предложении или threadprivate директиве атрибута общего доступа, то переменная является общей. Статические переменные, объявленные в динамической степени параллельного региона, являются общими. Память, выделенная в куче (например, при использовании malloc() в C или C++ или оператора new в C++), разделяется. (Однако указатель на эту память может быть частным или общим.) Переменные с автоматической длительностью хранения, объявленной в динамической степени параллельного региона, являются частными.

Большинство предложений принимают аргумент списка переменных, который представляет собой разделенный запятыми список видимых переменных. Если переменная, указанная в предложении атрибута общего доступа к данным, имеет тип, производный от шаблона, и в программе нет других ссылок на нее, поведение не определено.

Все переменные, отображаемые в предложениях директив, должны быть видимыми. Условия могут повторяться по мере необходимости, но переменная не может быть указана в нескольких условиях, за исключением случаев, когда переменная может быть указана как в условии firstprivate, так и в условии lastprivate.

В следующих разделах описаны условия атрибутов совместного использования данных:

2.7.2.1 Частный

Предложение private объявляет переменные в списке переменных частным для каждого потока в команде. private Синтаксис предложения выглядит следующим образом:

private(variable-list)

Поведение переменной, указанной private в предложении, выглядит следующим образом. Для конструкции выделяется новый объект с автоматическим сроком хранения. Размер и выравнивание нового объекта определяются типом переменной. Это выделение происходит один раз для каждого потока в команде, и при необходимости вызывается конструктор по умолчанию для объекта класса; в противном случае начальное значение является неопределенным. Исходный объект, на который ссылается переменная, имеет неопределенное значение при входе в конструкцию, не должно быть изменено в динамической степени конструкции и имеет неопределенное значение при выходе из конструкции.

В лексической степени конструкции директив переменная ссылается на новый частный объект, выделенный потоком.

Ограничения предложения private приведены следующим образом:

  • Переменная с типом класса, указанным в private предложении, должна иметь доступный, однозначно заданный конструктор по умолчанию.

  • Переменная, указанная в private предложении, не должна иметь тип с модификатором const, если только она не является типом класса с элементом mutable.

  • Переменная, указанная в предложении private , не должна иметь неполный тип или ссылочный тип.

  • Переменные, появляющиеся в reduction части директивы, не могут быть указаны в parallel или private части директивы общего управления работой, которая связана с параллельной конструкцией.

2.7.2.2 firstprivate

Предложение firstprivate предоставляет супермножество функциональных возможностей, предоставляемых предложением private . firstprivate Синтаксис предложения выглядит следующим образом:

firstprivate(variable-list)

Переменные, указанные в списке переменных, имеют семантику private условия, как описано в разделе 2.7.2.1. Инициализация или построение происходит так, как если бы оно выполнялось один раз на поток, до выполнения потоком конструкции. firstprivate Для предложения параллельной конструкции начальное значение нового частного объекта является значением исходного объекта, который существует непосредственно перед параллельной конструкцией для потока, который встречает его. firstprivate Для предложения firstprivate конструкции разделения работы начальное значение нового частного объекта для каждого потока, выполняющего конструкцию, равно значению исходного объекта, существовавшего до того момента, когда тот же поток встречает конструкцию. Кроме того, для объектов C++ новый частный объект для каждого потока копируется из исходного объекта.

Ограничения предложения firstprivate приведены следующим образом:

  • Переменная, указанная в предложении firstprivate , не должна иметь неполный тип или ссылочный тип.

  • Переменная с типом класса, указанного как firstprivate, должна иметь доступный и однозначный конструктор копирования.

  • Переменные, которые являются частными в параллельном регионе или которые указаны в reduction секции parallel директивы, не могут быть указаны в firstprivate секции директивы распределения работы, которая привязывается к параллельной конструкции.

2.7.2.3 lastprivate

Предложение lastprivate предоставляет супермножество функциональных возможностей, предоставляемых предложением private . lastprivate Синтаксис предложения выглядит следующим образом:

lastprivate(variable-list)

Переменные, указанные в списке переменных, имеют private семантику предложения. lastprivate Когда предложение появляется в директиве, определяющей конструкцию разделения работы, значение каждой lastprivate переменной из последовательной последней итерации связанного цикла или лексически последнего раздела директивы назначается исходному объекту переменной. Переменные, которым не было назначено значение в последнюю итерацию for или parallel for, или в лексически последний раздел sections или parallel sections, имеют неопределенные значения после конструкции. Неназначенные подобъекты также имеют неопределенное значение после конструирования.

Ограничения предложения lastprivate приведены следующим образом:

  • Все ограничения для private распространяются.

  • Переменная с типом класса, указанным как lastprivate должна иметь доступный, однозначное оператор назначения копирования.

  • Переменные, которые являются частными в параллельном регионе или находятся в reduction предложении директивы parallel, не могут быть указаны в lastprivate предложении директивы разделения работы, которая привязывается к параллельной конструкции.

2.7.2.4 Общий доступ

Эта клаузула разделяет переменные, которые перечислены в списке переменных, между всеми потоками в команде. Все потоки в команде обращаются к одной области хранения для shared переменных.

shared Синтаксис предложения выглядит следующим образом:

shared(variable-list)

По умолчанию 2.7.2.5

Предложение default позволяет пользователю влиять на атрибуты обмена данными переменных. default Синтаксис предложения выглядит следующим образом:

default(shared | none)

Указание default(shared) эквивалентно явному перечислению каждой текущей видимой переменной в выражении shared, если она не является threadprivate или const квалифицированной. В отсутствие явного default предложения поведение по умолчанию совпадает с тем, как если бы default(shared) было указано.

При указании default(none) требуется, чтобы для каждой ссылки на переменную в лексической степени параллельной конструкции должно быть по крайней мере одно из следующих значений:

  • Переменная явно указана в предложении атрибута общего доступа к данным конструкции, содержащей ссылку.

  • Переменная объявляется в параллельной конструкции.

  • Переменная threadprivate.

  • Переменная имеет const-атрибут тип.

  • Переменная является переменной управления циклом для for цикла, который следует сразу после директивы for или parallel for, и ссылка на переменную появляется внутри цикла.

Указание переменной в условии firstprivate, lastprivate или reduction директивы приводит к неявной ссылке на переменную в охватывающем контексте. Такие неявные ссылки также относятся к указанным выше требованиям.

В директиве parallel может быть указано только одно default условие.

Атрибут общего доступа к данным по умолчанию переменной можно переопределить с помощью предложений private, firstprivate, lastprivate, reduction и shared, как показано в следующем примере:

#pragma  omp  parallel  for  default(shared)  firstprivate(i)\
   private(x)  private(r)  lastprivate(i)

Сокращение 2.7.2.6

Это предложение выполняет сокращение скалярных переменных, которые отображаются в списке переменных с помощью оператора op. reduction Синтаксис предложения выглядит следующим образом:

reduction( op:список переменных)

Сокращение обычно указывается для инструкции с одной из следующих форм:

  • X=xopexpr
  • xbinop=expr
  • x=expropx (за исключением вычитания)
  • X++
  • ++ X
  • X--
  • -- X

где:

x
Одна из переменных сокращения, указанных в списке.

список переменных
Разделенный запятыми список скалярных переменных сокращения.

expr
Выражение с скалярным типом, не ссылающимся на x.

op
Не перегруженный оператор, а один из +, *, -, &, ^, |, && или ||.

binop
Не перегруженный оператор, но один из +, *, -, &^или |.

Ниже приведен пример reduction предложения:

#pragma omp parallel for reduction(+: a, y) reduction(||: am)
for (i=0; i<n; i++) {
   a += b[i];
   y = sum(y, c[i]);
   am = am || b[i] == c[i];
}

Как показано в примере, оператор может быть скрыт внутри вызова функции. Пользователь должен следить за тем, чтобы оператор, обозначенный в reduction условии, соответствовал операции сокращения.

Хотя правый операнд || оператора не имеет побочных эффектов в этом примере, его использование разрешается, но должно быть осторожным. В этом контексте побочный эффект, который гарантированно не возникает во время последовательного выполнения цикла, может возникать во время параллельного выполнения. Это различие может произойти из-за того, что порядок выполнения итерации не определен.

Оператор используется для определения начального значения любых частных переменных, используемых компилятором для уменьшения, и для определения оператора завершения. Указание оператора явным образом позволяет оператору сокращения находиться вне лексической степени конструкции. Любое количество reduction критериев может быть указано в директиве, но переменная может появляться только в одном reduction критерии для этой директивы.

Создается частная копия каждой переменной в списке переменных, по одной для каждого потока, как если бы использовалось предложение private. Частная копия инициализируется в соответствии с оператором (см. следующую таблицу).

В конце региона, для которого был указан оператор reduction, исходный объект обновляется таким образом, чтобы отразить результат объединения его исходного значения с окончательным значением каждой из частных копий с использованием указанного оператора. Операторы сокращения являются ассоциативными (за исключением вычитания), а компилятор может свободно пересоединять вычисления окончательного значения. (Частичные результаты сокращения вычитания добавляются для формирования окончательного значения.)

Значение исходного объекта становится неопределенным, когда первый поток достигает содержащего предложения и остается таким образом, пока не завершится вычисление сокращения. Как правило, вычисление будет завершено в конце конструкции; Однако если reduction предложение используется в конструкции, к которой nowait также применяется, значение исходного объекта остается неопределенным до тех пор, пока не будет выполнена синхронизация барьеров, чтобы убедиться, что все потоки завершили reduction предложение.

В следующей таблице перечислены операторы, допустимые и их канонические значения инициализации. Фактическое значение инициализации будет соответствовать типу данных переменной сокращения.

Оператор Инициализация
+ 0
* 1
- 0
& ~0
| 0
^ 0
&& 1
|| 0

Ограничения предложения reduction приведены следующим образом:

  • Тип переменных в reduction предложении должен быть допустимым для оператора сокращения, за исключением того, что типы указателей и ссылочные типы никогда не допускаются.

  • Переменная, указанная в клаузуле reduction, не должна быть const квалифицирована.

  • Переменные, которые являются частными в параллельном регионе или находятся в reduction предложении директивы parallel, не могут быть указаны в reduction предложении директивы разделения работы, которая привязывается к параллельной конструкции.

    #pragma omp parallel private(y)
    { /* ERROR - private variable y cannot be specified
                  in a reduction clause */
        #pragma omp for reduction(+: y)
        for (i=0; i<n; i++)
           y += b[i];
    }
    
    /* ERROR - variable x cannot be specified in both
                a shared and a reduction clause */
    #pragma omp parallel for shared(x) reduction(+: x)
    

2.7.2.7 копин

Предложение copyin предоставляет механизм назначения одного и того же значения threadprivate переменным для каждого потока в команде, выполняющей параллельный регион. Для каждой переменной, указанной в copyin предложении, значение переменной в главном потоке команды копируется в копии, специфичные для потока, в начале параллельного региона, как если бы это происходило через присваивание. copyin Синтаксис предложения выглядит следующим образом:

copyin(
variable-list
)

Ограничения предложения copyin приведены следующим образом:

  • Переменная, указанная в copyin предложении, должна иметь доступный, неоднозначный оператор назначения копирования.

  • Переменная, указанная в предложении copyin , должна быть переменной threadprivate .

2.7.2.8 copyprivate

Предложение copyprivate предоставляет механизм использования частной переменной для трансляции значения от одного члена команды другим участникам. Это альтернатива использованию общей переменной для передачи значения, когда передача такой общей переменной может представлять сложность (например, в рекурсии, требующей другой переменной на каждом уровне). Пункт copyprivate может появляться только в директиве single.

copyprivate Синтаксис предложения выглядит следующим образом:

copyprivate(
variable-list
)

Влияние copyprivate предложения на переменные в списке переменных происходит после выполнения структурированного блока, связанного с single конструкцией, и до того, как любой из потоков в команде оставил барьер в конце конструкции. Затем во всех остальных потоках команды каждая переменная из списка переменных определяется, как если бы она была присвоена, со значением соответствующей переменной из потока, который выполнял структурированный блок конструкции.

Ограничения для copyprivate предложения приведены следующим образом:

  • Переменная, указанная в блоке copyprivate, не должна появляться в блоке private или firstprivate для одной и той же директивы single.

  • single Если директива с copyprivate условием обнаружена в динамическом контексте параллельного региона, все переменные, указанные в copyprivate условии, должны быть приватными в заключающем контексте.

  • Переменная, указанная в copyprivate предложении, должна иметь понятный оператор назначения копирования.

Директивная привязка 2.8

Динамическая привязка директив должна соответствовать следующим правилам:

  • Директивы for, sections, single, master и barrier привязываются к динамическому окружению parallel, если оно существует, независимо от значения любого условия if, которое может присутствовать в этой директиве. Если параллельный регион в настоящее время не выполняется, директивы выполняются командой, состоящей только из управляющего потока.

  • Директива ordered привязывается к динамически охватывающему for.

  • Директива atomic обеспечивает монопольный доступ к atomic директивам во всех потоках, а не только текущей команде.

  • Директива critical обеспечивает монопольный доступ к critical директивам во всех потоках, а не только текущей команде.

  • Директива никогда не может привязаться к директиве вне ближайшей динамически заключающей оболочки parallel.

Вложенность директив 2.9

Динамическое вложение инструкций должно соответствовать следующим правилам:

  • parallel Директива динамически внутри другой parallel логически устанавливает новую команду, которая состоит только из текущего потока, если не включен вложенный параллелизм.

  • for, sections, и single директивы, которые привязываются к тому же parallel, не допускается их взаимное вложение.

  • critical Директивы с одинаковым именем не могут быть вложены друг в друга. Обратите внимание, что это ограничение недостаточно для предотвращения взаимоблокировки.

  • for, sections, и single директивы не допускаются в динамическом охвате critical, ordered и master регионов, если директивы привязываются к тем же parallel регионам, что и эти регионы.

  • barrier Директивы не допускаются в динамическом контексте for, ordered, sections, single, master и critical регионов, если эти директивы привязываются к тем же parallel регионам.

  • masterДирективы не допускаются в динамической степени forи sectionsдирективы, если single директивы привязываются к таким же master директивам, что и parallel директивы общего доступа к работе.

  • ordered Директивы не допускаются в динамической critical степени регионов, если директивы привязываются к тем же parallel регионам, что и регионы.

  • Любая директива, которая разрешена при динамическом выполнении в параллельном регионе, также допускается при выполнении вне параллельного региона. При динамическом выполнении за пределами заданного пользователем параллельного региона директива выполняется командой, состоящей только из главного потока.