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. MySQL Network--域名与VIP

    VIP与域名1.域名能在多个IDC切换,而VIP通常在特定网段内切换.2.VIP切换可以立即生效,而域名切换存在一定时间延迟. DNS解析顺序:1.查询本地域名映射配置(/etc/hosts)2.查查 ...

  2. MySQL Case--应用服务器性能瓶颈导致慢SQL

    在分析优化慢SQL时,除考虑慢SQL对应执行计划外,还需要考虑 1. 慢SQL发生时间点的数据库服务器性能 2.慢SQL发生时间点的应用程序服务器性能 3. 慢SQL发生时间点数据库服务器和应用服务器 ...

  3. django--模型字段引用

    如果内置字段不起作用,您可以尝试使用django-localflavor(文档),其中包含对特定国家和文化有用的各种代码片段. 此外,您可以轻松编写自己的自定义模型字段. 注意 从技术上讲,这些模型是 ...

  4. Flink原理(五)——容错机制

    本文是博主阅读Flink官方文档以及<Flink基础教程>后结合自己理解所写,若有表达有误的地方欢迎大伙留言指出. 1.  前言 流式计算分为有状态和无状态两种情况,所谓状态就是计算过程中 ...

  5. python获取第前多少天的日期

    1. 显示昨天(前一天)的日期 from datetime import date, timedelta yesterday_date = (date.today() + timedelta(days ...

  6. Nginx编译安装和平滑升级

    一.Nginx的编译安装 1.安装依赖包gcc,gcc-c++,pcre,openssl-devel 命令:yum -y install gcc gcc-c++ pcre-devel openssl- ...

  7. 汽车电子测试项目管理系统-TPA

    概述 INTEWORK-TPA(Test Project Administrator, 以下简称TPA) 是一款集成的测试项目管理工具,它可以管理测试过程中的所有数据,包括需求.用例.样件.计划.报告 ...

  8. DT6.0下搜索页解决canonical获取乱码问题

    最近研究dt6.0,官方内核默认是把搜索页屏蔽的,但是做seo的人都知道,搜索页聚合是争取排名的好地方,所以我就二次开发搜索页,具体可以查看前几期分享的,今天说说关于搜索的canonical的url乱 ...

  9. 本地仓库推送到远程仓库:fatal: refusing to merge unrelated histories

    最近,在操作git的时候,遇到各种问题,下面总结一下. 最开始,我不是先把远程仓库拉取到本地 ,而是直接在本地先创建一个仓库,再git remote add添加远程仓库. 当然,gitee官方还是有操 ...

  10. php 常用字符串函数总结

    php里面自带的字符串函数,日期函数,数组函数等,有时候可以帮助我们解决很复杂的问题,运用起来也比较简单. 下面总结了一下常用的字符串函数. addcslashes — 为字符串里面的部分字符添加反斜 ...