OpenMP 指令格式 |
C / C++ 指令格式
格式:
#pragma omp
directive-name
[clause, ...]
newline
OpenMP C/C++ 指令都需要.
有效OpenMP 指令. 必须在pragma指令后,在 clauses指令前。
可选. 除非有另外的限制,子句可以任意顺序, 根据需要重复。
必需. 位于被这个指令包围的结构块之前。
示例:
#pragma omp parallel default(shared) private(beta,pi)
规则:
- 区分大小写
- 指令遵守C/C++编译器指令标准
- 每条指令只能有一个指令名称
- 每条指令适用于最少一条被结构块包围的语句
- 常指令可以在一条指令行结束后,用"/"连接
#pragma omp
directive-name
[clause, ...]
newline
OpenMP C/C++ 指令都需要.
有效OpenMP 指令. 必须在pragma指令后,在 clauses指令前。
可选. 除非有另外的限制,子句可以任意顺序, 根据需要重复。
必需. 位于被这个指令包围的结构块之前。
#pragma omp parallel default(shared) private(beta,pi)
Fortran 指令格式
格式: (不区分大小写)
sentinel |
directive-name |
[clause ...] |
所有的Fortan OpenMP指令必须以哨兵开始。 接受的哨兵依赖于Fortan源程序。可能的形式: !$OMP C$OMP *$OMP |
有效OpenMP指令,必须在哨兵和子句之间。 |
可选.除非有另外的限制,子句可以任意顺序, 根据需要重复。 |
示例:
!$OMP PARALLEL DEFAULT(SHARED) PRIVATE(BETA,PI) |
固定格式源:
- !$OMP C$OMP *$OMP 作为接受的哨兵,必须出现在第一列
- 所有Fortan的固定规则,比如行长度,空格,继续,注释等,对整条指令都适合
- 初始指令行必须在第六列包含一个空格/0
- 接下来的指令行在第六列必须包含一个非空格/0
自由格式源:
- !$OMP 是唯一的接受哨兵. 可以出现在任何列,但必须前面只能有空格
- 所有Fortan的自由规则,比如行长度,空格,继续,注释等,对整条指令都适合
- 初始指令行之后必须有个空格
- 后续行的每一行,最后都必须有个作为非空白字符的符号
规则:
- 注释不能作为指令出现在同一行
- 每条指令只能指定一个指令名称
- 启用了OpenMP的Fortran编译器通常包含一条命令行选项,用来指示编译器激活并解释所有的OpenMP指令
- 几个Fortran OpenMP指令成对出现,格式如下所示. "end"是可选的,但为了提高可读性,建议不要省略.
!$OMP directive [ structured block of code ] !$OMP end directive |
OpenMP 指令 |
指令作用域
不罗嗦了...
静态范围:
- 代码由一条OpenMP的指令开头,写在一个结构块的开始和结束之间
- 指令的静态范围不能跨越多个例程或代码文件
孤立指令:
- 看起来和另外一个包围指令没有关系的指令被称为孤立指令。 它存在于另外一个指令的静态范围之外。
- 跨越多个例程(也许是代码文件)
Dynamic Extent动态范围:
- 指令的动态范围包括它的静态范围以及孤立指令范围.
Example:
PROGRAM TEST ... !$OMP PARALLEL ... !$OMP DO DO I=... ... CALL SUB1 ... ENDDO ... CALL SUB2 ... !$OMP END PARALLEL |
SUBROUTINE SUB1 ... !$OMP CRITICAL ... !$OMP END CRITICAL END SUBROUTINE SUB2 ... !$OMP SECTIONS ... !$OMP END SECTIONS ... END |
静态范围 |
孤立指令 |
动态范围 |
为什么这一点很重要?
- OpenMP 制定了一系列关于指令关联(绑定)与嵌套的范围规则
- 如果OpenMP的绑定与嵌套规则被忽略,可能导致程序非法或不正确
- 有关细节请参考Directive Binding and Nesting Rules
并行区域结构
作用:
- 并行区域是一块能被多个线程执行的代码。下面是基本的OpenMP并行结构:
格式:
Fortran |
!$OMP PARALLEL [clause ...] IF (scalar_logical_expression) PRIVATE (list) SHARED (list) DEFAULT (PRIVATE | FIRSTPRIVATE | SHARED | NONE) FIRSTPRIVATE (list) REDUCTION (operator: list) COPYIN (list) NUM_THREADS (scalar-integer-expression) block !$OMP END PARALLEL |
C/C++ |
#pragma omp parallel [clause ...] newline if (scalar_expression) private (list) shared (list) default (shared | none) firstprivate (list) reduction (operator: list) copyin (list) num_threads (integer-expression) structured_block |
注意:
- 当线程遇到一个PARALLEL指令,它创建一组线程并成为主控者。主控者也属于这组线程,并在组内的线程号为0
- 从这个并行区域开始,代码被复制并被所有线程执行
- 并行区域结束时有个隐藏的关卡,只有主控线程能在此之后继续执行
- 并行区域内的任何线程终止都会终止所有的线程。工作不能继续,直到这点被消除定义。
有多少线程?
- 并行区域内的线程数量是由以下因素决定的,按优先级排序:
- IF 子句的值
- NUM_THREADS 子句的设置
- 使用 omp_set_num_threads() 库函数
- OMP_NUM_THREADS 环境变量
- 默认实现-一般是一个计算机的CPU数量,也可能是动态的(参考下节).
- 线程编号从0 (主线程) to N-1
动态线程:
- 用 omp_get_dynamic() 库函数检测动态线程是否被启用.
- 如果支持动态线程,下面两个方法可以启用动态线程:
- omp_set_dynamic() 库例程
- 设置 OMP_DYNAMIC 环境变量为TRUE
嵌套并行区域:
- 使用 omp_get_nested() 库函数检测嵌套并行区域是否被启用.
- 如果支持嵌套并行区域,可以用下面两个方法启用:
- omp_set_nested() 库例程
- 设置OMP_NESTED 环境变量为TRUE
- 如果不支持,嵌套并行区域默认将生成一个由单个线程组成的新组。
子句:
- IF 子句:如果存在,值必须为 .TRUE. (Fortran) 或者非零(C/C++) 以使能创建一组线程. 否则,此区域将被主线程串行执行.
- 在Data Scope Attribute Clauses 章节,将详细讨论剩下的子句.
限定:
- 一个并行区域必须是一个结构块,不能跨越多个程序或者代码文件
- 进入或者离开一个并行区域的分支都是非法的
- 只允许一个IF子句
- 只允许一个NUM_THREADS子句
Example: Parallel Region
- "Hello World" 程序
- 每一个线程都执行了并行区域的全部代码
- OpenMP 库例程用来获得线程标识与总线程数量
Fortran - Parallel Region Example PROGRAM HELLO INTEGER NTHREADS, TID, OMP_GET_NUM_THREADS, + OMP_GET_THREAD_NUM C Fork a team of threads with each thread having a private TID variable !$OMP PARALLEL PRIVATE(TID) C Obtain and print thread id TID = OMP_GET_THREAD_NUM() PRINT *, 'Hello World from thread = ', TID C Only master thread does this IF (TID .EQ. 0) THEN NTHREADS = OMP_GET_NUM_THREADS() PRINT *, 'Number of threads = ', NTHREADS END IF C All threads join master thread and disband !$OMP END PARALLEL END |
·
C / C++ - Parallel Region Example #include <omp.h> main () {
int nthreads, tid; /* Fork a team of threads with each thread having a private tid variable */ #pragma omp parallel private(tid) {
/* Obtain and print thread id */ tid = omp_get_thread_num(); printf("Hello World from thread = %d/n", tid); /* Only master thread does this */ if (tid == 0) {
nthreads = omp_get_num_threads(); printf("Number of threads = %d/n", nthreads); } } /* All threads join master thread and terminate */ } |