第2章 编程模型
2.1 高度多线程协处理器
通过CUDA编程时,将GPU看作可以并行执行非常多个线程的计算设备(compute device)。它作为主CPU的协处理器或者主机(host)来运作:换句话说,在主机上运行的应用程序中数据并行的、计算密集的部分卸载到此设备上。
更准确地说,多次但在不同数据上独立执行的应用程序部分可以独立放到在此设备上作为许多不同线程执行的函数中。要达到这种效果,可以将这样一个函数编译到设备的指令集合中,并将得到的程序(叫做内核, kernel)下载到设备上。
主机和设备都保留自己的DRAM,分别称为主机内存(host memory)和设备内存(device memory)。用户可以通过优化的API调用将数据从一个DRAM复制到其他DRAM中,而优化的API调用使用了设备的高性能直接内存访问(DMA)引擎。
2.2 线程分批
执行内核的线程批组织为线程块的网格,如2.2.1和2.2.2所述,并参见图2-1。
2.2.1 线程块
线程块是可以一起协作的线程批次,它们通过一些快速的共享内存有效地共享数据,并同步其执行以协调内存访问。更准确地说,用户可以在内核中指定同步点,块中的线程在到达此同步点时挂起。
每个线程由线程ID(thread ID)标识,这是块中的线程号。为了帮助基于线程ID的复杂寻址,应用程序还可以将块指定为任意大小的二维或三维度组,并使用2个或3个组件索引来标识每个线程。对于大小(Dx,Dy)为的二维块,索引为(x,y)的线程的线程ID为(x+yDx),对于大小为(Dx,Dy,Dz)的三维块,索引为(x,y,z)的线程的线程ID为(x+yDx+zDxDy)。
2.2.2 线程块网格
块可以包含的最大线程数是有限制的。但是,执行相同内核的具有相同维度和大小的块可以分批组合到块网格中,以便可以在单个内核调用中启动的线程总数变得更大。这是以线程协作的降低为代价的,因为同一网格中不同线程块中的线程不能互相通信和同步。此模型允许内核有效运行,而不必在具有不同并行能力的各种设备上重新编译:如果设备具有非常少的并行能力,则可以顺序运行网格的所有块,如果具有很多并行能力,则可以并行运行网格的所有块,通常是二者组合使用。
每个块由其块ID标识,这是网格中的块号。为了帮助基于块ID的复杂寻址,应用程序还可以将网格指定为任意大小的二维度组,并使用2个组件索引来标识每个块。对于大小(Dx,Dy)为的二维块,索引为(x,y)的块的块ID为(x+yDx)。
主机执行一连串对设备的内核调用。每个内核作为组织为线程块网格的一批线程来执行。
图2-1. 线程分批
2.3 内存模型
在设备上执行的线程只能通过下列内存空间访问设备的DRAM和芯片内存储单元,如图2-2所示:
1、读写每线程寄存器,
2、读写每线程本地内存,
3、读写每块共享内存,
4、读写每网格全局内存,
5、只读每网格常量内存,
6、只读每网格纹理内存。
全局、常量和纹理内存空间可以通过主机读或写,并永久存在于相同应用程序的内核启动中。
全局、常量和纹理内存空间为不同的内存使用进行了优化(参见5.1.2.1、5.1.2.2和5.1.2.3)。纹理内存还为一些特定的数据格式提供不同的寻址模式以及数据筛选(参见4.3.4)。
线程可以通过不同范围的一组内存空间来访问设备的DRAM和芯片内存。
图2-2. 内存模型