OpenMP和OpenACC

2023-11-13

OpenMP

OpenMP是CPU的并行编程模型,它使用编译器指令来识别并行区域。

omp_set_num_threads(n_streams);//用来指定要用到的CPU线程数,类似于设置环境变量
#pragma omp parrallel //标记代码为并行部分
{
    int i= omp_get_thread_num();//为每个主机线程返回唯一的线程ID,将该ID作为streams数组的索引,实现OpenMP线程与CUDA流的一一对应
    kernel_1<<<gria,block,0,streams[i]>>>(); 		 
    kernel_2<<<gria,block,0,streams[i]>>>();
    kernel_3<<<gria,block,0,streams[i]>>>();
    kernel_4<<<gria,block,0,streams[i]>>>();
}
//下面是上述代码更简单可靠的OpenMP指令形式
#pragma omp parrallel num_threads(n)//指定OpenMP并行区域里要用到的CPU线程数
{
    int i= omp_get_thread_num();//为每个主机线程返回唯一的线程ID
}

在OpenMP并行区域之前或之后进行分配/释放资源能够在该区域达到最佳的CPU利用率。

编译命令

$ nvcc -O3 -Xcomplier -fopenmp  src.cu -o dst -lgomp

OpenACC

OpenACC是OpenMP模型在GPU上的扩展,两者极其相似。

OpenAcc是CUDA的一个补充编程模型,使用基于编译器指令的API,具有高性能、可编程性和跨平台可移植性。OpenACC由Nvidia主攻编译器的子公司PGI提出并开源,它们的编译器为pgcc。

OpenACC 规范要求支持它的编译器预定义一个宏_OPENACC ,宏的值为 yyyymm ,其中 yyyy 是编译器所支持 OpenACC 版本的发布年份, mm 是月份。

编程模型

OpenACC CUDA
gangs Blocks
workers Warps
vectors Threads

OpenACC 的线程模型与 CUDA 的线程模型类似,但添加了一个并行的维度。 OpenACC 可以分为 gang 、worker 和 vector 三个并行层次。在上层, gang 类似于 CUDA 线程块。一个 gang 可包含一个或多个执行的线程,在每个 gang 内部都包含一个或多个 worker 。在 CUDA 中,一个 worker类似于线程中的一个线程束。每个 worker 都有一个向量宽度,由一个或多个同时执行相同指令的向量元素组成。毎个向量元素都类似于一个CUDA 线程,因为它是一个单一的执行流。 OpenACC 和 CUDA 线程模型之间的主要区别在于, OpenACC 在编程模型中直接指出了 worker 的概念(即线程束),而在 CUDA 中并没有明确建立线程束。
OpenACC 平台模型与 CUDA 类似,但它使用不同的术语和略有不同的抽象概念。 OpenACC 的目标是建立一个具有单线程的主机程序平台,在该主机程序中,将内核交付给多处理单元( PU ),在此平台上,每个 PU 一次只运行一个gang 。每个PU可以同时执行多个独立的并发执行线程(worker)。每个执行线程可以执行具有一定向量宽度的向量运算。在 OpenACC 中, gang 并行使用多个PU。每个 gang 里的多线程并行即为 worker 并行。每个 worker 里的并行以及一个跨向量操作的并行被称为向量并行。当在 GPU 上使用 OpenACC 时,一个PU就类似于一个 SM。

编程方法

OpenACC将串行程序并行化的手段是添加一些C/C++预处理语句,预处理语句分为directive(导语)和clause(子语)两类。

  • 导语(directive) 表示主要功能,每句有且只能有一个(除了parallel loopkernels loop),作用是给编译器一些指导,指出哪
    些代码需要并行化、需要怎么并行化,编译器根据程序员的指导信息生成最佳的并行代码。
  • 子语(clause) 表示对 导语(directive) 的修饰,更详细地表明导语的意图,每句可以有零个或多个。

一个directive(导语)和若干个clause(子语)构成一个功能模块construct(构件)。

#pragma acc direcive [clause ...]

当使用 OpenACC 时,由程序员用编译器指令指定并行代码区域,或是并行运行。编译器指令还可以指定使用何种类型的并行处理。编译器指令是一行源代码,用 C/C++编写,开头为#pragma 。 OpenACC 指令使用 acc关键字作为唯一标识,这意味着所有 OpenACC 指令都是以#pragma acc 开头的。

#pragma acc kernels loop gang(64),vector(128)//指定该循环使用64个线程块,且每个线程块有128个线程

如果不指定任何东西,编译器会选择他认为最佳的值。

默认情况下OpenACC模型使用同步内核调用。也就是说,主机处理器会等待GPU完成,然后在GPU内核调用返回后才继续执行,这类似于在C中进行函数调用,而不是生成一个从线程,随后进行汇合。

为此OpenACC为内核和数据提供了async子句以允许它们在主机端异步地运行并使传输与主机端操作异步执行

#pragma acc kernels loop async
for(i=0,i<num;i++){
    ...
}

异步传输需要锁页内存,也就是说不会交换到感盘上的内存。在 OpenACC 中,不需要像在 cUDA 中那样特别关心这些。指定 async 子句将会导致 OpenACC 编译器在传输的底层使用锁页内存。当然,使用异步操作时要记住的一点是,在异步操作完成之前,不能交换传输到内核或内核操作的数据。

计算构件

OpenACC 有两个计算构件( Compute Construct ): parallel 和 kernels ,用来将循环并。两个构件的目标是一样的,但行为有较大的区别。初学者应优先使用简单的 kernels 构件,熟练以后可以使用 parallel 构件,众多的子语还能够精细控制并行化方案。

编译器将把 kernels 构件区域编译成一系列在加速器设备上执行的 CUDA kernel ,这也是 kernels 构件名字是复数形式的原因。

编译器将 kernels 区域内的代码分割为一系列的加速器内核( kernel )。通常,每个非嵌套循环成为一个单独的内核。当程序遇到一个 kernels 构件时,它在设备上按顺序启动这一系列内核。对不同的内核, gang 的数量、每个 gang 包含多少个worker 、vector 的长度以及三者的组织方式都可能不同。

如果没有使用 async子语, kernels 区域结東时将有一个隐式障碍,本地线程不再向前执行,直至所有的内核都执行完毕。

parallel构件
#pragma acc parallel [子语列表]
{
    //结构块
}

parallel的子语:

  • async[(id)] :接收一个可选的整型参数,给内核块指定唯一ID
  • wait [(id列表)]:等待ID内核块执行完成
  • num_gangs (整数表达式)
  • num_workers (整数表达式)
  • vector_length(整数表达式)
  • device_type(设备类型列表)
  • if (条件)
  • reduction (操作符:变量列表)
  • copy (变量列表)
  • copyin (变量列表)
  • copyout (变量列表)
  • create (变量列表)
  • present (变量列表)
  • deviceptr (变量列表)
  • private(变量列表)
  • firstprivate (变量列表)
  • default(none|present)
kernels构件
#pragma acc kernels [子语列表]
{
    //结构块
}

kernels的子语:

  • async(id)
  • wait[(整数表达式列表)]
  • num_gangs (整数表达式)
  • num_workers (整数表达式)
  • vector_length (整数表达式)
  • device_type(设备类型列表)
  • if(条件)
  • copy(变量列表)
  • copyin(变量列表)
  • copyout (变量列表)
  • create (变量列表)
  • present (变量列表)
  • deviceptr (变量列表)
  • default(none|present)
data构件
#pragma acc data [子语列表]
{
    //结构块
}

data的子语:

  • if (条件)
  • copy (变量列表)
  • copyin (变量列表)
  • copyout (变量列表)
  • create (变量列表)
  • present (变量列表)
  • deviceptr (变量列表)
enter data导语
#pragma acc enter data [子语列表]

enter data的子语:

  • if (条件)

  • async(id)

  • wait [(整数表达式列表)]

  • copyin (变量列表)

  • create (变量列表)

exit data导语
#pragma acc exit data [子语列表]

exit data的子语:

  • if (条件)

  • async[(整数表达式)]

  • wait [(整数表达式列表)]

  • copyout (变量列表)

  • delete (变量列表)

  • finalize

host_data导语
#pragma acc host_data [子语列表]
 结构化块

host_data的子语:

  • use_device(变量列表)
loop导语
#pragma acc loop [子语表表]
	for循环 

loop 的子语:

  • collapse(n)
  • gang[( gang 参数列表)]
  • worker[([num:] 整数表达式)
  • vector[([ length :]整数表达式)]
  • seq
  • auto
  • tile(尺寸表达式列表)
  • device_type (设备类型列表)
  • independent
  • private (列表)
  • reduction (操作符:列表)
组合导语
#pragma acc parallel loop [子语列表]
for循环
    
#pragma acc kernels loop [子语列表]
for循环
declare导语
#pragma acc declare [子语表表]

declare的子语:

  • copy (变量列表)
  • copyin (变量列表)
  • copyout (变量列表)
  • create (变量列表)
  • present (变量列表)
  • deviceptr (变量列表)
  • device_resident(变量列表)
  • link(变量列表)
init导语
#pragma acc init [子语表表]

init的子语:

  • device_type(设备类型列表)
  • device_num(整数表达式)
shutdown导语
#pragma acc shutdown [子语表表]

shutdown的子语:

  • device_type(设备类型列表)
  • device_num(整数表达式)
set 导语
#pragma acc set[子语列表]

set 的子语:

  • default_async(整数表达式)
  • device_num(整数表达式)
  • device_type (设备类型列表)
update 导语
#pragma acc update [子语列表]

update 的子语:

  • async(id)

  • wait [(整数表达式列表)]

  • device_type (设备类型列表)

  • if (条件)

  • if_present

  • self (变量列表)

  • host (变量列表)

  • device (变量列表)

routine 导语
#pragma acc routine [子语列表]

#pragma acc routine(名字) [子语列表]

routine的子语:

  • gang

  • worker

  • vector

  • seq

  • bind(名字)

  • bind(字符串)

  • device_type(设备类型列表)

  • nohost

wait 导语
#pragma acc wait[(整数表达式列表)] [子语列表]

wait 的子语:

  • async[(整数表达式)]

子语含义

independent子语

independent 子语告诉编译器该循环的迭代步是彼此数据独立的。因此允许生成并行执行这些迭代步的代码,且不需要同步。在一个 parallel 构件内,所有不带 seg 子语的 loop 导语都暗含一个 independent 子语。

下而的子语常用于探度调优,在本书中应用不是特别多,只列出含义,不再举例,(1) privae 子语
loop 构件上的 private 子语指明,要为变量列表中的每一项创建一个刷本。如循环体以向量分製模式执行,那么与每一个向量通道相关联的每一个线程都会创建…个本。如果循环体以 worker 分裂 vector 单独的模式执行,那么将只创建一个变量副本,井在们一个 worker 内部的所有向量通道的关联线程间其享。其他情形中,创建一个副本,并在你一个 gang 中的所有 worker 中的所有向量通道的关联线程之间共享。

colapse 子语

collapae子语用来指定与1oop构件相关联的紧密嵌套循环有多少层。 collapse 子语的参数必须是一个常量正整数表达式。如果没有 collapse 子语,只有紧接着的循环才与 loop 构件相关联,
如果有一个以上的循环与 loop 构件关联,那么关联循环中的所有循环体都会按照剩余的子语来调度。与 collapse 子语相关联的所有循环的迭代步数必须是可计算的,并在所有循环中保持不变。
编译器自行决定是否将导语的 gang 、 worker 或 vector 子语应用到每一个循环、线性化迭代空间。

gang 子语

当上一层计算构件是一个 kernela 构件时, gang 子语指明,关联循环的迭代步需要在为内核创建的所有 gang 上并行执行。如果指定一个不带关键字的参数,或者指定一个放在 num 关键字后而的参数,那么该參数指明使用多少个 gang 来执行本循环的迭代步。除非处在一个嵌套的 parallel 区域内或嵌套的 kernels 区域内,否则不允许一个带 gang 子语的1oop区城包含另外一个带 gang 子语的loop区域。
除非出现 static 参数,否则循环的迭代步对各个 gang 的调度方式不确定。如果出现一个带星号的 atatic 参数,那么编译器将自行决定块的尺寸。

数据指令

在OpenACC中,#pragma acc data被显式地用于主机 与加速器之间的数据传输,类似于CUDA中的cudaMemcpy。与 kernels 和 parallel 指令类似,数据被应用到代码的某个区域。它定义了在该区域边界处必须进行的数据传输工作。例如,可以把一个变量标记为copyin ,也就是说,可以将这个变量在该区域的起始位置传送给加速器,但最后不能传出。相反地, copyout 是在数据区的末端将该变量传回主机端,但不能在该数据区域的起始位置将其传给加速器。

#pragma acc data copyin(A[0:N],B[0:N]) copyout(C[0:N],D[0:N) 
{
	#pragma acc parallel 
    {
         #pragma acc loop
         for(i=0;i<M;i++){
         	C[i]= A[i]+B[i];
         }
         #pragma acc loop 
         for(i=0;i<M;i++){
             D[i]=C[i]*A[i] ;
         }   
    }
}

所添加的#pragma acc data指令通知编译器:只有A和 B 应该拷贝到设备端,只有 C 和 D 应该拷贝回主机端。该段代码还指明了传输数组的范围,在这种情况下,传输的应该是整个数组。在某些情况下,编译器能够推断出要复制的数组大小,这能略微简化代码:

#pragma acc data copyin(A,B) copyout(C,D)

除了数据指令,在执行过程中也可以用#pragma acc enter data#pragma acc exit data 来标记任意节点传人和传出加速器的数组。当编译器遇到 enter data指令时,它会指明哪些数据应该复制到设备端。这些数据将继续留在设备端,直到编译器遇到将其传回的exit data 指令或者程序终止执行。当与async 子句和 wait子句相结合时, enter data指令和exit data指令能够发挥最大的作用。注意。 data 指令不支持 async子句和wait子句。
当把 async子句应用到 enter dataexit data 指令中时,它会创建将数据传人或传出加速器的异步传输任务,类似于 cudaMemcpyAsync 。正如在 CUDA 中异步拷贝是很有用的一样,作为一种重叠计算和通信的方法,在 OpenACC 中它也是很有用的。当把 wait 子句应用到 enter dataexit data 指令中时,它的作用与在 kernels 指令或 parallel 指令中一样:通信指令要等待其他异步任务结束后再执行。需要注意的是通信指令(即 enter data 和 e data 指令)可以便用 asyne 和 wait 子句来交互异步计算任务(即 kernels 指令和 parallel 作今),反之亦然。参考下面的代码段:

#pragma acc data <directives>

其中<directives>可以是以下之一:

  • copy(data1 data2 …)
  • copyin(data1 data2 …)
  • copyout(data1 data2 …)
  • create(data1 data2 …)
  • present(data1 data2 …)

多GPU

OpenACC 标准仅支持“毎个 GPU 一个 CPU 线程”形式的单个节点上的多GPU 。这可以充分利用多核CPU的潜力。

只需简单地使用OpenMP指令启动一些线程就行了

acc_set_device_num API调用是每个主机线程执行一次的事件

编译方法

$ pgcc -acc -Minfo=accel src.c -o app

为PGI编译器添加-acc标志使其支持OpenACC,允许其在提供的代码中识别带有#pragma acc的指令。添加

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenMP和OpenACC 的相关文章

随机推荐

  • 解决Windows11系统缺少Windows.Data.Pdf.dll文件出现错误问题

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库 这时你可以下载这个Windows
  • nodeJS fs.writeFile 实现文字换行

    写文件遇见需要换行的情况就在需要换行的内容前面加换行符号就阔以了 简单 r n windows n linux r mac 下面是一个复制文件的方法 每次追加时间戳的时候希望他换行 gt appendFile 的时候内容前面加 r n co
  • php自动验证,ThinkPHP 自动验证及验证规则详解

    ThinkPHP 自动验证 ThinkPHP 内置了数据对象的自动验证功能来完成模型的业务规则验证 自动验证是基于数据对象的 而大多情况下数据对象是基于 POST表单 不是绝对的 创建的 基本的自动验证功能包括 必须字段 email邮箱格式
  • 使用 ChatGPT、Stable Diffusion、React 和 NodeJS 构建网站画廊

    TLDR 在本文中 您将学习如何构建一个 Web 应用程序 该应用程序使用 ChatGPT 和 Stable Diffusion 为您提供的任何网站描述生成徽标和合适的域名 介绍 人工智能正在接管世界 这些技术每天都在震撼着我们的世界 Ch
  • 家里用服务器放在哪个位置,路由器摆放在家中哪个位置好 路由器摆放位置【详解】...

    路由器摆放在家中哪个位置好 路由器的摆放位置其实非常讲究的 这里就给大家讲解下相关知识 一起来看看 其实wifi所发射的信号 也就是无线电波 向手机和收音机发射出的电磁波是一样的 但是呢wifi的信号相当的短 一般常见的话只有12公分左右
  • Windows Server 系列 - User logon name(pre-Windows 2000) 和 User logon name 的区别

    一 在Active Directory中一直疑惑User logon name pre Windows 2000 和 User logon name这两个字段的区别 详细如下 AD UI界面展示名称 AD 后端属性名称 User logon
  • 使用Composition API和setup语法糖重构Vue组件

    Vue3 引入了Composition API 它是一种更灵活的方式来组织和复用组件的逻辑 而不是依赖于传统的选项式API 如data methods computed等 Composition API的核心是一个名为setup的函数 它可
  • 如何在github上重命名或修改文件夹

    在github上整理流程的时候 有一个文件夹命名不合适 想返回去改 但是在网页上没有找到重命名文件夹的选项 经过一番折腾之后 我是这么做的 1 首先在服务器上找到公匙 公匙在 ssh目录下 以 pub结尾的文件 将其复制 2 在github
  • markdown基本用法

    标题 和 都可以用于表示标题 一级标题 二级标题 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 标题的前后都要空一行 号后应当加一个空格 和 应当顶格书写 建议使用 来表示标题 字体 斜体 斜体 加粗 粗体 斜体 加粗 斜体
  • OpenCV中的人脸活体检测和身份认证如何实现?OpenCV人脸识别

    本文将介绍如何在OpenCV中实现人脸活体检测和身份认证 结合人脸检测 关键点定位和深度学习模型 我们可以有效地检测和区分真实人脸和照片 视频等非真实生物特征 以实现可靠的身份认证和活体检测 人脸检测和关键点定位 使用OpenCV提供的人脸
  • [STM32学习笔记(一)] 如何安装keil5 MDK版本并安装C51

    文章目录 1 注意事项 2 安装流程 2 1 获取Keil5安装包 2 2 安装keil5 2 3破解keil5 MDK 2 4 安装STM32芯片包 3 在安装了mdk的基础上安装c51 1 注意事项 安装路径必须全部是英文 如果已经安装
  • 突破前端反调试:阻止页面无限不断debugger

    不知道你们有没有遇到过上图这样 有时候想调试网站 一打开开发者工具立即 debugger 而且跳过了还是会继续 或者是有时候在调试网页时 突然就给你来一个 debugger 接着就是反复来回 debugger 了 贼烦 那今天分享个教程 教
  • Spock1

    文章目录 背景 扩展 BDD Behavior driven development行为驱动测试 依赖 Demo Spock深入 结构 setup与given assert 异常断言 Mock 创建对象 注入对象 调用频率约束 目标约束 方
  • Nacos-2.1.1安装配置+集群

    Nacos安装配置 集群 nacos 2 1 1安装配置 集群 Linux 一 环境准备 二 Nacos安装 运行 单机 三 替换nacos内置数据源 四 nacos集群配置 nacos 2 1 1安装配置 集群 Linux 本篇博客用于记
  • linux 启动盘zhi,Linux制作启动盘之dd命令详解

    1 dd命令简介 dd在linux中是 一个非常强大的工具 常用于复制大量数据 测试读写性能 清空硬盘数据 不可恢复 由于dd 命令允许以二进制方式读写 所以特别适合在原始设备上输入 输出 dd命令用于复制文件并对原文件的内容进行转换和格式
  • Windows 通过CMD窗口利用mybatis-generator连接Oracle快速生成代码

    环境说明 Windows10 JDK8 ojdbc6 11 2 0 4 jar mybatis generator core 1 3 7 jar 1 在C盘新建autoMybatis文件夹 文件夹中新建generator xml文件 并将o
  • bzoj3309 DZY Loves Math

    题目链接 bzoj3309 题目大意 对于正整数n 定义f n 为n所含质因子的最大幂指数 给定正整数a b 求 ai 1 bj 1f gcd i j sum i 1 a sum j 1 b f gcd i j T lt 10000 1 l
  • Android系统中设置TextView的行间距

    Android系统中TextView默认显示中文时会比较紧凑 不是很美观 为了让每行保持一定的行间距 可以设置属性android lineSpacingExtra或android lineSpacingMultiplier 1 设置行间距
  • Echarts中X轴label间隔显示=>interval

    项目中我们经常都会遇到大量数据 需要我们的echarts来展示 但是我们的X轴的label是长度是有限的 在大量数据的传递下必然出现label重叠 这个时候我们就要用到interval如图 通过设置xAxis中的axisLabel inte
  • OpenMP和OpenACC

    OpenMP OpenMP是CPU的并行编程模型 它使用编译器指令来识别并行区域 omp set num threads n streams 用来指定要用到的CPU线程数 类似于设置环境变量 pragma omp parrallel 标记代