前言:零基础,之前跑过一点 python ,几乎没有搞过 C++ 。这次会利用 chatGPT 辅助阅读这本官方手册。学习 CUDA 编程。最终要达到啥效果呢,希望可以读懂 cudasift 的源码。
Chapter 1 使用GPU的好处
(3-4) cpu与gpu结构对比:gpu大部分晶体管都用于计算,而cpu会分心在缓存,控制等功能,以此来减少内存读取的时间花销。
Chapter 2 CUDA:通用的并行计算平台和编程模型
(5-6) cuda的应用:
- 库和中间件的:包括cudnn,tensorrt,cufft,cublas,curand,cusparse,cula之类的,还有一些没听过的
- 语言,c++,python之类的;
- 还有现实工业场景应用的。
Chapter 3 可伸缩的编程模型
(7) cuda的三个关键的概念
- 线程块层级
- 共享内存
- 障碍同步
问题不断拆分,在线程块中同步运行
(8) 一张线程块的结构图,Streaming Multiprocessors(SMs)提供了自动伸缩性,多核更快
(9) 整本书的结构
(11) 核函数基础概念,threadIdx
,<<<线程块数量,每个线程块的线程数>>>
5.2 线程层级
(12-13) threadIdx
是一个三维向量,线程块索引的计算,一个线程块因为内存的限制最多1024个线程,一个线程块可以是一维,二维,三维的(解答:https://www.zhihu.com/question/63319070);在一个block内的线程可以通过共享内存和同步来统筹内存读取,通过在一个需要同步的点来调用__syncthreads()函数实现同步,这个函数就像一个障碍,所有block内的线程都要在这个障碍前停下来等待。
5.2.1 线程块簇
(14-15) 线程块簇
- 线程块簇的调度:线程块可以在流处理器(SMs)内调度,也可以GPU处理簇(GPC)内调度。
- 簇内线程块数:一个簇内最多就只能有8个线程块,可以通过cudaOccupancyMaxPotentialClusterSize这个api查一下最多支持多少个簇。
- 线程块簇的启动方式有2种:
- 第一种:使用编译时核属性来修饰核函数,
__cluster_dims__(X,Y,Z)
- 第二种:
cudaLaunchKernelEx
- 第一种:使用编译时核属性来修饰核函数,
个人疑问:线程块簇有什么作用?
ChatGPT回答:在CUDA架构中,线程块(Thread Block)是一组并发执行的线程的集合。线程块通常是将任务分配给GPU的最小单位,且在一个多处理器上运行。然而,有时候一个多处理器可能无法容纳全部线程块,导致一些线程块无法立即执行。这时,Thread Block Clusters就发挥作用了。
Thread Block Clusters是多个线程块的组合,它们被调度在多个多处理器上执行。当一个多处理器上的线程块执行完成后,Thread Block Clusters可以通过调度器被分配到这个多处理器上继续执行,从而充分利用多处理器的计算资源。
也就是说,线程块集群的目的是提高并行性和资源利用率,通过在多个多处理器上同时执行线程块,提高CUDA程序的整体性能。
5.3 内存层级
(16) 线程与内存关系:
- 线程:每个线程都有私有的局部内存
- 线程块:每个线程块都一个块共享内存,对块内所有线程可见。
- 线程块簇:组成线程块簇的线程块,此时线程块内的共享内存变成了分布式的共享内存,对簇内的所有线程块可见。
- 额外的只读内存,内部的数据有更快的存取速度:
- 常量存储(constant memory):存储常量,使用
__constant__
关键字将数据声明为常量, - 纹理存储(texture memory):存储图像或者二维数据,使用
texture
关键字声明为纹理
- 常量存储(constant memory):存储常量,使用
5.4 异构编程
(17) 异构编程的概念,gpu内存的allocation和deallocation,cpu与gpu之间数据传输。
统一内存(unified memory):gpu和cpu公用一块内存地址空间,不需要分配拷贝之类的操作,host和device之间数据传输和访问更方便。
5.5 异步SIMT编程模型
(17) cuda编程模型提供异步编程模型来加速内存操作,包含:
- 线程之间的异步障碍
- 异步数据拷贝到GPU
5.5.1 异步操作
(17) 异步操作使用一个同步对象来同步操作的完成,怎么理解这句话呢,就是异步操作之后可能对执行别的操作,所以也需要在一个同步点停下来,确保所有的异步操作都完成了,才能进行下一步。
ChatGPT:同步对象可以是显式的同步原语,例如栅栏(barrier)或信号量(semaphore),也可以是由库提供的特定功能的对象,例如CUDA的cuda::barrier或cuda::pipeline。
线程范围(thread scope):定义了同步对象可以同步异步操作的线程集合。
(18) 异构编程示意图
(19) 线程范围和定义
5.6 算力(compute capability)
(19) 也叫 SM版本。一般表示为 X.Y
,X
一样表示核心架构一样,Y
只是表示新特性的更新。
6 编程接口
- C++拓展:定义自己的核函数,分配grid和block
- runtime库:
- 在host运行的分配和解除分配device的内存的函数
- 在host和device之间传输数据的函数
- 管理多device的系统
runtime库是基于底层的
- C接口
- CUDA驱动接口:这个接口可以控制更为底层的概念,CUDA context(上下文,类似于cpu的进程),CUDA modules(模块,类似于cpu的动态加载库)
6.1 使用NVCC来进行编译
6.1.1 编译流程
离线编译
使用nvcc来编译,可以编译host和device的混合代码。但是两种代码在nvcc编译的时候会分开进行编译:
- 将device的代码编译成汇编形式的PTX或者二进制码