本文是参加2022 CUDA on Platform 线上训练营学习笔记,感谢NVIDIA各位老师的精彩讲解!
1. 异构计算
异构计算就是计算机进行计算时会将不同的内容放到不同的设备进行计算。大部分计算密集的任务放到GPU,虽然代码量可能较少,但是消耗的时间较长,而整体的逻辑控制放到CPU。定义在不同设备的变量也会做相应的标注,如后缀h,d等。
目前,异构计算在高性能计算方面已经成为主流,通常都会使用GPU解决计算密集型任务。
2. cuda安装
安装方法如上图所示。
安装好后,可以查看GPU的详细参数,命令如上图。
3. CUDA程序编写
GPU和CPU上运行的代码,即并行代码和串行代码是写在一起的,通常程序都会遵循以下步骤:
-
把输入数据从CPU内存复制到GPU显存:因为GPU无法访问常规的CPU内存,所以只能先在CPU准备好数据,再传输给GPU
-
在执行芯片上加载GPU程序并执行:GPU会将数据从显存读取到内核芯片进行计算。
-
将计算结果从GPU显存中复制到CPU内存中:GPU会将计算结果从芯片写回GPU显存,再传给CPU。
CUDA有一些自己的关键字,例如1)变量定义:需要指定在GPU上;2)线程的操控;3)同步:由于是异构计算,CPU不确定GPU何时完成计算,因此需要同步。
首先是定义函数在哪种设备执行的关键字1)__global__2)__device__3)__host__三种。三种关键字定义和区别如上图所示。这里的计算能力并不是性能多强,更多的是指架构。设备调用是指执行一个程序,内部调用其他函数模块,调用的函数必须在设备上。
上表是对几个关键字具体调用方法进行总结对比。
上图是核函数调用的一个示例。
4. CUDA程序编译
具体编译过程如上图所示:C++预处理器将程序分成两部分。黄色虚线框的右边是GPU编译,左边是正常的c编译。
- 右边首先将分离出来的代码用cicc编译生成ptx代码,.ptx是一个稳定的编程模型,或者说是一个指令集,它是一个虚拟架构的汇编产物,可以跨多种架构的多个GPU,优化编译,可以生成不同的GPU代码。因为GPU有很多代,各代之间有较大差异。为保证各代之间的兼容,需要一个虚拟架构,可以针对不同代的GPU适配性编译,再生成cubin文件。最后将不同版本不同架构的生成的文件合成在一起,得到fatbinary。
- 左边首先是在CPU正常的代码编译,然后通过cudafa++将刚才GPU编译的库加载进来,生成.o或.obj文件。
- 最后将所有内容编译链接得到可执行程序。
上述过程实际通过nvcc命令,并指定一些参数就可以得到可执行文件,具体如下面的流程图。
详细内容可以参考说明文档。
nvcc编译时需要注意设备架构,设备真实架构和设备虚拟架构,通常用gpu-architecture和gpu-code区分。真实架构就是实际代码所运行的设备,用gpu-code定义,虚拟架构就是上面说的ptx,用gpu-architecture定义,注意,gpu-architecture的版本一定要低于gpu-code。因为虚拟的版本低可以在一个高版本的真实架构运行,但是高版本虚拟架构无法在低版本真实设备上运行。下图是具体设定方法以及架构版本说明,具体可以查看链接
上图是具体执行编译的一个示例,先用nvcc编译函数模块生成.o,然后再用生成的.o文件和主函数生成可执行文件。
5. NVPROF
Kernel Timeline输出的是以gpu kernel为单位的一段时间的运行时间线,我们可以通过它观察GPU在什么时候有闲置或者利用不够充分的行为,更准确地定位优化问题,还可以对数据传输和一些API调用的时间线进行绘制。nvprof是nvidia提供的用于生成gpu timeline的工具,其为cuda toolkit的自带工具。使用的命令为nvprof -o out.nvvp a.exe
,可以结合nvvp或者nsight进行可视化分析。具体参考说明文档
nvprof有不同的模式
- gpu-trace模式:可以查看函数的资源占用情况,吞吐量等,便于程序性能分析。
- api-trace:可以查看api调用情况。不仅是自己程序的api调用,还有cuda内部的接口调用。