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编译原理及实现技术>第2章 代码测试环境:Windows7 64bit, VS2010, 4核机. 可以说OpenMP制导指令将C语言扩展为一个并行语言,但OpenM ...

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

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

  4. OpenMP使用详解

    文章目录 一.OpenMP基本概念 二.OpenMP执行模式 三.编译制导 3.1 常用的功能指令 3.2 相应的OpenMP子句 四.API函数 五.环境变量 六.简单示例 6.1 parallel ...

  5. openmp配置指南_openmp开发环境配置

    语句绑定和嵌套规则.库函数和环境变量设置 12. OpenMP计算实例 13. OpenMP应用程序设计的性能分析 2013-8-18 3 6.9 OpenMP线程同步与线程数据语句 ?数据竞争问题. ...

  6. OpenMP相关文章收藏学习

    BLOG 关于生产者消费者问题的OpenMP实现 https://blog.csdn.net/zhangjt33/article/details/81165253 简介:关于生产者消费者问题的Open ...

  7. 使用omp技术实现wordcount算法

    [问题描述] 编写程序统计一个英文文本文件中每个单词的出现次数(词频统计),并将统计结果按单词字典序输出到屏幕上. 注:在此单词为仅由字母组成的字符序列.包含大写字母的单词应将大写字母转换为小写字母后 ...

  8. 高性能计算开发软件培训班-选猿代码科技IT培训机构!

    学习CPU并行程序性能优化的意义: 学习CPU计算是现代计算机科学中不可或缺的一部分.掌握CPU计算原理和应用,能够提高我们的编程技能和解决问题的能力,帮助我们更好地应对计算机科学领域中的挑战和机遇. ...

  9. openmp官方源码_MNN推理过程源码分析笔记(一)主流程

    在正式开始推理代码分析之前, 回顾下 MNN整体结构 推理分为三个大部分 Engine Backends Runtime Optimize 那么问题来了,从哪里开始,怎么入手呢? 我的心得是源码分析不 ...

最新文章

  1. 【 C 】函数参数通过传值调用还是传址调用?
  2. linux怎么永久保存,Linux系统中,让alias命令永久保存的方法!
  3. java+分支合并主线_git如何合并两个远端分支?
  4. 51 Nod 阶乘后面0的数量
  5. 创建一个1000w个随机浮点数的数组
  6. java 图片地址,java中图片地址存入数据库
  7. WCF系列之什么是WCF
  8. Linux DNS服务详解——DNS基础知识
  9. 交换机TFTP配置备份
  10. Unity学习01-unity物体移动三种方式
  11. win10记得pin码 重置密码登录
  12. (图文详细)设置文件夹中的文件显示文件后缀名的方法
  13. Vue报错Invalid handler for event “click“: got undefined的原因及解决办法
  14. C#编程-47:选择类控件复习笔记
  15. WARNING: Waited 15 secs for write IO to PST disk 4 in group 3 in alert_asm.log
  16. SSH所选用户密钥为在远程主机上注册。请再试一次
  17. Chrome开发工具Network没有显示完整的http request和response对话
  18. Linux从零学习记录(四)
  19. 最低2000多,中国房价最低的30城,你会选择吗?
  20. 负载均衡获得真实源IP的6种方法

热门文章

  1. severity distribution: tail of distributions
  2. 希望找到计算机系的好期刊的影响因子的排序?并作为选刊依据?选SJR就足够了
  3. 要承认我们换电脑频繁的效率并不是很高
  4. hello2 source analysis
  5. 六年磨一剑 Novell转身云计算架构供应商
  6. 校讯通近期爆发短信诈骗:取消不可能 那如何规范
  7. Citrix虚拟化技术之一XenServer6.2安装
  8. C++:关于“error C2374: 'i' : redefinition; multiple initialization”
  9. 图解VS2010打包全过程
  10. .NET(C#) Internals: 以一个数组填充的例子初步了解.NET 4.0中的并行(一)