Data Scope Attribute Clauses
- 也叫Data-sharing属性子句
- 对数据作用域的理解与应用是OpenMP编程的重要因素
- 因为OpenMP是基于内存共享编程模型的,很多变量都默认共享了
- 全局变量包括:
- Fortran: COMMON块, SAVE变量, MODULE变量
- C:文件范围内的变量, static
- 私有变量包括:
- 循环索引变量
- 被并行区域调用的子程序的栈(stack)变量
- Fortran:语句块内的自动变量(automatic)
- OpenMP的数据作用域属性子句常用来显式定义变量的作用域,包括:
- PRIVATE
- FIRSTPRIVATE
- LASTPRIVATE
- SHARED
- DEFAULT
- REDUCTION
- COPYIN
- 数据作用域子句用来联合几个指令(PARALLEL,DO/for,SECTIONS)来控制被封装变量的作用域。
- 这些构造提供了在执行并行构造时控制数据环境的能力
- 定义了在程序的串行化区域的哪些数据变量以何种方式传递给程序的并行区域
- 定义了哪些变量应该对并行块的所有线程可见,哪些变量应该被所有线程私有分配
- 数据作用域属性子句只有在它的词法/静态范围内才有效
- 重要:请参考最新的OpenMP文献来获得更多的重要细节和关于此主题的详述
- 方便起见,提供了Clauses / Directives Summary Table
目的:
- PRIVATE子句声明在它列表中的变量对各个线程都是私有的
格式:
Fortran |
PRIVATE(list) |
C/C++ |
private(list) |
注意:
- PRIVATE变量的有如下行为:
- 对于组内的每个线程,相同类型的新对象都要被声明一次
- 对原始对象的所有引用都被指向新线程的引用替换
- 每个线程都假定被声明为PRIVATE的变量未初始化
- 对比PRIVATE和THREADPRIVATE:
| PRIVATE | THREADPRIVATE |
数据项 | C/C++:变量 | C/C++:变量 |
声明位置 | 在区域或者工作共享组的开头 | 在使用块或者全局文件作用域的程序声明里 |
持久化? | 否 | 是 |
范围 | 语法性的-除非当作参数传递给子程序 | 动态 |
初始化 | 使用 FIRSTPRIVATE | 使用 COPYIN |
问题:
| For the C/C++ and FortranTHREADPRIVATE example codes, what output would you expect for alpha[3] and beta[3]? Why? |
SHARED子句
目的:
- SHARED子句声明在其列表中的变量被线程组中的所有线程共有
格式:
Fortran |
SHARED(list) |
C/C++ |
shared(list) |
注意:
- 一个共享变量只存在一个内存位置,并且所有线程都可以对此地址读写
- 确保多线程正确访问SHARED变量是程序员的职责(比如CRITICAL段)
DEFAULT子句
目的:
- DEFAULT子句允许用户在任何并行区域为所有变量指定词法范围的默认作用域
格式:
Fortran |
DEFAULT (PRIVATE | FIRSTPRIVATE | SHARED | NONE) |
C/C++ |
default (shared | none) |
注意:
- 使用PRIVATE, SHARED, FIRSTPRIVATE, LASTPRIVATE,和 REDUCTION子句可以使指定的变量不受此子句的影响
- C/C++ OpenMP规范并不包含private或者firstprivate作为可能的DEFAULT。然而,实际实现时可能会提供此选项。
- 把NONE作为default需要程序员明确范围内的所有变量
限制:
- 一个PARALLEL指令只能被一个DEFAULT子句修饰
FIRSTPRIVATE子句
目的:
- FIRSTPRIVATE子句的行为相当于能自动初始化其列表中的变量的PRIVATE子句
格式:
Fortran |
FIRSTPRIVATE(list) |
C/C++ |
firstprivate(list) |
注意:
- 列表中的变量初始化的顺序与进入并行或工作共享构造的他们的原始对象顺序一致
LASTPRIVATE子句
目的:
- LASTPRIVATE子句的作用相当于在循环迭代或者区域结束时将变量的值赋予原始变量对象的PRIVATE子句
格式:
Fortran |
LASTPRIVATE(list) |
C/C++ |
lastprivate(list) |
注意:
- 拷贝回原始变量对象的值是从封装构造的最后一次迭代(顺序)或者区域取得的
例如,执行一个DO代码段最后一次迭代,或者一个SECTIONS上下文的最后一个SECTION的小组成员,用其值完成这次拷贝
COPYIN子句
目的:
- COPYIN子句将THREADPRIVATE中的变量为所有线程组中的线程赋予同样的值
格式:
Fortran |
COPYIN(list) |
C/C++ |
copyin (list) |
注意:
- (list)包含了要拷贝的变量。对于Fortran,(list)可以包含公共块的命名和已命名变量
- 主线程的变量作为拷贝源。进入并行构造的时候,线程组用它的值来初始化。
COPYPRIVATE子句
目的:
- COPYPRIVATE子句用来由单个线程将私有变量的值广播到其他线程中的实例
- 与SINGLE指令关联
- 查看最近的OpenMP规范文档获取更多信息与示例
格式:
Fortran |
COPYPRIVATE(list) |
C/C++ |
copyprivate (list) |
REDUCTION子句
目的:
- REDUCTION子句对在其列表中的变量执行一次约简(reduction)
- 对列表中的每个变量创建各个线程的私有拷贝。约简结束时,约简变量被应用到相关共享变量的所有私有拷贝,并且最终的结果被写到其全局共享变量。
格式:
Fortran |
REDUCTION(operator|intrinsic: list) |
C/C++ |
reduction(operator: list) |
示例: REDUCTION - Vector Dot Product:
- 并行循环迭代为线程组中的线程均分为同等大小的块(SCHEDULE STATIC)
- 在并行循环构造结束时,所有的线程将把他们得到的值加到主线程中的全局拷贝上
Fortran - REDUCTION Clause Example
PROGRAM DOT_PRODUCT
INTEGER N, CHUNKSIZE, CHUNK, I PARAMETER (N=100) PARAMETER (CHUNKSIZE=10) REAL A(N), B(N), RESULT
! Some initializations DO I = 1, N A(I) = I * 1.0 B(I) = I * 2.0 ENDDO RESULT= 0.0 CHUNK = CHUNKSIZE
!$OMP PARALLEL DO !$OMP& DEFAULT(SHARED) PRIVATE(I) !$OMP& SCHEDULE(STATIC,CHUNK) !$OMP& REDUCTION(+:RESULT)
DO I = 1, N RESULT = RESULT + (A(I) * B(I)) ENDDO
!$OMP END PARALLEL DO NOWAIT
PRINT *, 'Final Result= ', RESULT END |
·
C / C++ - reduction Clause Example
#include <omp.h>
main () {
int i, n, chunk; float a[100], b[100], result;
/* Some initializations */ n = 100; chunk = 10; result = 0.0; for (i=0; i < n; i++) { a[i] = i * 1.0; b[i] = i * 2.0; }
#pragma omp parallel for / default(shared) private(i) / schedule(static,chunk) / reduction(+:result)
for (i=0; i < n; i++) result = result + (a[i] * b[i]);
printf("Final result= %f/n",result);
} |
限制:
- 列表中的变量必须是数值变量,不能是数据或者结构类型变量。而且,他们必须在封装的上下文中被声明为SHARED
- 约简操作可能也无法与实数相关
- Reduction operations may not be associative for real numbers.
- 规定REDUCTION子句只能用于区域(region)或者工作共享构造,而且语句中的约简变量要遵守以下形式:
Fortran | C / C++ |
x = x operator expr | x = x op expr |
x为list中的数值变量 intrinsic: MAX, MIN, IAND, IOR, IEOR | x为list中的数值变量 op不是重载的(overloaded),为下面之一: +, *, -, /, &, ^, |, &&, || |
子句 /指令总结
- 下面的表格总结了哪些子句可被哪些OpenMP指令接受(黑色表示否)
子句 | 指令 | |||||
PARALLEL | DO/for | SECTIONS | SINGLE | PARALLEL | PARALLEL | |
IF |
|
|
|
|
|
|
PRIVATE |
|
|
|
|
|
|
SHARED |
|
|
|
|
|
|
DEFAULT |
|
|
|
|
|
|
FIRSTPRIVATE |
|
|
|
|
|
|
LASTPRIVATE |
|
|
|
|
|
|
REDUCTION |
|
|
|
|
|
|
COPYIN |
|
|
|
|
|
|
COPYPRIVATE |
|
|
|
|
|
|
SCHEDULE |
|
|
|
|
|
|
ORDERED |
|
|
|
|
|
|
NOWAIT |
|
|
|
|
|
|
- 下面的OpenMP指令不接受子句:
- MASTER
- CRITICAL
- BARRIER
- ATOMIC
- FLUSH
- ORDERED
- THREADPRIVATE
- 实现时,关于哪些子句被哪些指令支持可能(肯定)会有差异
指令绑定和嵌套规则
| 该章节主要作为管理OpenMP指令和绑定规则的快速参考。用户应该查阅它们的实现文档和OpenMP标准获得更多的条款说明和限制。 |
- 如果没有特别指出,规则同时适用于Fortran和C/C++的OpenMP实现
- 注意:Fortran的API也定义了一些数据环境规则,这里就不重复提及了
指令绑定:
- The DO/for, SECTIONS, SINGLE, MASTER和 BARRIER指令绑定到动态封装的PARALLEL,如果存在.如果当前没有要被执行的PARALLEL区域,这些指令就没有效果
- ORDERED指令绑定到动态封装的DO/for
- ATOMIC指令在所有线程中执行独占访问,而不仅仅是当前线程组
- CRITICAL指令在所有线程中执行独占访问,而不仅仅是当前线程组
- 指令不会绑定到超出最近那个PARALLEL封装的任何其他指令
指令嵌套:
- 除非启用嵌套并行处理,一个动态进入另一个PARALLEL指令的PARALLEL指令逻辑上建立的新线程组只有一个当前线程组成。
- 绑定到同一个PARALLEL的DO/for, SECTIONS, 和 SINGLE指令,不允许相互嵌套
- DO/for, SECTIONS,和 SINGLE指令不允许在CRITICAL, ORDERED and MASTER区域的动态范围内.
- 相同命名的CRITICAL指令不允许相互嵌套
- BARRIER指令不允许在DO/for, ORDERED, SECTIONS, SINGLE, MASTER和 CRITICAL区域的动态范围内.
- MASTER指令不允许在DO/for, SECTIONS和 SINGLE指令的动态范围内.
- ORDERED指令不允许在CRITICAL区域的动态范围内.
- 任何允许在一个PARALLEL区域动态执行的指令也被允许在一个PARALLEL区域外执行。当指令在一个用户指定的并行区域外动态执行时,只受由主线程组成的线程组的影响。