NVIDIA CUDA on ARM Platform Summer Camp Note 4

Jul 8,2021   1800 words   7 min

Tags: GPU

Notes for CUDA on ARM Platform Summer Camp Day4.由于只记录了一些我认为比较重要的东西,笔记内容可能并非非常完整,完整内容可以直接参考课程视频,点击查看百度云,密码2qom。但我尽量保证了笔记内容的逻辑性和整体性,方便其他人阅读。

1.Jetson NANO存储单元调用

(1)统一内存

统一内存是指可以从任何处理器(CPU或GPU)访问的单个内存地址空间,通过cudaMallocManaged()调用,可以理解为是把内存和显存整合到了一起(并非是物理层面的统一)。当然,这并非是真的不会进行数据传输,而是不需要进行显式的传输(手动申请),由系统会自动完成。除非是像Tegra平台一样,内存和显存共享同一个物理存储,这样才是真正不需要传输,这点需要注意一下。具体从代码层面,有两种实现方法: 统一内存的优点如下: 一个统一内存的案例如下。

(2)Jetson NANO存储特点

前面在介绍统一内存的时候可以看到,它其实是不适合于Tegra这种嵌入式处理器的。但对于Tegra这种嵌入式处理器也有其独特之处。对于常规GPU和CPU而言,其数据传输通过PCIe进行,这也是制约性能的因素之一。而在Tegra上,显存内存合二为一,都在SoC里,避免了这种传输,在一定程度上提升了性能。 换句话说,对于Tegra这种嵌入式平台而言,其本身就可以做到GPU、CPU可以访问同一个内存地址。所以就没有必要再支持统一内存了,也是Jetson平台的重要特色。而将来,如果GPU和CPU都可以像Tegra一样共享存储单元,那么就非常方便了。

2.流与CUDA库

(1)CUDA流

流表示GPU的操作队列。一个流的不同操作有着严格的顺序,不同流之间是没有任何限制的。多个流可以同时启动多个内核,形成网格(Grid)级别的并行。CUDA中单流可以分为两大类:默认流和非默认流。如果我们什么都不做,其实系统就已经为我们创建了一个默认流(空流,NULL Stream)。我们一般可以使用这个流来创建和调度其它的流,而不是把它和其它流放在一起运行。另外需要说明的是,对于Jetson平台而言,目前是不支持多流的,只支持默认流。 流的相关API如下。 这里可以看到,流同样是在尖括号里设置的,也就是尖括号里的第四个参数。 利用流来加速程序的示意图如上图所示。简单来说就是尽可能减少等待时间,对大型数据进行拆分。让GPU数据传输的同时进行计算,让GPU尽可能busy起来。对于一般的程序,都是先将数据拷贝到Device,然后执行核函数,最后再将数据拷贝到Host。但如果数据量非常大,那么这里显然就有一个问题。在数据拷贝到时候,我们什么都做不了,只能等待。这显然是不够合理的。所以我们可以将一个大的数据拆分成多个小块。每一个小块拷贝好以后,就执行核函数。而在执行当前核函数的同时,再将下一小块的数据拷贝进来执行,以此类推。这样就可以尽可能减少了等待时间。 这里其实涉及到不同的调度方式:深度优先和宽度优先。所谓深度优先是指,尽可能保证同一个流中单所有操作都执行完了(如果将多个流并排画在一起,就是某一个流快执行完了,其它流还没动。这样看起来执行的很深,所以叫深度优先)才会去执行下一个流的操作。那么其实可以发现,这就是串行执行。而宽度优先是指尽可能同时执行多个相同的操作。如果也将多个流画在一起,就是每个流都执行了步骤1之后,才会一起再执行步骤2。这样看起来执行的很宽,但是不深,所以叫宽度优先。 另外,在使用多流时需要注意同步的问题。上面说了不同流之间是异步的,不会存在谁等谁的情况。所以对于一个多流的程序,需要进行流的同步。不然可能还有某一小块没执行完就返回结果了,需要注意一下。

可以看到,在之前,我们都是从核函数内部,考虑各种内存、指令的使用来实现对程序的优化。而流则是跳出核函数,从核函数外部实现进一步的加速。

(2)CUDA加速工具库

四个原生的CUDA加速库如下。 cuBLAS是基础线性代数相关的库,cuSPARSE是稀疏矩阵相关的库,cuFFT是快速傅立叶变换相关的库,cuRAND是随机数相关的库。cuBLAS简介如下。 cuBLAS相关API如下。 cuBLAS中一些重要的参数,如下。

本文作者原创,未经许可不得转载,谢谢配合

返回顶部