• OpenMP入门教程(三)


    承接前面两篇,这里直接逐一介绍和使用有关OpenMP的指令和函数

    Directives

    1、for 

    作用:for指令指定紧随其后的程序的循环的迭代必须由团队并行执行,只是假设已经建立了并行区域,否则它在单个处理器上串行执行。

    格式:

     1 #pragma omp for [clause ...] newline 
     2                 schedule (type [,chunk]) 
     3                 ordered 
     4                 private (list) 
     5                 firstprivate (list) 
     6                 lastprivate (list) 
     7                 shared (list) 
     8                 reduction (operator:list) 
     9                 collapse (n) 
    10                 nowait for_loop

    可以使用如下子句:

    还可以通过Schedule子句(clause)设置for循环的并行化方法:(有关一种调度如何比其他调度更优化的讨论,请参阅http://openmp.org/forum/viewtopic.php?f=3&t=83

    • static:循环迭代被分成size chunk,然后静态的分配给各个线程,如果chunk没有被指定,则均匀地划分(如果可能)给各个线程
    • dynamic:循环迭代被分成size chunk,然后动态地分配给各个线程,当一个chunk完成时,被分配另外一个chunk。默认地chunk size为1
    • guided:当线程请求循环迭代时,迭代会动态地分配给块中地线程,直到没有剩余的块要被分配。与dynamic类似,不同的地方在于每次为线程分配chunk时都会变小,所以最初组中的循环体执行数目较大。初始大小与以下成正比,number_of_iterations / number_of_thread,后续块与之成比例,number_of_iterations_remaining / number_of_threads。
    • runtime: 循环的并行化方式不在编译时静态确定,而是推迟到程序执行时动态地根据环境变量OMP_SCHEDULE 来决定要使用的方法。此时在子句中指定chunk_size是非法的
    • auto:调度决策取决于编译器/运行时系统  

    nowait子句:如果指定,则线程在循环结束时不同步

    ordered子句:指定必须像在串行程序中一样执行循环的迭代,可以对for的部分使用

    collapse子句:指定嵌套循环中应将多少循环折叠到一个大的迭代空间中,并根据schedule子句进行划分 。折叠迭代空间中的迭代顺序被确定为顺序执行它们。可以改善表现。

    其它的子句后面会做介绍

    限制:

    • 循环迭代变量必须是整数,并且所有线程的循环控制参数必须相同
    • 程序正确性不能取决于哪个线程执行特定迭代,需要确保程序的正确性
    • 从for指令关联的循环中分支是非法的
    • 必须将块大小指定为循环不变整数表达式,因为在不同线程的评估期间没有同步

    示例

     1  #include <omp.h>
     2  #define N 1000
     3  #define CHUNKSIZE 100
     4 
     5  main(int argc, char *argv[]) {
     6 
     7  int i, chunk;
     8  float a[N], b[N], c[N];
     9 
    10  /* Some initializations */
    11  for (i=0; i < N; i++)
    12    a[i] = b[i] = i * 1.0;
    13  chunk = CHUNKSIZE;
    14 
    15  #pragma omp parallel shared(a,b,c,chunk) private(i)
    16    {
    17 
    18    #pragma omp for schedule(dynamic,chunk) nowait
    19    for (i=0; i < N; i++)
    20      c[i] = a[i] + b[i];
    21 
    22    }   /* end of parallel region */
    23 
    24  }

    2、section

    作用:section是一种非迭代的工作共享结构,代码被划分成多个区域

    格式:

     1 #pragma omp sections [clause ...]  newline 
     2                      private (list) 
     3                      firstprivate (list) 
     4                      lastprivate (list) 
     5                      reduction (operator: list) 
     6                      nowait
     7   {
     8 
     9   #pragma omp section   newline 
    10 
    11      structured_block
    12 
    13   #pragma omp section   newline 
    14 
    15      structured_block
    16 
    17   }

    注意:

    • 除非使用nowait子句,否则sections指令结尾都有一个隐含的障碍
    • 分区块里不能含有分支

    示例

     1  #include <omp.h>
     2  #define N 1000
     3 
     4  main(int argc, char *argv[]) {
     5 
     6  int i;
     7  float a[N], b[N], c[N], d[N];
     8 
     9  /* Some initializations */
    10  for (i=0; i < N; i++) {
    11    a[i] = i * 1.5;
    12    b[i] = i + 22.35;
    13    }
    14 
    15  #pragma omp parallel shared(a,b,c,d) private(i)
    16    {
    17 
    18    #pragma omp sections nowait
    19      {
    20 
    21      #pragma omp section
    22      for (i=0; i < N; i++)
    23        c[i] = a[i] + b[i];
    24 
    25      #pragma omp section
    26      for (i=0; i < N; i++)
    27        d[i] = a[i] * b[i];
    28 
    29      }  /* end of sections */
    30 
    31    }  /* end of parallel region */
    32 
    33  }

    3、其它的不一一介绍了,请参阅:OpenMP

    Clause

    前面已经介绍了几个子句,这里主要介绍数据作用域子句。

    1、private

    作用:private子句将其列表中的变量声明为每个线程的私有变量

    格式:

    private (list)

    要点:

    • 在组中的每个线程声明一个相同数据类型的变量
    • 所有对原始变量的引用全部替换为对新变量的引用
    • 被声明为private的变量应被认为未初始化

    2、shared

    作用:shared子句声明其列表中的变量,以便在团队中的所有线程之间共享

    格式:

    shared (list)

    要点:

    • 共享变量仅存在于一个内存位置,并且所有线程都可以读取或写入该地址
    • 程序员有责任确保多个线程正确访问SHARED变量(例如通过CRITICAL部分)

    3、reduction

    作用:reduction子句对列表中的每个变量执行简化操作。为每个线程创建并初始化每个列表变量的私有副本。在缩减结束时,reduce变量应用于共享变量的所有私有副本,最终结果将写入全局共享变量。

    格式:

    1 reduction (operator: list)

    示例:

    并行循环的迭代将以相同大小的块分配给团队中的每个线程(SCHEDULE STATIC);

    在并行循环结构的末尾,所有线程将添加其“result”值以更新主线程的全局副本;

     1 #include <omp.h>
     2 
     3  main(int argc, char *argv[])  {
     4 
     5  int   i, n, chunk;
     6  float a[100], b[100], result;
     7 
     8  /* Some initializations */
     9  n = 100;
    10  chunk = 10;
    11  result = 0.0;
    12  for (i=0; i < n; i++) {
    13    a[i] = i * 1.0;
    14    b[i] = i * 2.0;
    15    }
    16 
    17  #pragma omp parallel for        
    18    default(shared) private(i)    
    19    schedule(static,chunk)        
    20    reduction(+:result)  
    21 
    22    for (i=0; i < n; i++)
    23      result = result + (a[i] * b[i]);
    24 
    25  printf("Final result= %f
    ",result);
    26 
    27  }

    4、其它还有很多,省略

    Run-time Library Routines

    • OpenMP API包含越来越多的运行时库例程
    • 对于C / C ++,所有运行时库例程都是实际的子例程。对于Fortran,有些实际上是函数,有些是子例程。
    • 对于C / C ++,通常需要包含 <omp.h>头文件

    例如:

    1 #include <omp.h> 
    2 int omp_get_num_threads(void

    详细的函数介绍可见OpenMP入门教程(二)

    Environment Variables

    • OpenMP提供一些环境变量来控制并行程序的执行
    • 所有的环境变量名都是大写字母,但是分配给它们的值不区分大小写

    1、OMP_NUM_THREADS:设置在运行期间最大的线程数

    setenv OMP_NUM_THREADS 8

    2、OMP_DYNAMIC启用或禁用动态调整可用于执行并行区域的线程数。有效值为TRUE或FALSE

    setenv OMP_DYNAMIC TRUE

    3、OMP_PROC_BIND启用或禁用绑定到处理器的线程。有效值为TRUE或FALSE。

    setenv OMP_PROC_BIND TRUE

    4、OMP_STACKSIZE:控制创建(非主)线程的堆栈大小

    setenv OMP_STACKSIZE 2000500B 
    setenv OMP_STACKSIZE“3000 k” 
    setenv OMP_STACKSIZE 10M 
    setenv OMP_STACKSIZE“10 M” 
    setenv OMP_STACKSIZE“20 m” 
    setenv OMP_STACKSIZE“1G” 
    setenv OMP_STACKSIZE 20000

    4、还有很多其它的,省略

    注:前面的运行API也能做与环境变量一样的工作,同时使用环境变量和运行时 API 会出现什么情况?运行时 API 将获得更高的优先权。

    注:这是一个简单的OpenMP的练习网站:https://computing.llnl.gov/tutorials/openMP/exercise.html

    参考链接:https://computing.llnl.gov/tutorials/openMP/#Abstract

  • 相关阅读:
    Linux c 开发-17 pugixml xml_node Collection和一个数组的值比较示例
    Linux c 开发-16 不需要头文件也可以编译???
    SourceInsight快捷键
    Linux c 开发-16 VsCode下使用CMakeFile编译项目
    java 字符串转Base64
    Linux c 开发-15 Ubuntu子系统中使用串口
    Linux c 开发-14 一例不能直接调试gdb程序的解决办法
    Ubuntu子系统与Windows互相访文件系统
    Linux c 开发-13 Makefile与VisualStudio Linux C环境对应关系
    Linux c 开发-12 创建子进程
  • 原文地址:https://www.cnblogs.com/lfri/p/10117193.html
Copyright © 2020-2023  润新知