if(
scalar-expression)
private(
variable-list)
firstprivate(
variable-list)
default(shared | none)
shared(
variable-list)
copyin(
variable-list)
reduction(
operator:
variable-list)
num_threads(
integer-expression)
当线程进入并行构造时,如果下列情况之一为 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
子句。
未指定 if 表达式或 num_threads
表达式中是否会出现任何副作用。
在并行区域中执行的 throw
必须使执行在同一结构化块的动态范围内恢复,并且必须由引发异常的同一线程捕获。
指令上只能显示单个 num_threads
子句。 num_threads
表达式是在并行区域的上下文之外计算的,并且必须计算为正整数值。
未指定 if
和 num_threads
子句的计算顺序。
private
、firstprivate
、default
、shared
、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 指令
single 指令
2.4.1 for 构造
for
指令标识迭代工作共享构造,该构造指定将并行执行的关联循环的迭代。 for
循环的迭代在团队中已经存在的线程之间分布,该团队执行其绑定到的并行构造。 for
构造的语法如下所示:
#pragma omp for [clause[[,] clause] ... ] new-line for-loop
子句为以下其中一项:
private(
variable-list)
firstprivate(
variable-list)
lastprivate(
variable-list)
reduction(
operator:
variable-list)
ordered
schedule(
kind [,
chunk_size] )
nowait
for
指令对相应 for
循环的结构进行了限制。 具体而言,相应的 for
循环必须具有规范形状:
for (
init-expr;
var logical-op b;
incr-expr)
init-expr
下列类型作之一:
var = lb
integer-type var = lb
incr-expr
下列类型作之一:
++
var
var++
--
var
var--
var+=
incr
var-=
incr
var=
var+
incr
var=
incr+
var
var=
var-
incr
一个带符号整数变量。 否则,如果共享此变量,那么在 for
期间它将隐式地设置为私有。 请勿在 for
语句正文中修改此变量。 除非指定 lastprivate
变量,否则循环后的值不确定。
logical-op
下列类型作之一:
lb、b 和 incr
循环固定整数表达式。 这些表达式的计算过程中没有同步,因此任何计算的副作用都会产生不确定的结果。
规范形式允许在循环的条目上计算循环迭代数。 此计算是在整型提升后使用 var 类型的值进行的。 具体而言,如果 b-
lb+
incr 的值不能在该类型中表示,则结果不确定。 此外,如果 logical-op 为 <
或 <=
,则 incr-expr 必须在循环的每个迭代上增加 var。 如果 logical-op 为 >
或 >=
,则 incr-expr 必须在循环的每个迭代上减小 var。
schedule
子句指定在团队线程之间如何划分 for
循环迭代。 程序的正确性不得取决于哪个线程执行特定迭代。 如果指定,则 chunk_size 的值必须是具有正值的循环固定整数表达式。 此表达式的计算过程中没有同步,因此任何计算的副作用都会产生不确定的结果。 schedule kind 可以是下列值之一:
表 2-1:schedule
子句 kind 值
static
指定 schedule(static,
chunk_size)
时,迭代被划分为由 chunk_size 指定的大小的区块。 区块按照线程编号的顺序以轮循机制方式静态分配给团队中的线程。 如果未指定 chunk_size,则迭代空间将划分为大小大致相等的区块,每个线程分配一个区块。
dynamic
指定 schedule(dynamic,
chunk_size)
时,迭代被划分为一系列区块,每个区块包含 chunk_size 迭代。 每个区块都分配给正在等待分配的线程。 该线程执行迭代区块,然后等待下一个分配,直到没有要分配的区块为止。 要分配的最后一个区块可能具有较少的迭代数。 如果未指定 chunk_size,则默认为 1。
指定 schedule(guided,
chunk_size)
后,迭代将分配给区块中的线程(大小逐渐减小)。 当线程完成其分配的迭代区块时,它会动态分配另一个区块,直到没有可分配的区块。 对于 chunk_size 1,每个区块的大小大约是未分配迭代数除以线程数。 这些大小几乎呈指数级递减到 1。 对于值 k 大于 1 的 chunk_size,大小几乎呈指数递减到 k,但最后一个区块的迭代数可能小于 k。 如果未指定 chunk_size,则默认为 1。
指定 schedule(runtime)
后,有关计划的决定将推迟到运行时。 可以通过设置环境变量 OMP_SCHEDULE
在运行时选择 schedule kind 和区块大小。 如果未设置此环境变量,则生成的计划是实现定义的。 指定 schedule(runtime)
时,不得指定 chunk_size。
如果没有显式定义的 schedule
子句,则默认 schedule
为实现定义。
符合 OpenMP 的程序不应依赖于特定计划来正确执行。 程序不应依赖于完全符合上述说明的 schedule kind,因为同样的 schedule kind 在不同编译器中可能有不同的实现。 这些说明可用于选择适合特定情况的计划。
当 ordered
指令绑定到 for
构造时,必须存在 ordered
子句。
for
构造末尾存在隐式屏障,除非指定 nowait
子句。
for
指令的限制如下:
for
循环必须是结构化块,此外,其执行不得由 break
语句终止。
与 for
指令关联的 for
循环的循环控件表达式的值对于团队中的所有线程必须相同。
for
循环迭代变量必须具有带符号整数类型。
for
指令上只能显示单个 schedule
子句。
for
指令上只能显示单个 ordered
子句。
for
指令上只能显示单个 nowait
子句。
对于 chunk_size、lb、b 或 incr 表达式中是否发生任何副作用或其发生频率未明确说明。
对于团队中的所有线程,chunk_size 表达式的值必须相同。
private
、firstprivate
、lastprivate
和 reduction
子句(第 2.7.2 节)
OMP_SCHEDULE 环境变量
ordered 构造
schedule 子句
2.4.2 sections 构造
sections
指令标识非迭代工作共享构造,该构造指定要在团队线程中划分的一组构造。 每个 section 由团队线程执行一次。 sections
指令的语法如下所示:
#pragma omp sections [clause[[,] clause] ...] new-line
[#pragma omp section new-line]
structured-block
[#pragma omp section new-linestructured-block ]
子句为以下其中一项:
private(
variable-list)
firstprivate(
variable-list)
lastprivate(
variable-list)
reduction(
operator:
variable-list)
nowait
每个 section 前面都有一个 section
指令,尽管 section
指令对于第一个 section 是可选的。 section
指令必须出现在 sections
指令的词法范围内。 sections
构造末尾存在隐式屏障,除非指定 nowait
。
sections
指令的限制如下:
section
指令不得出现在 sections
指令的词法范围之外。
sections
指令上只能显示单个 nowait
子句。
private
、firstprivate
、lastprivate
和 reduction
子句(第 2.7.2 节)
2.4.3 single 构造
single
指令标识一个构造,该构造指定团队中只有一个线程执行关联的结构化块(不一定是主线程)。 single
指令的语法如下所示:
#pragma omp single [clause[[,] clause] ...] new-linestructured-block
子句为以下其中一项:
private(
variable-list)
firstprivate(
variable-list)
copyprivate(
variable-list)
nowait
single
构造后面存在隐式屏障,除非指定 nowait
子句。
single
指令的限制如下:
single
指令上只能显示单个 nowait
子句。
copyprivate
子句不得与 nowait
子句一起使用。
private
、firstprivate
和 copyprivate
子句(第 2.7.2 节)
2.5 组合的并行工作共享构造
组合的并行工作共享构造是指定只有一个工作共享构造的并行区域的快捷方式。 这些指令的语义与显式指定 parallel
指令后跟单个工作共享构造相同。
以下部分介绍了组合的并行工作共享构造:
parallel for 指令
parallel sections 指令
2.5.1 parallel for 构造
parallel for
指令是仅包含单个 for
指令的 parallel
区域的快捷方式。 parallel for
指令的语法如下所示:
#pragma omp parallel for [clause[[,] clause] ...] new-linefor-loop
此指令允许 parallel
指令和 for
指令的所有子句(但 nowait
子句除外),它们具有相同的含义和限制。 语义与显式指定 parallel
指令后跟指令 for
相同。
parallel 指令
for 指令
数据属性子句
2.5.2 parallel sections 构造
parallel sections
指令提供一个快捷方式窗体,用于指定只有单个 sections
指令的 parallel
区域。 语义与显式指定 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
子句除外。
parallel 指令
sections 指令
2.6 Master 和同步指令
以下几节将介绍:
master 构造
critical 构造
barrier 指令
atomic 构造
flush 指令
ordered 构造
2.6.1 master 构造
master
指令标识一个构造,该构造指定由团队的主线程执行的结构化块。 master
指令的语法如下所示:
#pragma omp master new-linestructured-block
团队中的其他线程不会执行关联的结构化块。 进入或退出主构造时没有隐式屏障。
2.6.2 critical 构造
critical
指令标识一个构造,该构造一次将关联的结构化块的执行限制为单个线程。 critical
指令的语法如下所示:
#pragma omp critical [(name)] new-linestructured-block
可选名称可用于标识关键区域。 用于标识关键区域的标识符具有外部链接,并且位于与标签、标记、成员和普通标识符所使用的命名空间分开的命名空间中。
线程在关键区域的开头等待,直到没有其他线程在执行同名关键区域(程序中的任何位置)。 所有未命名 critical
指令都映射到相同的未指定名称。
2.6.3 barrier 指令
barrier
指令同步团队中的所有线程。 遇到该指令时,团队中的每个线程都将等待,直到其他所有线程都达到此点。 barrier
指令的语法如下所示:
#pragma omp barrier new-line
在团队中的所有线程都遇到 barrier 后,团队中的每个线程将开始行执行 barrier 指令之后的语句。 由于 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
指令可确保特定内存位置以原子方式更新,而不是将其公开给可能多个同时写入的线程。 atomic
指令的语法如下所示:
#pragma omp atomic new-lineexpression-stmt
表达式语句必须具有以下形式之一:
x binop=
expr
在前面的表达式中:
x 是标量类型的 lvalue 表达式。
expr 是标量类型的表达式,它不引用 x 指定的对象。
binop 不是重载运算符,并且是 +
、*
、-
、/
、&
、^
、|
、<<
或 >>
中的一个。
虽然实现是否将所有 atomic
指令替换为具有相同唯一名称的 critical
指令是实现定义的,但 atomic
指令允许更好的优化。 通常可以使用硬件指令,以最少的开销执行原子更新。
只有 x 指定的对象的加载和存储是原子式;expr 的计算不是原子式。 为了避免争用条件,应使用 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
指令(无论是显式还是隐式)都指定了一个“跨线程”序列点,该序列点需要此实现,以确保团队中的所有线程对于内存中的某些对象(下面指定)具有一致的视图。 这意味着引用这些对象的表达式的先前计算已完成,而后续计算尚未开始。 例如,编译器必须将对象的值从寄存器还原到内存,硬件可能需要将写入缓冲区刷新到内存,并从内存中重新加载对象的值。
flush
指令的语法如下所示:
#pragma omp flush [(variable-list)] new-line
如果需要同步的对象都可以由变量指定,则可以在可选 variable-list 中指定这些变量。 如果 variable-list 存在指针,则刷新指针本身,而不是指针引用的对象。
没有 variable-list 的 flush
指令会将除不可访问的对象之外的所有共享对象与自动存储持续时间同步。 (这可能比带 variable-list 的 flush
开销更大。)下面的指令隐含了一个没有 variable-list 的 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
指令后的结构化块。 ordered
指令的语法如下所示:
#pragma omp ordered new-linestructured-block
指令 ordered
必须在 for
或 parallel for
构造的动态范围内。 ordered
构造绑定的 for
或 parallel for
指令必须指定 ordered
子句,如第 2.4.1 节中所述。 在使用 for
子句执行 parallel for
或 ordered
构造时,ordered
构造将严格按照循环顺序执行的顺序执行。
ordered
指令的限制如下:
具有 for
构造的循环迭代不能多次执行相同的有序指令,并且不能执行多个 ordered
指令。
2.7 数据环境
本部分提供一个指令和多个子句,用于在并行区域执行期间控制数据环境,如下所示:
提供了 threadprivate 指令,使文件范围、命名空间范围或静态块范围变量成为线程的局部变量。
第 2.7.2 节介绍了在并行构造或工作共享构造期间,可以在指令上指定用于控制变量的共享属性的子句。
2.7.1 threadprivate 指令
threadprivate
指令将 variable-list 中指定的命名文件范围、命名空间范围或静态块范围变量设为线程的私有变量。 variable-list 是没有不完整类型的变量的逗号分隔列表。 threadprivate
指令的语法如下所示:
#pragma omp threadprivate(variable-list) new-line
threadprivate
变量的每个副本在对副本的第一次引用之前在程序中的未指定点初始化一次,并按照通常的方式进行(即主控副本将在程序的串行执行中初始化)。 请注意,如果在 threadprivate
变量的显式初始化表达式中引用对象,并且在对变量副本的第一次引用之前修改变量值,则行为未指定。
与任何私有变量一样,线程不得引用另一个线程的 threadprivate
对象副本。 在程序的串行区域和主区域期间,引用将指向主线程的对象副本。
执行第一个并行区域后,仅当已禁用动态线程机制并且所有并行区域的线程数保持不变时,才能保证 threadprivate
对象中的数据能够持久保存。
threadprivate
指令的限制如下:
文件范围或命名空间范围变量的 threadprivate
指令必须出现在任何定义或声明之外,并且必须在对其列表中任何变量的所有引用之前按词法表示。
文件或命名空间范围内 threadprivate
指令 variable-list 中的每个变量都必须引用在词法上位于该指令前面的文件或命名空间范围内的变量声明。
静态块范围变量的 threadprivate
指令必须出现在变量范围内,而不是嵌套范围内。 指令必须按词法位于列表中对任何变量的所有引用之前。
块范围中 threadprivate
指令的 variable-list 中的每个变量都必须引用相同范围内在词法上位于该指令前的变量声明。 变量声明必须使用静态存储类说明符。
如果在一个转换单元的 threadprivate
指令中指定变量,则必须在声明该变量的每个转换单元的 threadprivate
指令中指定它。
threadprivate
变量不得出现在除 copyin
、copyprivate
、schedule
、num_threads
或 if
子句以外的任何子句中。
threadprivate
变量的地址不是地址常量。
threadprivate
变量不得有不完整的类型或引用类型。
如果具有非 POD 类类型的 threadprivate
变量使用显式初始化表达式声明,则该变量必须具有可访问且明确的复制构造函数。
以下示例演示修改初始化表达式中显示的变量会如何导致未指定的行为,以及如何使用辅助对象和复制构造函数避免此问题。
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) {
#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
指令中指定,则共享该变量。 共享在并行区域的动态范围内声明的静态变量。 共享堆分配的内存(例如,在 C 或 C++ 中使用 malloc()
或在 C++ 中使用 new
操作符)。 (但是,指向此内存的指针可以是私有,也可以共享。)在并行区域的动态范围内声明的具有自动存储持续时间的变量是私有的。
大多数子句都接受 variable-list 参数,该参数是逗号分隔的可见变量列表。 如果在数据共享属性子句中引用的变量具有派生自模板的类型,并且程序中没有对该变量的其他引用,则行为是未定义的。
指令子句中显示的所有变量都必须可见。 子句可以根据需要重复,但不能在多个子句中指定变量,除非变量可以同时在 firstprivate
和 lastprivate
子句中指定。
以下部分介绍数据共享属性子句:
private
firstprivate
lastprivate
shared
default
reduction
copyin
copyprivate
2.7.2.1 private
private
子句将 variable-list 中的变量声明为团队中每个线程的私有变量。 private
子句的语法如下:
private(variable-list)
private
子句中指定的变量的行为如下所示。 为构造分配具有自动存储持续时间的新对象。 新对象的大小和对齐方式由变量类型决定。 此分配为团队中的每个线程进行一次,必要时为类对象调用默认构造函数;否则初始值不确定。 变量引用的原始对象在进入构造时具有不确定值,不能在构造的动态范围内修改,并且在退出构造时具有不确定值。
在指令构造的词法范围内,变量引用线程分配的新私有对象。
private
子句的限制如下:
具有 private
子句中指定的类类型的变量必须具有可访问且明确的默认构造函数。
private
子句中指定的变量不得有 const
限定类型,除非它具有包含 mutable
成员的类类型。
private
子句中指定的变量不得有不完整的类型或引用类型。
parallel
指令的 reduction
子句中出现的变量不能在 private
子句中指定,后者位于绑定到并行构造的工作共享指令上。
2.7.2.2 firstprivate
firstprivate
子句提供 private
子句提供的功能超集。 firstprivate
子句的语法如下:
firstprivate(variable-list)
variable-list 中指定的变量具有 private
子句语义,如第 2.7.2.1 节中所述。 初始化或构造就像在线程执行构造之前每个线程完成一次一样。 对于并行构造上的 firstprivate
子句,新私有对象的初始值是紧邻遇到它的线程并行构造之前存在的原始对象的值。 对于工作共享构造上的 firstprivate
子句,执行工作共享构造的每个线程的新私有对象初始值是同一线程遇到工作共享构造之前存在的原始对象的值。 此外,对于 C++ 对象,每个线程的新私有对象都是从原始对象构造的。
firstprivate
子句的限制如下:
firstprivate
子句中指定的变量不得有不完整的类型或引用类型。
具有指定为 firstprivate
的类类型的变量必须具有可访问且明确的复制构造函数。
在并行区域内为私有的变量或 parallel
指令的 reduction
子句中出现的变量不能在 firstprivate
子句中指定,后者位于绑定到并行构造的工作共享指令上。
2.7.2.3 lastprivate
lastprivate
子句提供 private
子句提供的功能超集。 lastprivate
子句的语法如下:
lastprivate(variable-list)
variable-list 中指定的变量具有 private
子句语义。 当 lastprivate
子句出现在标识工作共享构造的指令上时,将关联循环的按顺序排列的上一次迭代或词法上最后一节指令中每个 lastprivate
变量的值分配给变量的原始对象。 未按 for
或 parallel for
的上一次迭代或词法上 sections
或 parallel sections
的最后一节分配值的变量在构造后具有不确定的值。 未分配的子对象在构造后也会得到不确定的值。
lastprivate
子句的限制如下:
private
的所有限制适用。
具有指定为 lastprivate
的类类型的变量必须具有可访问且明确的复制赋值运算符。
在并行区域内为私有的变量或 parallel
指令的 reduction
子句中出现的变量不能在 lastprivate
子句中指定,后者位于绑定到并行构造的工作共享指令上。
2.7.2.4 shared
此子句在团队的所有线程中共享出现在 variable-list 中的变量。 团队中的所有线程都访问 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
、firstprivate
、lastprivate
、reduction
和 shared
子句来重写,如下所示:
#pragma omp parallel for default(shared) firstprivate(i)\
private(x) private(r) lastprivate(i)
2.7.2.6 reduction
此子句使用运算符 op 对 variable-list 中显示的标量变量执行缩减。 reduction
子句的语法如下:
reduction(
op:
variable-list)
通常为具有以下形式之一的语句指定缩减:
x=
xopexpr
xbinop=
expr
x=
expropx(减法除外)
列表中指定的缩减变量之一。
variable-list
逗号分隔的标量缩减变量列表。
具有不引用 x 的标量类型的表达式。
不是重载运算符,而是 +
、*
、-
、&
、^
、|
、&&
或 ||
中的一个。
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
子句中。
创建 variable-list 中每个变量的私有副本,每个线程一个,就像使用了 private
子句一样。 根据运算符初始化私有副本(请参阅下表)。
在指定 reduction
子句的区域末尾,将更新原始对象,以反映使用指定运算符将原始值与每个私有副本的最终值相结合的结果。 缩减运算符都是关联的(减法除外),编译器可以自由重新关联最终值的计算。 (将添加减法的部分结果以构成最终值。)
当第一个线程到达包含子句时,原始对象的值将变得不确定,并且一直保持到缩减计算完成。 通常,计算将在构造结束时完成;但是,如果 reduction
子句用于应用 nowait
的构造,则原始对象的值将保持不确定状态,直到执行屏障同步以确保所有线程均已完成 reduction
子句。
下表列出了有效运算符及其规范初始化值。 实际初始化值将与缩减变量的数据类型一致。
reduction
子句中指定的变量不得为 const
限定。
在并行区域内为私有的变量或 parallel
指令的 reduction
子句中出现的变量不能在 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
子句对其 variable-list 中变量的影响发生在与 single
构造关联的结构化块执行之后,以及在构造结束时团队中的任何线程离开屏障之前。 然后,在团队的所有其他线程中,对于 variable-list 中的每个变量,该变量将使用执行该构造的结构化块的线程中相应变量的值来定义(就像通过赋值一样)。
copyprivate
子句的限制如下:
copyprivate
子句中指定的变量不得出现在同一 single
指令的 private
或 firstprivate
子句中。
如果在并行区域的动态范围内遇到具有 copyprivate
子句的 single
指令,则 copyprivate
子句中指定的所有变量都必须在封闭上下文中私有。
copyprivate
子句中指定的变量必须具有可访问且明确的复制赋值运算符。
2.8 指令绑定
指令的动态绑定必须遵循以下规则:
for
、sections
、single
、master
和 barrier
指令绑定到动态封闭的 parallel
(如果存在),而不考虑该指令中可能存在的任何 if
子句的值。 如果当前未执行并行区域,则指令由仅由主线程组成的团队执行。
ordered
指令绑定到动态封闭 for
。
atomic
指令对所有线程中的 atomic
指令强制实施独占访问,而不仅仅是当前团队。
critical
指令对所有线程中的 critical
指令强制实施独占访问,而不仅仅是当前团队。
指令永远不能绑定到最近的动态封闭的 parallel
之外的任何指令。
2.9 指令嵌套
指令的动态嵌套必须遵循以下规则:
在另一个 parallel
指令中的 parallel
指令会在逻辑上建立一个新团队,该团队只由当前线程组成,除非启用嵌套并行。
绑定到同一个 parallel
的 for
、sections
和 single
不允许相互嵌套。
具有相同名称的 critical
指令不允许相互嵌套。 请注意,此限制不足以阻止死锁。
如果 for
、sections
和 single
指令绑定到与 critical
、ordered
和 master
区域相同的 parallel
,那么这些指令不允许在这些区域的动态范围内。
如果 barrier
指令绑定到与 for
、ordered
、sections
、single
、master
和 critical
区域相同的 parallel
,那么这些指令不允许在这些区域的动态范围内。
如果 master
指令绑定到与工作共享指令相同的 parallel
,那么 master
指令不允许在 for
、sections
和 single
区域的动态范围内。
如果 ordered
指令绑定到与 critical
区域相同的 parallel
,那么这些指令不允许在这些区域的动态范围内。
在并行区域中动态执行时允许的任何指令也允许在并行区域外执行。 在用户指定的并行区域外动态执行时,指令由仅由主线程组成的团队执行。