CUDA Graphs学习与实验
CUDA Graphs学习与实验
- 一.参考链接
- 二.测试方案
- 三.测试代码
CUDA图(CUDA Graphs)为CUDA引入了一种全新的工作提交模型。它允许将一系列操作(如内核启动)以图的形式表示,并通过依赖关系将这些操作连接起来。这种图的定义过程与其执行过程是分开的,这意味着我们可以提前定义好一个图,然后多次重复执行它。
这种定义与执行的分离带来了多方面的优化:
- 降低CPU启动开销:相比传统的流(streams)方式,由于大量的设置工作已经在图的定义和实例化阶段完成,实际执行时的CPU开销明显减少。
- 全局优化机会:通过将整个工作流程以图的形式呈现给CUDA,CUDA有机会对整个流程进行优化。这在逐步提交工作的流机制中是无法实现的,因为流机制只能看到局部的、片段式的工作提交。
流机制中的问题:
在传统的流中,当你向流中放置一个内核时,主机驱动程序需要执行一系列操作来准备在GPU上执行该内核。这些操作包括设置内核参数、配置执行环境等。对于执行时间较短的GPU内核,这些准备工作的开销可能占到总执行时间的很大一部分,从而降低了整体效率。
CUDA图的工作提交分为三个阶段:
-
定义(Definition):
在这个阶段,程序创建一个包含操作及其依赖关系的图。开发者描述需要执行的操作(如内核函数)以及这些操作之间的先后顺序或并行关系。
-
实例化(Instantiation):
在定义完成后,CUDA对图进行实例化。实例化过程包括:
- 快照:对图模板进行捕获,生成一个具体的可执行图结构。
- 验证:检查图的正确性,确保所有的操作和依赖关系都是有效的。
- 预处理:执行大部分的设置和初始化工作,目的是尽可能减少在实际执行时需要完成的工作量。
实例化的结果是一个可执行图(executable graph)。
-
执行(Execution):
已实例化的可执行图可以像普通的CUDA工作一样被提交到流中执行。重要的是,这个可执行图可以被多次执行,而无需每次都重新实例化。这大大提高了执行的效率,特别是在需要重复执行相同操作的情况下。
CUDA图的优势:
- 性能提升:通过减少CPU的启动开销,特别是在需要频繁启动小型内核的情况下,CUDA图能够显著提升性能。
- 优化执行:由于CUDA能够提前知道整个工作流程,它可以进行全局优化。例如,它可以重新排列操作以提高并行性,或者优化内存传输以减少延迟。
- 简化编程模型:开发者可以以更直观的方式描述计算任务,而无需手动管理复杂的依赖关系和同步机制。
举例说明:
假设我们有一系列需要按特定顺序执行的内核操作。在传统的流机制中,我们需要:
- 为每个内核启动,都要进行一次完整的设置和启动过程。
- 手动管理这些内核之间的依赖关系,确保它们按正确的顺序执行。
使用CUDA图后,我们可以:
- 一次性地定义所有的内核操作和它们的依赖关系。
- 实例化后,CUDA会处理好所有的设置和依赖关系。
- 执行时,只需简单地启动可执行图即可。
结论:
CUDA图为GPU计算提供了更高效、更灵活的工作提交方式。通过预先定义和实例化计算图,CUDA能够减少CPU的开销,并利用全局信息对执行进行优化。这对于需要高性能计算的应用,尤其是包含大量小型、短时内核的应用,具有重要意义
一.参考链接
- graph management functions of the low-level CUDA driver api
- CUDA Runtime Graph API
二.测试方案
三.测试代码
tee cuda_graph.cu<<-'EOF'
#include <iostream>
#include <cuda_runtime.h>
#include <iostream>
#include <vector>
#include <stdio.h>
#include <assert.h>
#include <cstdio>
#include <cuda.h>
#include <iostream>
#include <chrono>
#include <thread>#define CHECK_CUDA(call) \do { \cudaError_t err = call; \if (err != cudaSuccess) { \std::cerr << "CUDA error at " << __FILE__ << ":" << __LINE__; \std::cerr << " code=" << err << " (" << cudaGetErrorString(err) << ")" << std::endl; \exit(EXIT_FAILURE); \} \} while (0)#define CHECK_CUDA_DRV_API(call) \do { \CUresult err = call; \if (err != CUDA_SUCCESS) { \char *error_str=new char[1024]; \cuGetErrorString(err,(const char**)&error_str); \printf("[%s:%d] %s Error :%s!\n",__FILE__,__LINE__,#call,error_str); \} \} while (0)__global__ void Kernel1(float *a,float *b,float *c,float *d)
{unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;a[tid]=1;b[tid]=2;c[tid]=3;d[tid]=0;if(tid==0){printf("Kernel1\n");}
}__global__ void Kernel2(float *a,float *b,float *c,float *d)
{unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;a[tid]+=1;if(tid==0){printf("Kernel2\n");}
}__global__ void Kernel3(float *a,float *b,float *c,float *d)
{unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;b[tid]+=2;if(tid==0){printf("Kernel3\n");}
}__global__ void Kernel4(float *a,float *b,float *c,float *d)
{unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;c[tid]+=3;if(tid==0){printf("Kernel4\n");}
}__global__ void Kernel5(float *a,float *b,float *c,float *d)
{unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;d[tid]=a[tid]+b[tid]+c[tid];if(tid==0){printf("Kernel5\n");}
}struct HostFuncParam
{float *a;float *b;float *c;float *d;int thread_size;
};void CUDART_CB HostFunc(void *data){HostFuncParam *pstParam=(HostFuncParam*)data;for(int i=0;i<pstParam->thread_size;i++){pstParam->d[i]+=1;}printf("HostFunc\n");
}int run(bool graph_mode)
{int deviceid=0; int block_count=1;int block_size=8;int thread_size=block_count*block_size;int total_count=thread_size*sizeof(float);cudaStream_t stream[3];cudaEvent_t event[3];CHECK_CUDA(cudaSetDevice(deviceid)); for(int i=0;i<3;i++){CHECK_CUDA(cudaStreamCreate(&stream[i]));CHECK_CUDA(cudaEventCreate(&event[i]));}float *a,*b,*c,*d;CHECK_CUDA(cudaMallocManaged(&a, total_count));CHECK_CUDA(cudaMallocManaged(&b, total_count));CHECK_CUDA(cudaMallocManaged(&c, total_count));CHECK_CUDA(cudaMallocManaged(&d, total_count));cudaGraph_t graph;if(graph_mode){CHECK_CUDA_DRV_API(cuGraphCreate(&graph, 0));CHECK_CUDA(cudaStreamBeginCapture(stream[0],cudaStreamCaptureModeGlobal));} Kernel1<<<block_count, block_size,0,stream[0]>>>(a,b,c,d);CHECK_CUDA(cudaEventRecord(event[0], stream[0]));CHECK_CUDA(cudaStreamWaitEvent(stream[1], event[0]));CHECK_CUDA(cudaStreamWaitEvent(stream[2], event[0]));Kernel2<<<block_count, block_size,0,stream[0]>>>(a,b,c,d);Kernel3<<<block_count, block_size,0,stream[1]>>>(a,b,c,d);CHECK_CUDA(cudaEventRecord(event[1], stream[1]));Kernel4<<<block_count, block_size,0,stream[2]>>>(a,b,c,d);CHECK_CUDA(cudaEventRecord(event[2], stream[2]));CHECK_CUDA(cudaStreamWaitEvent(stream[0], event[1]));CHECK_CUDA(cudaStreamWaitEvent(stream[0], event[2]));Kernel5<<<block_count, block_size,0,stream[0]>>>(a,b,c,d);HostFuncParam stParam;stParam.d=d;stParam.thread_size=thread_size;CHECK_CUDA(cudaLaunchHostFunc(stream[0], HostFunc, (void*)&stParam));if(graph_mode){CHECK_CUDA(cudaStreamEndCapture(stream[0], &graph));cudaGraphExec_t graphExec;CHECK_CUDA(cudaGraphInstantiate(&graphExec, graph, NULL, NULL, 0));CHECK_CUDA(cudaGraphLaunch(graphExec, 0));CHECK_CUDA(cudaDeviceSynchronize());CHECK_CUDA_DRV_API(cuGraphDebugDotPrint(graph,"graph.dot",0));CHECK_CUDA(cudaGraphExecDestroy(graphExec));CHECK_CUDA(cudaGraphDestroy(graph));}else{CHECK_CUDA(cudaStreamSynchronize(stream[0]));}for(int i=0;i<thread_size;i++){printf("%6.2f\n",d[i]);}CHECK_CUDA(cudaFree(a));CHECK_CUDA(cudaFree(b));CHECK_CUDA(cudaFree(c));CHECK_CUDA(cudaFree(d));return 0;
}int main(int argc,char *argv[])
{int mode=atoi(argv[1]);if(mode==0){printf("normal mode\n");run(0);}else{printf("graph mode\n");run(1);}
}
EOF
/usr/local/cuda/bin/nvcc -std=c++17 -arch=sm_86 -lineinfo \-o cuda_graph cuda_graph.cu \-I /usr/local/cuda/include -L /usr/local/cuda/lib64 -lcuda
./cuda_graph 0
./cuda_graph 1
dot -Tpng graph.dot -o graph.png
- 输出
normal mode
Kernel1
Kernel2
Kernel3
Kernel4
Kernel5
HostFunc13.0013.0013.0013.0013.0013.0013.0013.00
graph mode
Kernel1
Kernel2
Kernel3
Kernel4
Kernel5
HostFunc13.0013.0013.0013.0013.0013.0013.0013.00
相关文章:

CUDA Graphs学习与实验
CUDA Graphs学习与实验 一.参考链接二.测试方案三.测试代码 CUDA图(CUDA Graphs)为CUDA引入了一种全新的工作提交模型。它允许将一系列操作(如内核启动)以图的形式表示,并通过依赖关系将这些操作连接起来。这种图的定义…...

【自注意力与Transformer架构在自然语言处理中的演变与应用】
背景介绍 在自然语言处理(NLP)领域,序列到序列(seq2seq)模型和Transformer架构的出现,极大地推动了机器翻译、文本生成和其他语言任务的进展。传统的seq2seq模型通常依赖于循环神经网络(RNN&…...

LabVIEW交直流接触器动态检测系统
LabVIEW软件与霍尔传感器技术结合的交直流接触器动态检测系统通过实时数据采集和处理技术,有效地测量并分析交直流接触器在吸合及吸持阶段的电流和电压变化,以及相应的功率消耗,从而优化电力和配电系统的性能和可靠性。 项目背景 交直流接触…...
Unity3D中基于四叉树的范围检测算法详解
在游戏开发中,碰撞检测和范围检测是常见的需求,尤其是在处理大量物体时,传统的暴力检测法(即每个物体与其他所有物体进行碰撞检测)会消耗大量的计算资源,导致性能下降。为了优化这一过程,四叉树…...

k8s网络通信
k8s通信整体架构 k8s通过CNI接口接入其他插件来实现网络通讯。目前比较流行的插件有flannel,calico等 CNI插件存放位置:# cat /etc/cni/net.d/10-flannel.conflist 插件使用的解决方案如下 虚拟网桥,虚拟网卡,多个容器共用一个虚…...

07 欢乐的跳
题目: 代码: #include<bits/stdc.h> using namespace std; #define M 1000005int main() {int n;cin>>n;int a[M]{0};for(int i0;i<n;i){cin>>a[i];}int c[M]{0};for(int i1;i<n;i){c[i]abs(a[i]a[i1]);}sort(c1,cn); // 注意f…...

【韩顺平Java笔记】第8章:面向对象编程(中级部分)【262-271】
文章目录 262. 回顾上一章内容263. IDEA介绍263.1 IDEA 介绍263.2 Eclipse 介绍 264. IDEA下载265. IDEA使用1257. IDEA使用2268. IDEA使用3268. 269. 270. IDEA快捷键1,2,3271. IDEA模板 262. 回顾上一章内容 看视频 263. IDEA介绍 263.1 IDEA 介绍 IDEA 全称 IntelliJ ID…...
GNU链接器(LD):输入分区的垃圾回收及保护处理(KEEP命令)介绍
0 参考资料 GNU-LD-v2.30-中文手册.pdf GNU linker.pdf1 前言 一个完整的编译工具链应该包含以下4个部分: (1)编译器 (2)汇编器 (3)链接器 (4)lib库 在GNU工具链中&…...

论文翻译 | Fairness-guided Few-shot Prompting for LargeLanguage Models
摘要 大型语言模型已经显示出令人惊讶的执行上下文学习的能力,也就是说,这些模型可以通过对由几个输入输出示例构建的提示进行条件反射,直接应用于解决大量下游任务。然而,先前的研究表明,由于训练示例、示例顺序和提示…...
【分布式微服务云原生】战胜Redis脑裂:深入解析与解决方案
战胜Redis脑裂:深入解析与解决方案 摘要: Redis脑裂问题(Split Brain Syndrome)是分布式系统中的一个常见且复杂的问题,通常发生在网络分区或主节点出现问题时。本文将详细探讨脑裂的主要原因、导致的问题以及有效的解…...

数据治理与可持续发展:开启企业价值新模式——The Open Group 2024生态系统架构·可持续发展年度大会邀您共襄盛举
在当今数字化转型的浪潮中,企业正面临着前所未有的机遇和挑战。当数据治理遇上可持续发展,企业价值的新模式应运而生。那么,如何在数字化时代实现数据治理与可持续发展的融合,推动企业价值的飞跃? The Open Group 202…...
数据库的分类及主流数据库
一、数据库的分类 (一)关系型数据库(RDBMS) 定义与原理 关系型数据库是基于关系模型建立的数据库。它以表格(关系)的形式组织数据,每个表格包含行(记录)和列࿰…...
Qt C++设计模式->备忘录模式
备忘录模式(Memento Pattern)是一种行为型设计模式,用于在不破坏封装性的前提下,捕获并保存对象的内部状态,以便在将来的某个时刻可以恢复到之前的状态。备忘录模式的核心是状态的保存和恢复,常用于实现撤销…...

Vue使用@别名替换后端ip地址
1. 安装 types/node types/node 包允许您在TypeScript项目中使用Node.js的核心模块和API,并提供了对它们的类型检查和智能提示的支持。 npm install types/node --save-dev 比如安装之后,就可以导入nodejs的 path模块,在下面代码 import path…...

强大的PDF到Word转换工具
Solid Converter:强大的PDF到Word转换工具推荐 在日常工作和学习中,PDF是最常用的文件格式之一。然而,编辑PDF文档并不总是那么方便,尤其是当你需要将PDF文件转换为Word文档时。Solid Converter 是一款强大的工具,专为…...
js进阶——深入解析JavaScript中的URLSearchParams
深入解析 JavaScript 中的 URLSearchParams 在现代Web开发中,我们经常需要处理URL中的查询参数,尤其是在构建动态Web应用时。这些查询参数(query parameters)通常以 ?keyvalue&key2value2 的形式存在。JavaScript 提供了一个…...

如何利用wsl-Ubuntu里conda用来给Windows的PyCharm开发
前提:咱们在wsl-Ubuntu上,有conda的虚拟环境 咱们直接打开PyCharm,打开Settings 更换Python Interpreter即可 当然一开始可能没有下面的选项,需要我们点击右边的Add Interpreter 这里选择wsl 点击next 将这两步进行修改 可以看出来࿰…...

操作系统的了解及安装
一、linux系统认识 linux是指操作系统的内核,ubuntu是指基于这种内核的操作系统,Ubuntu属于Linux的一个发行版本,有简易的用户界面,完善的包管理系统,Ubuntu还对大多数硬件有着良好的兼容性,包含最新的图形…...

【C++篇】虚境探微:多态的流动诗篇,解锁动态的艺术密码
文章目录 C 多态详解(进阶篇)前言第一章:多态的原理1.1 虚函数表的概念1.1.1 虚函数表的生成过程 1.2 虚表的存储位置 第二章:动态绑定与静态绑定2.1 静态绑定2.1.1 静态绑定的实现机制:2.1.2 示例代码: 2.…...
uniapp的相关知识(1)
1、hover-class:当有鼠标按下时,会切换对应的样式;也可以设置对应的变色时间。 2、selectable:设置text组件的文本是否可以进行复制。 3、with:当设置为80%时,表示宽占整个屏幕的80%。 4、border&#x…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
基于鸿蒙(HarmonyOS5)的打车小程序
1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...