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
выражения равно нулю, регион сериализуется.
Чтобы определить количество запрошенных потоков, будут рассматриваться следующие правила в порядке. Первое правило, условие которого выполняется, будет применено:
num_threads
Если предложение присутствует, то значение целочисленного выражения — это число запрошенных потоков.omp_set_num_threads
Если функция библиотеки была вызвана, то значение аргумента в последнем выполненном вызове — это количество запрошенных потоков.Если определена переменная среды, то значение этой переменной
OMP_NUM_THREADS
среды — это количество запрошенных потоков.Если ни один из приведенных выше методов не используется, то количество запрошенных потоков определяется реализацией.
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
, ,shared
default
copyin
и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 определяет следующие конструкции общего доступа к работе, и эти конструкции описаны в следующих разделах:
- директива for
- Директива sections
- одна директива
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 должно быть одинаковым для всех потоков в команде.
Перекрестные ссылки
private
, ,lastprivate
firstprivate
иreduction
предложения (раздел 2.7.2)- переменная среды OMP_SCHEDULE
- упорядоченная конструкция
- Предложение schedule
Конструкция разделов 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
, ,lastprivate
firstprivate
и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
.
Перекрестные ссылки
private
,firstprivate
и предложения (раздел 2.7.2copyprivate
)
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
предложения.
Перекрестные ссылки
- параллельная директива
- Директива sections
Директивы 2.6 Master и синхронизации
В следующих разделах описано:
- Главный конструктор
- критическая конструкция
- Директива барьера
- атомарная конструкция
- Директива flush
- упорядоченная конструкция
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
или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. */
}
}
Перекрестные ссылки
- динамические потоки
- переменная среды OMP_DYNAMIC
Предложения атрибутов общего доступа к данным 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
директивой, а ссылка на переменную отображается внутри цикла.
Указание переменной в firstprivate
lastprivate
предложении или reduction
предложении заключенной директивы приводит к неявной ссылке на переменную в заключенном контексте. Такие неявные ссылки также относятся к указанным выше требованиям.
В директиве parallel
может быть указано только одно default
предложение.
Атрибут общего доступа к данным по умолчанию переменной можно переопределить с помощью private
предложений , , lastprivate
firstprivate
reduction
и 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
иsingle
barrier
master
директивы привязываются к динамическому заключениюparallel
, если он существует, независимо от значения любогоif
предложения, которое может присутствовать в этой директиве. Если параллельный регион в настоящее время не выполняется, директивы выполняются командой, состоящей только из главного потока.Директива
ordered
привязывается к динамическому заключениюfor
.Директива
atomic
обеспечивает монопольный доступ кatomic
директивам во всех потоках, а не только текущей команде.Директива
critical
обеспечивает монопольный доступ кcritical
директивам во всех потоках, а не только текущей команде.Директива никогда не может привязаться к любой директиве за пределами ближайшего динамического заключения
parallel
.
Вложенный директив 2.9
Динамическое вложение директив должно соответствовать следующим правилам:
parallel
Директива динамически внутри другойparallel
логически устанавливает новую команду, которая состоит только из текущего потока, если не включен вложенный параллелизм.for
,sections
иsingle
директивы, которые привязываются к тому жеparallel
, не допускаются вложения друг в друга.critical
Директивы с одинаковым именем не могут быть вложены друг в друга. Обратите внимание, что это ограничение недостаточно для предотвращения взаимоблокировки.for
,sections
и директивы не допускаются в динамической степениcritical
ordered
, иsingle
master
регионы, если директивы привязываются к тем жеparallel
регионам, что и регионы.barrier
Директивы не допускаются в динамическойfor
степени ,ordered
,sections
master
single
и регионов, если директивы привязываются к тем жеparallel
регионам, что иcritical
регионы.master
Директивы не допускаются в динамической степениfor
иsections
директивы, еслиmaster
директивы привязываются к таким жеparallel
директивам, что иsingle
директивы общего доступа к работе.ordered
Директивы не допускаются в динамическойcritical
степени регионов, если директивы привязываются к тем жеparallel
регионам, что и регионы.Любая директива, которая разрешена при динамическом выполнении в параллельном регионе, также допускается при выполнении вне параллельного региона. При динамическом выполнении за пределами заданного пользователем параллельного региона директива выполняется командой, состоящей только из главного потока.