当前位置: 代码迷 >> 综合 >> OpenMP 参考 (指令格式)
  详细解决方案

OpenMP 参考 (指令格式)

热度:96   发布时间:2024-01-09 03:57:57.0

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++编译器指令标准
  • 每条指令只能有一个指令名称
  • 每条指令适用于最少一条被结构块包围的语句
  • 常指令可以在一条指令行结束后,用"/"连接

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

静态范围 
并行区域内的
DO 指令

孤立指令 
并行区域外的CRITICALSECTIONS 指令

动态范围 
CRITICAL 和SECTIONS 指令在DO 和PARALLEL 指令的动态范围内.

为什么这一点很重要?

  • 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
  • 从这个并行区域开始,代码被复制并被所有线程执行
  • 并行区域结束时有个隐藏的关卡,只有主控线程能在此之后继续执行
  • 并行区域内的任何线程终止都会终止所有的线程。工作不能继续,直到这点被消除定义。

有多少线程?

  • 并行区域内的线程数量是由以下因素决定的,按优先级排序:
    1.  IF 子句的值
    2.  NUM_THREADS 子句的设置
    3. 使用 omp_set_num_threads() 库函数
    4. OMP_NUM_THREADS 环境变量
    5. 默认实现-一般是一个计算机的CPU数量,也可能是动态的(参考下节).
  • 线程编号从0 (主线程) to N-1

动态线程:

  • 用 omp_get_dynamic() 库函数检测动态线程是否被启用.
  • 如果支持动态线程,下面两个方法可以启用动态线程:
    1. omp_set_dynamic() 库例程
    2. 设置 OMP_DYNAMIC 环境变量为TRUE

嵌套并行区域:

  • 使用 omp_get_nested()  库函数检测嵌套并行区域是否被启用.
  • 如果支持嵌套并行区域,可以用下面两个方法启用:
    1. omp_set_nested() 库例程
    2. 设置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 */
 
}