Поделиться через


2. Директивы

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

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

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

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

Каждая директива начинается с #pragma omp, чтобы уменьшить вероятность конфликта с другими (не 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(целочисленное выражение )

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

  • Предложение отсутствует 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 предложение может отображаться в директиве.

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

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

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

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

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

  • private, firstprivate, , shareddefaultcopyinи reduction предложения (раздел 2.7.2)
  • переменная среды OMP_NUM_THREADS
  • функция библиотеки omp_set_dynamic
  • переменная среды OMP_DYNAMIC
  • функция omp_set_nested
  • переменная среды OMP_NESTED
  • функция библиотеки omp_set_num_threads

2.4 Конструкции общего доступа к работе

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

Последовательность создаваемых конструкций и 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 = lb
  • целочисленный тип var = lb

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

  • ++var
  • var ++
  • --var
  • var --
  • var += incr
  • var -= incr
  • var incr = +
  • var incr 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 значения типа предложения

значение Описание
static При 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 или добавочного выражения.

  • Значение выражения 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 .

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

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

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

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

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

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

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

  • private(переменная списка )
  • firstprivate(переменная списка )
  • copyprivate(переменная списка )
  • nowait

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

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

  • В директиве single может отображаться только одно nowait предложение.
  • Предложение 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 Master и синхронизации

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

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

Несмотря на то, что реализация заменяет все директивы critical директивами с одинаковым atomic уникальным именем, 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

Ссылка, которая обращается к значению объекта с переменным типом, ведет себя так, как если бы существовала 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 конструкции. Директива for или директива, к которой ordered привязывается конструкция, должна иметь ordered предложение, указанное в разделе 2.4.1parallel for. В выполнении 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 Директива для переменных статического блока должна отображаться в области переменной, а не в вложенной области. Директива должна лексически предшествовать всем ссылкам на любые переменные в списке.

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

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

  • Переменная threadprivate не должна отображаться в любом предложенииcopyin, кроме предложения , copyprivateили schedulenum_threadsif предложения.

  • Адрес переменной 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 Синтаксис предложения выглядит следующим образом:

private(variable-list)

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

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

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

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

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

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

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

2.7.2.2 firstprivate

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

firstprivate(variable-list)

Переменные, указанные в списке переменных, имеют private семантику предложения, как описано в разделе 2.7.2.1. Инициализация или построение происходит так, как если бы оно было сделано один раз на поток, до выполнения потока конструкции. 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 Синтаксис предложения выглядит следующим образом:

shared(variable-list)

2.7.2.5 default

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

default(shared | none)

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

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

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

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

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

  • Переменная имеет полный constтип.

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

Указание переменной в firstprivatelastprivateпредложении или reduction предложении заключенной директивы приводит к неявной ссылке на переменную в заключенном контексте. Такие неявные ссылки также относятся к указанным выше требованиям.

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

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

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

2.7.2.6 reduction

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

reduction(op : переменная списка )

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

  • x = x op expr
  • x binop = expr
  • x expr op x = (за исключением вычитания)
  • 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

Предложение 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и singlebarrier masterдирективы привязываются к динамическому заключению parallel, если он существует, независимо от значения любого if предложения, которое может присутствовать в этой директиве. Если параллельный регион в настоящее время не выполняется, директивы выполняются командой, состоящей только из главного потока.

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

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

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

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

Вложенный директив 2.9

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

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

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

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

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

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

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

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

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