文章目录

  • 一、OpenMP基本概念
  • 二、OpenMP执行模式
  • 三、编译制导
    • 3.1 常用的功能指令
    • 3.2 相应的OpenMP子句
  • 四、API函数
  • 五、环境变量
  • 六、简单示例
    • 6.1 parallel使用
    • 6.2 paraller for使用
    • 6.3 OpenMP效率提升以及不同线程数效率对比
    • 6.4 API使用

一、OpenMP基本概念

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

二、OpenMP执行模式

OpenMP采用fork-join的执行模式。开始的时候只存在一个主线程,当需要进行并行计算的时候,派生出若干个分支线程来执行并行任务。当并行代码执行完成之后,分支线程会合,并把控制流程交给单独的主线程。
一个典型的fork-join执行模型的示意图如下:
OpenMP编程模型以线程为基础,通过编译制导指令制导并行化,有三种编程要素可以实现并行化控制,他们分别是编译制导、API函数集和环境变量。

三、编译制导

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

3.1 常用的功能指令

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

3.2 相应的OpenMP子句

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

四、API函数

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

函数名 作用
omp_in_parallel 判断当前是否在并行域中
omp_get_thread_num 返回线程号
omp_set_num_thread 设置后续并行域中的线程格式
omp_get_num_threads 返回当前并行域中的线程数
omp_get_max_threads 返回并行域可用的最大线程数目
omp_get_num_prpces 返回系统中处理器的数目
omp_get_dynamic 判断是否支持动态改变线程数目
omp_set_dynamic 启用或关闭线程数目的动态改变
omp_get_nested 判断系统是否支持并行嵌套
omp_set_nested 启用或关闭并行嵌套

五、环境变量

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

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

六、简单示例

6.1 parallel使用

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

#include<iostream>
#include"omp.h"
using namespace std;
int main()
{#pragma omp parallel{cout << "Test" << endl;}return 0;
}

编译:g++ -fopenmp test_para.cpp -o test_para
运行./op_test
结果:打印了16个Test(笔者电脑是16核,所以打印16个)

6.2 paraller for使用

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

#include<stdio.h>
#include <stdlib.h>
#include<omp.h>
int main(int argc,char** argv)
{#pragma omp parallel for num_threads(6)for (int i = 0; i < 12; i++){printf("OpenMP Test, 线程编号为: %d\n", omp_get_thread_num());}return 0;}

编译:gcc -fopenmp test_parafor.c -o test_parafor
运行./test_parafor
结果:如下图

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

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

diff_threads.c

#include <stdlib.h>
#include <stdio.h>
#include "omp.h"void test()
{for (int i = 0; i < 80000; i++){//do something}
}int main(int argc, char **argv){float startTime = omp_get_wtime();//指定2个线程
#pragma omp parallel for num_threads(2)for (int i = 0; i < 80000; i++){test();}float endTime = omp_get_wtime();printf("指定 2 个线程,执行时间: %f\n", endTime - startTime);startTime = endTime;//指定4个线程
#pragma omp parallel for num_threads(4)for (int i = 0; i < 80000; i++){test();}endTime = omp_get_wtime();printf("指定 4 个线程,执行时间: %f\n", endTime - startTime);startTime = endTime;//指定8个线程
#pragma omp parallel for num_threads(8)for (int i = 0; i < 80000; i++){test();}endTime = omp_get_wtime();printf("指定 8 个线程,执行时间: %f\n", endTime - startTime);startTime = endTime;//指定12个线程#pragma omp parallel for num_threads(12)for (int i = 0; i < 80000; i++){test();}endTime = omp_get_wtime();printf("指定 12 个线程,执行时间: %f\n", endTime - startTime);startTime = endTime;//不使用OpenMPfor (int i = 0; i < 80000; i++){test();}endTime = omp_get_wtime();printf("不使用OpenMP多线程,执行时间: %f\n", endTime - startTime);startTime = endTime;return 0;
}

编译:gcc -fopenmp diff_threads.c -o diff_threads
运行./diff_threads
结果:如下图

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

6.4 API使用

API.c

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{printf("ID: %d, Max threads: %d, Num threads: %d \n", omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());omp_set_num_threads(5);printf("ID: %d, Max threads: %d, Num threads: %d \n", omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());#pragma omp parallel num_threads(5){// omp_set_num_threads(6);  // Do not call it in parallel regionprintf("ID: %d, Max threads: %d, Num threads: %d \n", omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());}printf("ID: %d, Max threads: %d, Num threads: %d \n", omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());omp_set_num_threads(6);printf("ID: %d, Max threads: %d, Num threads: %d \n", omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());return 0;
}

编译:gcc -fopenmp API.c -o api
运行./api
结果:如下图

OpenMP使用详解相关推荐

  1. eclipse版本详解以及下载安装步骤及启动eclispe报错整理

    一.eclispe版本详解 本段内容转载自:https://www.cnblogs.com/chenmingjun/p/8404628.html 1.eclipse版本代号一览表 Eclipse的设计 ...

  2. java编译器源码详解_已更新至第8章 | LLVM 编译框架详解

    LLVM是什么? 我又不做编译器研究,干嘛要管它? 这是一些朋友想要对LLVM发起的提问,那么今天我们就来看看那款据说很酷炫的编译器--LLVM. LLVM是什么? LLVM是一个自由软件项目,它是一 ...

  3. caffe学习之——CMakeList.txt内容详解

    在对Caffe使用cmake方式编译安装时产生了对这个文件的好奇,打算做个注释理解, 参考:https://blog.csdn.net/fuzi2012/article/details/7245453 ...

  4. darknet详解(yolo图像检测)

    darknet详解(yolo图像检测) 个人记录整理,如有转载请注明来源, 本文中包含的一张图片来源于网络,如有不妥请与我联系. 文章目录 darknet详解(yolo图像检测) 简介 安装 使用 使 ...

  5. 计算机编程种常见的几种编码详解

    计算机编程种常见的几种编码详解 其实计算机编程离不开编码 但是大多数都不能真正全面了解各种编码 今天就来好好和几位编码熟悉熟悉 一.字符.字符集和字符编码方式 字符:字符是抽象的最小文本单位.它没有固 ...

  6. 【JavaScript 教程】ES6 中的 Promise对象 详解

    [JavaScript 教程]ES6 中的 Promise对象 详解 1.Promise对象含义 promise是异步编程的一种解决方法. 所谓promise,简单说是一个容器,里面保存着某个未来才会 ...

  7. 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)

    首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...

  8. JVM年轻代,老年代,永久代详解​​​​​​​

    秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...

  9. docker常用命令详解

    docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...

最新文章

  1. Java是如何实现Future模式的?万字详解!
  2. 【计算理论】图灵机 ( 非确定性图灵机 与 计算树 | 非确定性 | 非确定性图灵机 与 确定性图灵机 相互模仿 | 非确定性图灵机 -> 确定性图灵机 )
  3. 腾讯电脑管家13内测版官方下载地址
  4. 【机器学习基础】Python机器学习的神器- Scikit-learn使用说明
  5. sap.ca.scfld.md.Startup.init('cus.crm.notes', this);
  6. 应届生求职产品经理系列【三】:0岁产品经理的八大困惑(上)
  7. Guava入门~MoreObjects
  8. 元宵节电商促销首页设计PSD分层模板
  9. django orm 之makemigrations和migrate命令
  10. 如何检查对象是否为数组?
  11. 梯度消失和梯度爆炸_梯度消失和梯度爆炸详解
  12. java.io.tmpdir
  13. 备课好帮手,免费分享 下载
  14. mybatis使用详解
  15. 一加nfc门禁卡录入_Card Emulator(NFC卡模拟):一加5NFC模拟门禁卡|饭卡|电梯卡 工卡 借书卡...
  16. c语言 sin1怎么写,c语言sin30怎么写
  17. MFCC和语谱图的关系
  18. windows python3.6 tensorflow1.12搭建RCNN运行环境 bug解决
  19. 651被远程计算机关闭,win7宽带连接提示错误651调制解调器报告了一个错误怎么办...
  20. 【opencv】双目视觉下空间坐标计算/双目测距 6/13更新

热门文章

  1. Python使用Coverage进行代码覆盖率统计
  2. Python 3.9.0 一键安装脚本
  3. 中英文最全的网络安全术语表-学网络安全必备
  4. 移动端app开发-02-iPhone/iPad/Android UI尺寸规范
  5. python扩展库导入方式_使用pip安装Python扩展库的方法
  6. KW 新闻 | KaiwuDB 亮相数字中国并发布离散制造场景解决方案
  7. Android中ImageView的点击变暗效果
  8. SLAM让你如何去理解这个世界
  9. java moco_moco入门
  10. 弹框插件sweetalert