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…...

uniapp生成随机数
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...

使用jenkins将airflow-dbt部署到服务器上
系列文章目录 文章目录 系列文章目录课程地址YT一、jenkins服务器的初始化配置1.1 运行第一个jenkins pipeline二、编写本地dbt项目2.1 克隆git上的初始文件到本地2.2 本地创建虚拟环境2.3 创建airflow的Dockerfile2.4 安装dbt2.5 创建dbt所需要的snowflake数据库2.6 配置docke…...

初学java练习题【1】
import java.util.Scanner;public class HelloWorld{public static void main(String[] args){Scanner scannernew Scanner(System.in);//输入工资System.out.println("请输入您的工资:");double d1scanner.nextDouble();System.out.println("请输入…...

大模型应用探讨,免费AI写作、一键PPT、免费PDF百种应用、与AI对话
大模型应用平台知识普及, 应用可见评论区 我们生活在一个充满无限可能的数字时代,人工智能技术正在推动着各种创新的边界。大模型应用平台一般包含以下功能。 ## 1. 一键生成论文 写作是学生、研究人员和职场人士都无法避免的任务。大模型应用平台拥有强大的文本生…...

计算机视觉之OpenCV vs YOLO
好多开发者希望搞明白OpenCV 和YOLO区别,实际上,二者在计算机视觉领域都有广泛应用,但它们有很大的不同。 一、OpenCV 概述 OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它…...

【深度学习基础模型】胶囊网络(Capsule Networks, CapsNet)详细理解并附实现代码。
【深度学习基础模型】胶囊网络(Capsule Networks, CapsNet)详细理解并附实现代码。 【深度学习基础模型】胶囊网络(Capsule Networks, CapsNet)详细理解并附实现代码。 文章目录 【深度学习基础模型】胶囊网络(Capsul…...

科普向 -- 什么是RPC
科普向 – 什么是RPC RPC,全称为远程过程调用(Remote Procedure Call),是一种计算机通信协议,允许程序在不同的地址空间(通常是不同的计算机)上执行代码。RPC使得程序可以像调用本地函数一样调…...

SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz(基础)
SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz(基础) 简介适用场景Quartz核心概念Quartz 存储方式Quartz 版本类型引入相关依赖开始集成方式一:内存方式(MEMORY)存储实现定时任务1. 定义任务类2. 定…...

【现代控制理论】第2-5章课后题刷题笔记
文章目录 第二章:线性控制系统的状态空间描述第三章:控制系统状态空间描述的特性3.1 计算状态转移矩阵(矩阵指数函数)3.2 计算系统的时间响应(状态方程的解)3.3 判断系统的能控性和能观性,以及能…...

(四)Proteus仿真STM32单片机使用定时器控制LED
(四)Protues仿真STM32单片机使用定时器控制LED – ARMFUN 定时器在单片机中具有重要的作用,它可以提供精确的时间控制和事件触发功能。相比之下,使用延时函数(delay function)来实现时间控制存在以下一些坏…...