CUDA效率优化之CUDA Graph的使用

news2024/10/6 8:37:32

CUDA系列文章


文章目录

  • CUDA系列文章
  • 前言
  • 一、优化方案
    • 简单顺序调用
  • 二、Overlapping
  • 三、使用CUDA Graph
  • 总结


前言

GPU 架构的性能随着每一代的更新而不断提高。 现代 GPU 每个操作(如kernel运行或内存复制)所花费的时间现在以微秒为单位。 但是,将每个操作提交给 GPU 也会产生一些开销——也是微秒级的。

实际的应用程序中经常要执行大量的 GPU 操作:典型模式涉及许多迭代(或时间步),每个步骤中有多个操作。 如果这些操作中的每一个都单独提交到 GPU 启动并独立计算,那么提交启动开销汇总在一起可能导致明显的整体性能下降。

CUDA Graphs 将整个计算流程定义为一个图而不是单个操作的列表。 最后通过提供一种由单个 CPU 操作来启动图上的多个 GPU 操作的方式减少kernel提交启动开销,进而解决上述问题。 下面,通过一个非常简单的示例来演示如何使用 CUDA Graphs。

假设我们有一系列执行时间非常短的kernels:

Loop over timesteps
    …
    shortKernel1
    shortKernel2
    …
    shortKernelN
    …

而其中每个kernel都像下面这样简单:

#define N 500000 // tuned such that kernel takes a few microseconds

__global__ void shortKernel(float * out_d, float * in_d){
  int idx=blockIdx.x*blockDim.x+threadIdx.x;
  if(idx<N) out_d[idx]=1.23*in_d[idx];
}

从内存中读取浮点数的输入数组,将每个元素乘以一个常数因子,然后将输出数组写回内存。 该kernel单个执行所用的时间取决于数组大小。上面的例子中,在数组大小设置为 500,000 个元素时,kernel执行需要几微秒。 使用profiler测量所花费的时间,在使用 CUDA 10.1 的 NVIDIA Tesla V100 GPU 上运行(同时将每个block的线程数设置为 512 个线程),耗时为2.9μs。 后面,我们将保持这个kernel不变,只改变它的调用方式。


一、优化方案

简单顺序调用

#define NSTEP 1000
#define NKERNEL 20

// start CPU wallclock timer
for(int istep=0; istep<NSTEP; istep++){
  for(int ikrnl=0; ikrnl<NKERNEL; ikrnl++){
    shortKernel<<<blocks, threads, 0, stream>>>(out_d, in_d);
    cudaStreamSynchronize(stream);
  }
}
//end CPU wallclock time

双层循环中 ,内层循环调用内核 20 次,外层进行 1000 次迭代。 在CPU使用timer记录整个操作所花费的时间,然后除以 NSTEP*NKERNEL,得到每个内核 9.6μs(包括启动kernel开销),要远高于 2.9μs 的纯内核执行时间。

由于在每次内核启动后都调用了 cudaStreamSynchronize 方法,所以每个kernel在前一个完成之前不会启动。 这意味着与每次启动相关的任何开销都将完全暴露:总时间将是内核执行时间加上任何开销的总和。 可以使用 Nsight Systems 分析器直观地看到这一点:
在这里插入图片描述上图截取显示了timeline的一部分(时间从左到右),包括 8 次连续的kernel启动。 理想情况下,GPU 应该保持忙碌计算状态,但情况显然并非如此。 在“CUDA (Tesla V100-SXM2-16G)”右侧部分可以看到每个kernel执行间都有很大的gap,此时GPU处于空闲状态。

在CUDA API的那一行,紫色块代表着CPU调用kernel启动方法的耗时,绿色块代表同步GPU所需的时间(包括等待kernel启动完成耗时+计算),cpu对kernel启动方法的调用耗时+kernel启动本身的耗时最后加起来就成为上面gap的时间了。

虽然这个时间尺度上,分析器本身会增加一些额外的启动开销,因此为了准确分析性能,应该使用基于CPU计时器。 尽管如此,分析器在帮助我们理解代码行为方面仍然具有指导意义。

二、Overlapping

// start wallclock timer
for(int istep=0; istep<NSTEP; istep++){
  for(int ikrnl=0; ikrnl<NKERNEL; ikrnl++){
    shortKernel<<<blocks, threads, 0, stream>>>(out_d, in_d);
  }
  cudaStreamSynchronize(stream);
}
//end wallclock timer

一个简单有效的优化方案,是overlap不同kernel的调用和执行过程。

虽然上面的代码中,由于kernel在同一个stream中,它们仍将按顺序执行。但现在由于不需要每个kernel执行都进行同步(调用cudaStreamSynchronize),使得在前一个kernel执行完成之前可以启动下一个kernel(kernel调用是异步的),最终达到了将kernel启动开销隐藏在内核执行时间内。此时测量每个内核所花费的时间(包括开销)为 3.8μs。与 2.9μs 内核执行时间相比,这已大大改善,但仍然存在与多次启动相关的开销。
在这里插入图片描述可以看到绿色块代表的同步时间已经基本没有了(只有进入外层循环时会产生),但是不同kernel执行间还是有一定的gap存在。

三、使用CUDA Graph

bool graphCreated=false;
cudaGraph_t graph;
cudaGraphExec_t instance;
for(int istep=0; istep<NSTEP; istep++){
  if(!graphCreated){
    cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal);
    for(int ikrnl=0; ikrnl<NKERNEL; ikrnl++){
      shortKernel<<<blocks, threads, 0, stream>>>(out_d, in_d);
    }
    cudaStreamEndCapture(stream, &graph);
    cudaGraphInstantiate(&instance, graph, NULL, NULL, 0);
    graphCreated=true;
  }
  cudaGraphLaunch(instance, stream);
  cudaStreamSynchronize(stream);
}

引入了两个新对象:

cudaGraph_t 类型的对象定义了kernel graph的结构和内容;
cudaGraphExec_t 类型的对象是一个“可执行的graph实例”:它可以以类似于单个内核的方式启动和执行。

首先,定义一个kernel graph,然后通过 cudaStreamBeginCapture 和 cudaStreamEndCapture 方法来捕捉它们之间stream上所有的 GPU kernel,来得到kernel graph。
然后,必须通过 cudaGraphInstantiate 调用实例化graph,该调用创建并预初始化所有kernel工作描述符,以便它们可以尽可能快地重复启动。
最后,通过 cudaGraphLaunch 调用提交生成的实例以供执行。
关键点在于,kernel graph只需要捕获和实例化一次,并在所有后续循环中重复使用相同的实例(上例中由 graphCreated 布尔值上的条件语句控制)。

所以实际的执行流程是:

循环第一个步:
捕捉创建和实例化kernel graph
启动kernel graph(包含 20 个kernel)
等待kernel graph 执行完成
对于剩余循环步骤:
启动kernel graph(包含 20 个kernel)
等待kernel graph 执行完成

测量这个完整过程所花费的时间,除以 1000×20 得到每个内核的有效时间(包括开销),得到 3.4μs(相对于 2.9μs 内核执行时间),成功地进一步降低了开销。 请注意,在这种情况下,创建和实例化图的时间相对较大,约为 400μs,但这仅执行一次,平摊到每个kernel上约为 0.02μs。 同样,第一个图启动比所有后续启动慢约 33%,但当多次重复使用同一个图时,这变得微不足道。 初始化的开销是否不可接受显然取决于问题(也可以采用程序的预热来规避):通常为了从cuda graph中受益,需要重复调用相同的cuda graph足够多次。 许多现实世界的问题涉及大量重复执行,因此适合使用cuda graph。

当前的profiler和cuda graph还做不到完全兼容(Sep 05, 2019,现在应该ok了),所以开启profiler会禁用cuda graph,所以上不了图。但可以想象一下它的样子,大概就是20个kernel紧密执行,中间加上graph本身的启动开销。
进一步学习

即使在上述非常简单的演示案例中(其中大部分开销已经通过重叠的内核启动和执行隐藏),也能看到 CUDA Graphs 对效率的提升,但更复杂的计算逻辑提供了更多优化提升的空间。 cuda graph支持多个stream间的融合,而且不仅可以包含kernel执行,还可以包括在主机 CPU 上执行的函数和内存拷贝。在 CUDA 示例中的 simpleCUDAGraphs 有更详尽的例子。

另外,除了自动捕捉graph,也可以通过 API 调用显式定义节点和依赖关系——simpleCUDAGraphs 示例中有采用这两种技术来完成相同问题的例子。 此外,graph还可以跨越多个 GPU。

在同一个graph中包含更多的kernel信息,显然也给cuda以更多的优化空间。可以查看Programming Guide中CUDA Graphs的章节。在GTC 2019 talk 中也有相关信息:CUDA: New Features and Beyond.


总结

翻译自:Getting Started with CUDA Graphs | NVIDIA Developer Blog

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/413111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++跬步积累】——时间复杂度

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;C跬步积累 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日一题 &#x1f7e1; 每日反刍 &#x1f7e2; 读书笔记 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称王&a…

马云回国,首谈ChatGPT

马云今天回国了&#xff0c;这是一个备受关注的消息。 作为中国最具代表性的企业家之一&#xff0c;马云在过去的二十多年里&#xff0c;带领阿里巴巴从一个小小的创业公司&#xff0c;发展成为全球最大的电商平台之一&#xff0c;同时也推动了中国互联网行业的发展。 他的回…

使用向量机(SVM)算法的推荐系统

系统整体结构 运行环境 包括Python环境、TensorFlow环境、安装模块、MySQL数据库。 Python环境 需要Python 3.6及以上配置&#xff0c;在Windows环境下推荐下载Anaconda完成Python所需的配置&#xff0c;下载地址为https://www.anaconda.com/&#xff0c;也可下载虚拟机在Li…

项目经理如何做好项目数据分析?

“周一到周五哪天更适合工作&#xff1f;” 周一困倦&#xff0c;周二认命&#xff0c;周三亢奋&#xff0c;周四疲惫&#xff0c;周五又像打了鸡血…… 项目经理&#xff1a;哪天都不适合工作&#xff0c;项目日报、周报、月报一个都少不了&#xff1b;不是在加班整理项目数…

Direct3D 12——灯光——镜面光照

反射的发生是根据一种名为菲涅耳效应(Fresnel effect,也译作菲涅 尔效应)的物理现象。当光线到达两种不同折射率(index of refraction )介质之间的界面时&#xff0c;一部分光将 被反射&#xff0c;而剩下的光则发生折射(refract),折射率是一种介质的物理性质&#xff0c;即 光…

Day939.如何小步安全地升级数据库框架 -系统重构实战

如何小步安全地升级数据库框架 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于如何小步安全地升级数据库框架的内容。 当消息组件的数据存储都是采用 SQL 拼写的方式来操作&#xff0c;这样不便于后续的扩展及维护。除此之外&#xff0c;相比前面的其他重构&#x…

线性代数 --- Gram-Schmidt, 格拉姆-施密特正交化(上)

在求解最小二乘的问题时&#xff0c;已经介绍了类似于Gram-Schmidt的一些想法。在这里要继续介绍这些想法&#xff0c;那就是如何“改写”矩阵A中的列向量&#xff0c;使得最小二乘解的计算越来越简单&#xff0c;甚至可以直接写出答案。 标准正交基(Orthonormal Bases) 上一篇…

3.1 多维度随机变量及其分布

思维导图&#xff1a; 学习目标&#xff1a; 要学习二维随机变量及联合分布&#xff0c;我会按照以下步骤进行&#xff1a; 了解基本概念&#xff1a;首先要了解二维随机变量的概念&#xff0c;即同时包含两个随机变量的变量。还要了解二维随机变量的取值范围以及联合概率密…

【CSS】定位 ④ ( 绝对定位特点 | 相对定位不脱标示例 | 绝对定位脱标示例 )

文章目录一、绝对定位特点二、相对定位不脱标示例三、绝对定位脱标示例一、绝对定位特点 绝对定位 以 带有定位的 父级元素 为基准 , 通过 边偏移 移动位置 ; 如果 绝对定位 的元素 的 父级元素 没有定位 , 那么会 一直向上查找有定位的父级元素 , 直到浏览器 ; 绝对定位 元素…

【Linux】组管理和权限管理

目录1 Linux组的基本介绍2 文件/目录所有者2.1 查看文件的所有者2.2 修改文件所有者3 组的创建3.1 基本指令3.2 应用实例4 文件/目录 所在组4.1 查看文件/目录所在组4.2修改文件/目录所在的组5 其他组6 改变用户所在组6.1 改变用户所在的组6.2 应用实例7 权限介绍8 rwx权限详解…

多线程(八):常见锁策略

目录 前言 1. 乐观锁 VS 悲观锁 乐观锁 悲观锁 2. 轻量级锁 VS 重量级锁 轻量级锁 3. 自旋锁 VS 挂起等待锁 自旋锁 挂起等待锁 4. 读写锁 VS 互斥锁 5. 可重入锁 vs 不可重入锁 死锁 发生死锁的情况 死锁产生的四个必要条件如下&#xff1a; 6. 公平锁和非公平锁…

Hibernate多表关联——(一对多关系)

Hibernate多表关联——&#xff08;一对多关系&#xff09; 文章目录Hibernate多表关联——&#xff08;一对多关系&#xff09;1.分别在类中添加属性&#xff1a;2.hibernate建表3.使用测试类在表中添加数据hibernate是连接数据库使得更容易操作数据库数据的一个框架&#xff…

ERROR:org.apache.hadoop.hbase.PleaseHoldException: Master is initializing错误

一、问题 重新安装hbase后&#xff0c;在hbase shell中查看所有命名空间时&#xff0c;出现了ERROR:org.apache.hadoop.hbase.PleaseHoldException: Master is initializing错误。 二、方法 1、root用户下&#xff0c;关闭hbase stop-hbase.sh 2、执行以下命令删除HDFS下的hb…

go进阶篇gin框架系列三

一、模板引擎的语法 {{.}} 模板语法都包含在{{和}}中间&#xff0c;其中{{.}}中的点表示当前对象。 当我们传入一个结构体对象时&#xff0c;我们可以根据.来访问结构体的对应字段。 pipeline pipeline是指产生数据的操作。比如{{.}}、{{.Name}}等。Go的模板语法中支持使用管道…

JavaWeb开发 —— Element组件

目录 一、什么是Element&#xff1f; 二、快速入门 三、常见组件 一、什么是Element&#xff1f; ① Element&#xff1a;是饿了么团队研发的&#xff0c;一套为开发者、设计时和产品经理准备的基于Vue 2.0 的桌面端组件库。 ② 组件&#xff1a;组成网页的部件&#xff0…

FIFO的工作原理及其设计

1.简介 FIFO( First Input First Output)简单说就是指先进先出。FIFO存储器是一个先入先出的双口缓冲器&#xff0c;即第一个进入其内的数据第一个被移出&#xff0c;其中一个口是存储器的输入口&#xff0c;另一个口是存储器的输出口。 对于单片FIFO来说&#xff0c;主要有两种…

文字转语音软件的优缺点及如何选择最适合的工具

随着科技的进步&#xff0c;文字转语音技术已经越来越成熟&#xff0c;越来越多的人开始使用文字转语音软件来转换文本为语音。这种技术可以帮助人们在许多方面&#xff0c;例如改善阅读体验、方便学习、提高生产效率等。然而&#xff0c;文字转语音软件有其优缺点&#xff0c;…

一文读懂推荐系统用户画像

1.推荐系统用户画像 用户画像这个词具有广泛性。 它被应用于推荐&#xff0c;广告&#xff0c;搜索&#xff0c;个性化营销等各个领域。任何时候&#xff0c;不管出于什么目的&#xff0c;我们想描述我们的用户是谁的时候&#xff0c;大家都会用到用户画像这个词。 比如&…

VUE_学习笔记

一、 xx 二、模板语法 1.模板语法之差值语法 &#xff1a;{{ }} 主要研究&#xff1a;{{ 这里可以写什么}} 在data中声明的变量、函数等都可以。常量只要是合法的javascript表达式&#xff0c;都可以。模板表达式都被放在沙盒中&#xff0c;只能访问全局变量的一个白名单&a…

精准关键词获取-行业搜索词分析

SEO关键词的收集通常可以通过以下几种方法&#xff1a; 根据市场价值、搜索词竞争性和企业实际产品特征进行筛选&#xff1a;确定您的关键词列表之前&#xff0c;建议先进行市场分析&#xff0c;了解您的竞争对手、行业状况和目标受众等信息&#xff0c;以更好的了解所需的特定…