精简CUDA教程——CUDA Runtime API

精简CUDA教程——CUDA Runtime API

tensorRT从零起步迈向高性能工业级部署(就业导向) 课程笔记,讲师讲的不错,可以去看原视频支持下。

Runtime API 概述

环境

在这里插入图片描述

  • 图中可以看到,Runtime API 是基于 Driver API 之上开发的一套 API。
  • 之前提到过 Driver API 基本都是 cu 开头的,而Runtime API 基本都是以 cuda 开头的。

Runtime API 的特点

  • Runtime API 与 Driver API 最大的区别是懒加载 ,即在真正执行功能时才自动完成对应的动作,即:
    • 第一个 Runtime API 调用时,会自动进行 cuInit 初始化,避免 Driver API 未初始化的错误;
    • 第一个需要 context 的 API 调用时,会创建 context 并进行 context 关联,和设置当前 context,调用 cuDevicePrimaryCtxRetain 实现;
    • 绝大部分 api 都需要 context,例如查询当前显卡名称、参数、内存分配释放等
  • CUDA Runtime 是封装了 CUDA Driver 的更高级别、更友好的 API
  • Runtime API 使用 cuDevicePrimaryCtxRetain 为每个设备设置 context,不再手动管理 context,并且不提供直接管理 context 的 API(可 Driver API 管理,通常不需要)
  • 可以更友好地执行核函数,.cpp 可以与 .cu 文件无缝对接
  • Runtime API 对应 cuda_runtime.hlibcudart.so
  • Runtime API 随 cudatoolkit 发布
  • 主要知识点是核函数的使用、线程束布局、内存模型、流的使用
  • 主要是为了实现归约求和、放射变换、矩阵乘法、模型后处理,就可以解决绝大部分问题

错误处理

类似于在介绍 Driver API 时的情况,我们同样提出 Runtime API 的错误处理方式:

#define checkRuntime(op)  __check_cuda_runtime((op), #op, __FILE__, __LINE__)bool __check_cuda_runtime(cudaError_t code, const char* op, const char* file, int line){if(code != cudaSuccess){const char* err_name = cudaGetErrorName(code);const char* err_message = cudaGetErrorString(code);printf("runtime error %s:%d  %s failed. \n  code = %s, message = %s\n", file, line, op, err_name, err_message);return false;}return true;
}

内存模型 pinned memory

  • 内存模型是 CUDA 中很重要的知识点,主要理解 pinned_memory、global_memory、shared_memory 即可,其他的不太常用。
  • pinned_memory 属于 host memory,而 global_memory、shared_memory 属于 device memory。

下图是的 Device Memory 的分类

在这里插入图片描述

锁定性和性能

对于主机内存,即整个 host memory 而言,操作系统在逻辑上将其区分为两个大类:

  • pageable memory,可分页内存
  • page lock memory (pinned memory),页锁定内存/锁页内存

可以理解为 page lock memory 是酒店的 vip 房间,锁定给你一个人使用。而 pageable memory 是普通房间,在酒店房间不够的时候,选择性地将你的房间腾出来(交换到硬盘上)给其他人使用,这样就能容纳更多人了。造成房间很多的假象,代价是性能很低。pageable memory 就是常见的虚拟内存的特性。

基于前面的理解,我们总结如下:

  • 锁定性
    • pinned memory 具有锁定特性,是稳定不会被交换的,这很重要,相当于每次去这个房间都一定能找到你
    • pageable memory 没有锁定特性,对于第三方设备(如 GPU)去访问时,因为无法感知内存是否被交换,可能得到不到正确的数据,相当于每次去房间找你,说不定你的房间正好被交换了
    • 因此, GPU 可以直接访问 pinned memory 而不能访问 pageable memory
  • 性能
    • pageable memory 的性能比 pinned memory 差,因为我们的 pageable memory 很可能会被交换到硬盘上
    • pageable memory 策略能使用内存假象,比如实际只有 8G 内存却能使用 16G(借助 swap 交换),从而提高程序的运行数量
    • pinned memory 也不能太多,会导致操作系统整体性能变差(可同时运行的程序变少),而且 8G 内存最多就 8G 锁页内存。

数据传输到GPU

在这里插入图片描述

  • pinned memory 可以直接传送数据到 GPU

  • 而 pageable memory ,由于并不锁定,需要先传到 pinned memory。

关于内存其他几个点

  1. GPU 可以直接访问 pinned memory,称为 DMA (Direct Memort Access)

  2. 对于 GPU 访问而言,距离计算单元越近,效率越高,所以:

    SharedMemory > GlobalMemory > PinnedMemory

  3. 代码中,

    • new/malloc 分配的是 pageable memory
    • cudaMallocHost 分配的是 PinnedMemory
    • cudaMalloc 分配的是 GlobalMemory
  4. 尽量多用 PinnedMemory 储存 host 数据,或者显式处理 Host 到 Device 时,用 PinnedMemory 做缓存,都是提高性能的关键

流 stream

  • 流是一种基于 context 之上的任务管道(任务队列)抽象,一个 context 可以创建 n 个流
  • 流是异步控制的主要方式
  • nullptr 表示默认流,每个线程都有自己的默认流。

生活中的例子

同步(串行)异步
在这里插入图片描述
在这里插入图片描述
  • 在这个例子中,男朋友的微信消息,就是任务队列,流的一种抽象
  • 女朋友发出指令之后,她可以做任何事情,无需等待指令执行完毕。即异步操作中,执行的代码加入流的队列之后,立即返回,不耽误时间。
  • 女朋友发的指令被送到流中排队,男朋友根据流的队列,顺序执行
  • 女朋友选择性,在需要的时候等待所有的执行结果
  • 新建一个流,就是新建一个男朋友,给他发指令就是发微信,可以新建很多个男朋友
  • 通过 cudaEvent 可以选择性等待任务队列中的部分任务是否就绪

注意

要十分注意,指令发出后,流队列中储存的是指令参数,不能在任务加入队列后立即释放参数指针,这会导致流队列执行该指令时指针失效而出错。应当在十分肯定流已经不需要这个指针之后,才进行修改或释放,否则会有非预期行为出现。

就比如,女朋友让男朋友去卖西瓜并转给了他钱,但是却在男朋友买瓜成功前将转账撤了回去,这时就无法知道男朋友在水果店会发生什么,比如会不会跟老板打起来之类的。因此,要保证买瓜行为顺利完成(行为符合预期),在买瓜成功前就不能动买瓜的钱。

核函数

简介

  • 核函数是 cuda 编程的关键

  • 通过 xxx.cu 创建一个 cudac 程序文件,并把 cu 文件交给 nvcc 编译,才能识别 cuda 语法;

  • __xxx__ 修饰

    • __global__ 表示为核函数,由 host 调用;
    • __device__ 表示设备函数,由 device 调用;
    • __host__ 表示主机函数,由 host 调用;
    • __shared__ 表示变量为共享变量。
    • 可能存在上述多个关键字修饰同一个函数,如 __device____host__ 修饰的函数,既可以设备上调用,也可以在主机上调用
  • host 调用核函数:

    function<<<gridDim, blockDim, sharedMemorySize, stream>>>(args, ...)
    

    gridDimblockDim 的变量类型为 dim3,是一个三维的值;

    function 函数总共启动的线程数目可以这样计算:n_threads = gridDim.x * gridDim.y * gridDim.z * blockDim.x * blockDim.y * blockDim.z

    详细请参考线程束的相关知识

  • 只有 __global__ 修饰的函数才可以用 <<< >>> 的方式调用s

  • 调用核函数是传值的,不能传引用,可以传递类,结构体等,核函数可以使模板

  • 核函数的返回值必须是 void

  • 核函数的执行是异步的,也就是立即返回的

  • 线程 layout 主要用到 blockDim、gridDim

  • 和函数内访问线程索引主要用到 threadIdx、blockIdx、blockDim、gridDim 这些内置变量

线程索引计算

共涉及四个变量:blockDimgridDimthreadIdxblockIdx ,其中前两者可以认为是形状,后两者可以认为是对应的索引。就像我们 PyTorch 中如果一个张量的形状为 (2,3)(2,3)(2,3) ,那么对应的,其两个维度上索引的取值范围就是:0−1,0−20-1,0-201,02

在这里插入图片描述

线程索引 id 计算方法:左乘右加,如上图所示。

共享内存

  • __shared__ 关键字修饰

  • 共享内存因为更靠近计算单元,所以访问速度更快

  • 共享内存通常可以作为访问全局内存的缓存使用

  • 可以利用共享内存实现线程间的通信

  • 通常与 __syncthreads 同时出现,这个函数是同步 block 内的所有线程,全部执行到这一行才往下继续执行

    如:

    __shared__ int shared_value1;
    __shared__ int shared_value2;if (threadIdx.x == 0) {if (blockIdx.x == 0) {shared_value1 = 123;shared_value2 = 55;}else {shared_value1 = 331;shared_value2 = 8;}__syncthreads();printf("...")
    }
    

    其他 threadIdx.x 不为 0 的线程不会进到判断语句,但是会卡在 __syncthreads() ,等待 threadIdx.x 为 0 的线程设置好共享内存,再一起继续向下执行。

  • 共享内存使用方式:通常是在线程 id 为 0 的时候从 global memory 取值,然后 __syncthreads ,然后再使用

  • 动态共享内存与静态共享内存

    • 动态共享内存的声明需要加 extern 关键字,不需要指定数组大小,如:

      extern __shared__ char dynamic_shared_memory[];
      
    • 静态共享内存的声明需要指定数组大小,如:

      const size_t static_shared_memory_size = 6 * 1024; // 6KB
      __shared__ char static_shared_memory[static_shared_memory_size];
      

warp affine 实战

chapter: 1.6, caption: vector-add, description: 使用cuda核函数实现向量加法
chapter: 1.7, caption: shared-memory, description: 共享内存的操作
chapter: 1.8, caption: reduce-sum, description: 规约求和的实现,利用共享内存,高性能
chapter: 1.9, caption: atomic, description: 原子操作,实现动态数组的操作
chapter: 1.10, caption: warpaffine, description: 仿射变换双线性插值的实现,yolov5的预处理
chapter: 1.11, caption: cublas-gemm, description: 通用矩阵乘法的cuda核函数实现,以及cublasSgemm的调用
chapter: 1.12, caption: yolov5-postprocess, description: 使用cuda核函数实现yolov5的后处理案例

TODO

thrust

相当于 cuda 的 stl,但并不常用

错误处理

若核函数出错,由于它是异步的,立即执行 cudaPeekAtLastError 只会拿到对输入参数校验是否正确的状态,而不会拿到核函数是否正确执行的状态。

需要等待核函数真正执行完毕之后才知道当前核函数是否出错,一般通过设备同步或者流同步进行等待

错误分为可恢复和不可恢复两种

  • 可恢复
    • 参数配置错误,例如 block 越界(一般最大值是 1024),shared memory 超出大小范围(一般是 64KB)等
    • 通过 cudaGetlastError 可以获取错误代码,同时把当前状态恢复为success
    • 该种错误可以在调用核函数之后立即通过 cudaGetLastError / cudaPeekAtLastError 拿到
    • 该种错误在下一个函数调用时会覆盖
  • 不可恢复
    • 核函数执行错误,例如访问越界等
    • 该错误会传递到之后所有的 cuda 操作上
    • 错误状态通常需要等到核函数执行完毕才能够拿到,也就是有可能在后续的任何流程中突然异常(因为是异步的)

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

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

相关文章

TensorRT ONNX 基础

TensorRT ONNX 基础 tensorRT从零起步迈向高性能工业级部署&#xff08;就业导向&#xff09; 课程笔记&#xff0c;讲师讲的不错&#xff0c;可以去看原视频支持下。 概述 TensorRT 的核心在于对模型算子的优化&#xff08;合并算子、利用当前 GPU 特性选择特定的核函数等多种…

mmdetection tools工具梳理

mmdetection tools工具梳理 mmdetection 是一个非常好用的开源目标检测框架&#xff0c;我们可以用它方便地训练自己的目标检测模型&#xff0c;mmdetection 项目仓库提供许多实用的工具来实现帮助我们进行各种测试。本篇将梳理以下 mmdetection 项目仓库 tools 目录下的各种实…

TensorRT ONNX 基础(续)

TensorRT ONNX 基础&#xff08;续&#xff09; PyTorch正确导出ONNX 几条推荐的原则&#xff0c;可以减少潜在的错误&#xff1a; 对于任何使用到 shape、size 返回值的参数时&#xff0c;例如 tensor.view(tensor.size(0), -1) 这类操作&#xff0c;避免直接使用 tensor.s…

frp实现内网穿透极简教程

frp实现内网穿透极简教程 本文是内网穿透极简教程&#xff0c;为求简洁&#xff0c;我们不介绍为什么内网穿透也不介绍其原理&#xff0c;这里假设各位读者都已经明确的知道自己的目的&#xff0c;本文仅介绍如何安装配置 frp 实现内网穿透。 简单来说&#xff0c;内网穿透就…

图像预处理之warpaffine与双线性插值及其高性能实现

图像预处理之warpaffine与双线性插值及其高性能实现 视频讲解&#xff1a;https://www.bilibili.com/video/BV1ZU4y1A7EG 代码Repo&#xff1a;https://github.com/shouxieai/tensorRT_Pro 本文为视频讲解的个人笔记。 warpaffine矩阵变换 对于坐标点的变换&#xff0c;我们通…

sed 简明教程

sed 简明教程 转自&#xff1a;https://coolshell.cn/articles/9104.html awk于1977年出生&#xff0c;今年36岁本命年&#xff0c;sed比awk大2-3岁&#xff0c;awk就像林妹妹&#xff0c;sed就是宝玉哥哥了。所以 林妹妹跳了个Topless&#xff0c;他的哥哥sed坐不住了&#xf…

[深度][PyTorch] DDP系列第一篇:入门教程

[深度][PyTorch] DDP系列第一篇&#xff1a;入门教程 转自&#xff1a;[原创][深度][PyTorch] DDP系列第一篇&#xff1a;入门教程 概览 想要让你的PyTorch神经网络在多卡环境上跑得又快又好&#xff1f;那你definitely需要这一篇&#xff01; No one knows DDP better than I…

[深度][PyTorch] DDP系列第二篇:实现原理与源代码解析

[深度][PyTorch] DDP系列第二篇&#xff1a;实现原理与源代码解析 转自&#xff1a;https://zhuanlan.zhihu.com/p/187610959 概览 想要让你的PyTorch神经网络在多卡环境上跑得又快又好&#xff1f;那你definitely需要这一篇&#xff01; No one knows DDP better than I do! …

[深度][PyTorch] DDP系列第三篇:实战与技巧

[深度][PyTorch] DDP系列第三篇&#xff1a;实战与技巧 转自&#xff1a;https://zhuanlan.zhihu.com/p/250471767 零. 概览 想要让你的PyTorch神经网络在多卡环境上跑得又快又好&#xff1f;那你definitely需要这一篇&#xff01; No one knows DDP better than I do! – – …

机器学习:系统设计与实现 分布式训练

机器学习系统:设计与实现 分布式训练 转自&#xff1a;https://openmlsys.github.io/chapter_distributed_training/index.html 随着机器学习的进一步发展&#xff0c;科学家们设计出更大型&#xff0c;更多功能的机器学习模型&#xff08;例如说&#xff0c;GPT-3&#xff09;…

从零Makefile落地算法大项目,完整案例教程

从零Makefile落地算法大项目&#xff0c;完整案例教程 转自&#xff1a;从零Makefile落地算法大项目&#xff0c;完整案例教程 作者&#xff1a;手写AI 前言 在这里&#xff0c;你能学到基于Makefile的正式大项目的使用方式和考虑&#xff0c;相信我&#xff0c;其实可以很简单…

PyTorch扩展自定义PyThonC++(CUDA)算子的若干方法总结

PyTorch扩展自定义PyThon/C(CUDA)算子的若干方法总结 转自&#xff1a;https://zhuanlan.zhihu.com/p/158643792 作者&#xff1a;奔腾的黑猫 在做毕设的时候需要实现一个PyTorch原生代码中没有的并行算子&#xff0c;所以用到了这部分的知识&#xff0c;再不总结就要忘光了 &a…

给 Python 算法插上性能的翅膀——pybind11 落地实践

给 Python 算法插上性能的翅膀——pybind11 落地实践 转自&#xff1a;https://zhuanlan.zhihu.com/p/444805518 作者&#xff1a;jesonxiang&#xff08;向乾彪&#xff09;&#xff0c;腾讯 TEG 后台开发工程师 1. 背景 目前 AI 算法开发特别是训练基本都以 Python 为主&…

chrome自动提交文件_收集文档及提交名单统计

知乎文章若有排版问题请见谅&#xff0c;原文放在个人博客中【欢迎互踩&#xff01;】文叔叔文档收集使用动机在我们的学习工作中&#xff0c;少不了要让大家集体提交文件的情况&#xff0c;举个最简单的例子&#xff1a;收作业。 传统的文件收集流程大致是&#xff1a;群内发出…

惠普800g1支持什么内存_惠普黑白激光打印机哪种好 惠普黑白激光打印机推荐【图文详解】...

打印机的出现让我们在生活和日常工作中变得越来越方便&#xff0c;不过随着科技的发展&#xff0c;打印机的类型也变得非常多&#xff0c;其中就有黑白激光打印机&#xff0c;而黑白激光打印机的品牌也有很多&#xff0c;比如我们的惠普黑白激光打印机&#xff0c;今天小编就给…

控制台输出颜色控制

控制台输出颜色控制 转自&#xff1a;https://cloud.tencent.com/developer/article/1142372 前端时间&#xff0c;写了一篇 PHP 在 Console 模式下的进度显示 &#xff0c;正好最近的一个数据合并项目需要用到控制台颜色输出&#xff0c;所以就把相关的信息整理下&#xff0c;…

idea连接跳板机_跳板机服务(jumpserver)

一、跳板机服务作用介绍1、有效管理用户权限信息2、有效记录用户登录情况3、有效记录用户操作行为二、跳板机服务架构原理三、跳板机服务安装过程第一步&#xff1a;安装跳板机依赖软件yum -y install git python-pip mariadb-devel gcc automake autoconf python-devel readl…

【详细图解】再次理解im2col

【详细图解】再次理解im2col 转自&#xff1a;https://mp.weixin.qq.com/s/GPDYKQlIOq6Su0Ta9ipzig 一句话&#xff1a;im2col是将一个[C,H,W]矩阵变成一个[H,W]矩阵的一个方法&#xff0c;其原理是利用了行列式进行等价转换。 为什么要做im2col? 减少调用gemm的次数。 重要…

反思 大班 快乐的机器人_幼儿园大班教案《快乐的桌椅》含反思

大班教案《快乐的桌椅》含反思适用于大班的体育主题教学活动当中&#xff0c;让幼儿提高协调性和灵敏性&#xff0c;创新桌椅的玩法&#xff0c;正确爬的方法&#xff0c;学会匍匐前进&#xff0c;快来看看幼儿园大班《快乐的桌椅》含反思教案吧。幼儿园大班教案《快乐的桌椅》…

DCN可形变卷积实现1:Python实现

DCN可形变卷积实现1&#xff1a;Python实现 我们会先用纯 Python 实现一个 Pytorch 版本的 DCN &#xff0c;然后实现其 C/CUDA 版本。 本文主要关注 DCN 可形变卷积的代码实现&#xff0c;不会过多的介绍其思想&#xff0c;如有兴趣&#xff0c;请参考论文原文&#xff1a; …