OpenMP是一种用于共享内存并行系统的多线程程序设计方案,支持的编程语言包括C、C++和Fortran。OpenMP提供了对并行算法的高层抽象描述,特别适合在多核CPU机器上的并行程序设计。编译器根据程序中添加的pragma指令,自动将程序并行处理,使用OpenMP降低了并行编程的难度和复杂度。当编译器不支持OpenMP时,程序会退化成普通(串行)程序。程序中已有的OpenMP指令不会影响程序的正常编译运行。

在VS中启用OpenMP很简单,很多主流的编译环境都内置了OpenMP。在项目上右键->属性->配置属性->C/C++->语言->OpenMP支持,选择“是”即可。

OpenMP执行模式

OpenMP采用fork-join的执行模式。开始的时候只存在一个主线程,当需要进行并行计算的时候,派生出若干个分支线程来执行并行任务。当并行代码执行完成之后,分支线程会合,并把控制流程交给单独的主线程。

一个典型的fork-join执行模型的示意图如下:

OpenMP编程模型以线程为基础,通过编译制导指令制导并行化,有三种编程要素可以实现并行化控制,他们分别是编译制导、API函数集和环境变量。

编译制导

编译制导指令以#pragma omp 开始,后边跟具体的功能指令,格式如:#pragma omp 指令[子句[,子句] …]。常用的功能指令如下:

  • parallel:用在一个结构块之前,表示这段代码将被多个线程并行执行;
    for:用于for循环语句之前,表示将循环计算任务分配到多个线程中并行执行,以实现任务分担,必须由编程人员自己保证每次循环之间无数据相关性;
    parallel for:parallel和for指令的结合,也是用在for循环语句之前,表示for循环体的代码将被多个线程并行执行,它同时具有并行域的产生和任务分担两个功能;
    sections:用在可被并行执行的代码段之前,用于实现多个结构块语句的任务分担,可并行执行的代码段各自用section指令标出(注意区分sections和section);
    parallel sections:parallel和sections两个语句的结合,类似于parallel for;
    single:用在并行域内,表示一段只被单个线程执行的代码;
    critical:用在一段代码临界区之前,保证每次只有一个OpenMP线程进入;
    flush:保证各个OpenMP线程的数据影像的一致性;
    barrier:用于并行域内代码的线程同步,线程执行到barrier时要停下等待,直到所有线程都执行到barrier时才继续往下执行;
    atomic:用于指定一个数据操作需要原子性地完成;
    master:用于指定一段代码由主线程执行;
    threadprivate:用于指定一个或多个变量是线程专用,后面会解释线程专有和私有的区别。

相应的OpenMP子句为:

  • private:指定一个或多个变量在每个线程中都有它自己的私有副本;
    firstprivate:指定一个或多个变量在每个线程都有它自己的私有副本,并且私有变量要在进入并行域或任务分担域时,继承主线程中的同名变量的值作为初值;
    lastprivate:是用来指定将线程中的一个或多个私有变量的值在并行处理结束后复制到主线程中的同名变量中,负责拷贝的线程是for或sections任务分担中的最后一个线程; 
    reduction:用来指定一个或多个变量是私有的,并且在并行处理结束后这些变量要执行指定的归约运算,并将结果返回给主线程同名变量;
    nowait:指出并发线程可以忽略其他制导指令暗含的路障同步;
    num_threads:指定并行域内的线程的数目; 
    schedule:指定for任务分担中的任务分配调度类型;
    shared:指定一个或多个变量为多个线程间的共享变量;
    ordered:用来指定for任务分担域内指定代码段需要按照串行循环次序执行;
    copyprivate:配合single指令,将指定线程的专有变量广播到并行域内其他线程的同名变量中;
    copyin:用来指定一个threadprivate类型的变量需要用主线程同名变量进行初始化;
    default:用来指定并行域内的变量的使用方式,缺省是shared。

API函数

除上述编译制导指令之外,OpenMP还提供了一组API函数用于控制并发线程的某些行为,下面是一些常用的OpenMP API函数以及说明:

环境变量

OpenMP中定义一些环境变量,可以通过这些环境变量控制OpenMP程序的行为,常用的环境变量:

  • OMP_SCHEDULE:用于for循环并行化后的调度,它的值就是循环调度的类型;  
    OMP_NUM_THREADS:用于设置并行域中的线程数;   
    OMP_DYNAMIC:通过设定变量值,来确定是否允许动态设定并行域内的线程数;  
    OMP_NESTED:指出是否可以并行嵌套。

简单示例之parallel使用

parallel制导指令用来创建并行域,后边要跟一个大括号将要并行执行的代码放在一起:

 
  1. #include<iostream>

  2. #include"omp.h"

  3. using namespace std;

  4. void main()

  5. {

  6. #pragma omp parallel

  7. {

  8. cout << "Test" << endl;

  9. }

  10. system("pause");

  11. }

执行以上程序有如下输出:

程序打印出了4个“Test”,说明parallel后的语句被4个线程分别执行了一次,4个是程序默认的线程数,还可以通过子句num_threads显式控制创建的线程数:

 
  1. #include<iostream>

  2. #include"omp.h"

  3. using namespace std;

  4. void main()

  5. {

  6. #pragma omp parallel num_threads(6)

  7. {

  8. cout << "Test" << endl;

  9. }

  10. system("pause");

  11. }

编译运行有如下输出:

程序中显式定义了6个线程,所以parallel后的语句块分别被执行了6次。第二行的空行是由于每个线程都是独立运行的,在其中一个线程输出字符“Test”之后还没有来得及换行时,另一个线程直接输出了字符“Test”。

简单示例之parallel for使用

使用parallel制导指令只是产生了并行域,让多个线程分别执行相同的任务,并没有实际的使用价值。parallel for用于生成一个并行域,并将计算任务在多个线程之间分配,从而加快计算运行的速度。可以让系统默认分配线程个数,也可以使用num_threads子句指定线程个数。

 
  1. #include<iostream>

  2. #include"omp.h"

  3. using namespace std;

  4. void main()

  5. {

  6. #pragma omp parallel for num_threads(6)

  7. for (int i = 0; i < 12; i++)

  8. {

  9. printf("OpenMP Test, 线程编号为: %d\n", omp_get_thread_num());

  10. }

  11. system("pause");

  12. }

运行输出:

上边程序指定了6个线程,迭代量为12,从输出可以看到每个线程都分到了12/6=2次的迭代量。

OpenMP效率提升以及不同线程数效率对比

 
  1. #include<iostream>

  2. #include"omp.h"

  3. using namespace std;

  4. void test()

  5. {

  6. for (int i = 0; i < 80000; i++)

  7. {

  8. }

  9. }

  10. void main()

  11. {

  12. float startTime = omp_get_wtime();

  13. //指定2个线程

  14. #pragma omp parallel for num_threads(2)

  15. for (int i = 0; i < 80000; i++)

  16. {

  17. test();

  18. }

  19. float endTime = omp_get_wtime();

  20. printf("指定 2 个线程,执行时间: %f\n", endTime - startTime);

  21. startTime = endTime;

  22. //指定4个线程

  23. #pragma omp parallel for num_threads(4)

  24. for (int i = 0; i < 80000; i++)

  25. {

  26. test();

  27. }

  28. endTime = omp_get_wtime();

  29. printf("指定 4 个线程,执行时间: %f\n", endTime - startTime);

  30. startTime = endTime;

  31. //指定8个线程

  32. #pragma omp parallel for num_threads(8)

  33. for (int i = 0; i < 80000; i++)

  34. {

  35. test();

  36. }

  37. endTime = omp_get_wtime();

  38. printf("指定 8 个线程,执行时间: %f\n", endTime - startTime);

  39. startTime = endTime;

  40. //指定12个线程

  41. #pragma omp parallel for num_threads(12)

  42. for (int i = 0; i < 80000; i++)

  43. {

  44. test();

  45. }

  46. endTime = omp_get_wtime();

  47. printf("指定 12 个线程,执行时间: %f\n", endTime - startTime);

  48. startTime = endTime;

  49. //不使用OpenMP

  50. for (int i = 0; i < 80000; i++)

  51. {

  52. test();

  53. }

  54. endTime = omp_get_wtime();

  55. printf("不使用OpenMP多线程,执行时间: %f\n", endTime - startTime);

  56. startTime = endTime;

  57. system("pause");

  58. }

以上程序分别指定了2、4、8、12个线程和不使用OpenMP优化来执行一段垃圾程序,输出如下:

可见,使用OpenMP优化后的程序执行时间是原来的1/4左右,并且并不是线程数使用越多效率越高,一般线程数达到4~8个的时候,不能简单通过提高线程数来进一步提高效率。

OpenMP基本概念【转】的更多相关文章

  1. OpenMP用法大全

    OpenMP基本概念OpenMP是一种用于共享内存并行系统的多线程程序设计方案,支持的编程语言包括C.C++和Fortran.OpenMP提供了对并行算法的高层抽象描述,特别适合在多核CPU机器上的并 ...

  2. 并行编程OpenMP基础及简单示例

    OpenMP基本概念 OpenMP是一种用于共享内存并行系统的多线程程序设计方案,支持的编程语言包括C.C++和Fortran.OpenMP提供了对并行算法的高层抽象描述,特别适合在多核CPU机器上的 ...

  3. 并行计算之OpenMP入门简介

    在上一篇文章中介绍了并行计算的基础概念,也顺便介绍了OpenMP. OpenMp提供了对于并行描述的高层抽象,降低了并行编程的难度和复杂度,这样程序员可以把更多的精力投入到并行算法本身,而非其具体实现 ...

  4. OpenMP并行程序设计

    1.fork/join并行执行模式的概念 2.OpenMP指令和库函数介绍 3.parallel 指令的用法 4.for指令的使用方法 5 sections和section指令的用法 1.fork/j ...

  5. OpenMp 基本

      OpenMp是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,用于共享内存并行系统的多线程程序设计的一套指导性的编译处理方案(Compiler Di ...

  6. openmp入门总结

    Ref: https://wdxtub.com/2016/03/20/openmp-guide/ 简介 这门课作为 ECE 中少有的跟计算机科学相关的课,自然是必上不可.不过无论是 OpenMP 还是 ...

  7. [转]OpenMP 入门指南

    简介 这门课作为 ECE 中少有的跟计算机科学相关的课,自然是必上不可.不过无论是 OpenMP 还是 CUDA,对于平时极少接触并行编程的我来说,都是十分吃力的,第一次作业的 OpenMP 编程已经 ...

  8. OpenMP使用体验报告(概述)

    (本文原创,首次使用OpenMP,将使用体会记录下来供学习) OpenMP是啥玩意??? 多核多线程处理器的出现,让并行计算成为可能.在此之前,单核处理器并不能并行计算,这是很显然的,只有一个核心只能 ...

  9. C++中轻量级多线程openmp

    关于其概念及使用方法: https://baike.baidu.com/item/openmp/3735430?fr=aladdin

随机推荐

  1. elasticsearch 7版本 基础操作

    elasticsearch 7版本 基础操作 首先我们浏览器http://localhost:5601/进入 kibana里的Console中输入 首先让我们在 Console 中输入: PUT t1 ...

  2. c# 字符串修改

  3. SpringCloud2.0 Zuul 网关路由 基础教程(十)

    1.启动基础工程 1.1.启动[服务注册中心],工程名称:springcloud-eureka-server 参考 SpringCloud2.0 Eureka Server 服务中心 基础教程(二) ...

  4. WebLogic反序列化漏洞(CVE-2019-2725补丁绕过)

    影响产品: Oracle WebLogic Server10.3.6.0.0 Oracle WebLogic Server12.1.3.0.0 影响组件: wls9_async_response.wa ...

  5. 让Jupyter Notebook个性化

    Win下更改jupyter主题 Themes地址 本人环境  Win+Conda 开始使用pip 安装,发现无法使用pip,修改环境变量,将D:\Program Files\Conda\Scripts ...

  6. 4.Linq to Xml

    目录 1.Linq to Xml函数构造方法 2.创建包含文本节点的Xml文档 3.保存和加载Xml 4.处理Xml片段 5.从数据库中生成XML 1.Linq to Xml函数构造方法 Linq t ...

  7. machine learning (4)---feature scaling

    feature scaling:缩小或扩大feature的值,使所有的feature处于类似的范围,这样进行gradient descnet时更快趋向最小值.因为不同的feature的范围相差很大时, ...

  8. c# DES 加密解密方法

    #region 密钥 private static string key = "abcd1234"; //密钥(长度必须8位以上) #endregion #region DES加密 ...

  9. ML,DL核心数学及算法知识点总结

    ML,DL核心数学及算法知识点总结:https://mp.weixin.qq.com/s/bskyMQ2i1VMNiYKIvw_d7g

  10. SignalR2实时聊天

    SignalR2实时聊天 NuGet包中搜索SignalR添加引用 using Microsoft.AspNet.SignalR; 创建OWIN启动类 namespace SignalRChat { ...