CUDA 中对能够定义的网格大小和线程块大小做了限制。对任何从开普勒到安培架构 的 GPU 来说
网格大小(gridDim):
线程块大小(blockDim):
另外,还要求线程块总的大小,即 blockDim.x、blockDim.y 和 blockDim.z 的乘积不能大于1024。也就是说,不管如何定义,一个线程块最多只能有1024个线程。这些限制是必须牢记的。如果超过1024个线程核函数是不起作用的。
在将源代码编译为PTX代码时,需要用选项-arch=compute_XY指定—个虚拟架构的计算能力,用以确定代码中能够使用的CUDA功能。在将PTX代码编译为cubin代码时,需要用选项-code=sm_ZW指定一个真实架构的计算能力,用以确定可执行文件能够使用的GPU。真实架构的计算能力必须等于或者大于虚拟架构的计算能力。
用选项
-gencode arch=compute_35,code=sm35
-gencode arch=compute_50,code=sm50
-gencode arch=compute_60,code=sm60
-gencode arch=compute_70,code=sm70
编译出来的可执行文件将包含4个二进制版本,分别对应开普勒架构(不包含比较老的3.0和3.2的计算能力)、麦克斯韦架构、帕斯卡架构和伏特架构。这样的可执行文件称为胖二进制文件(fatbinary)。在不同架构的GPU中运行时会自动选择对应的二进制版本。需要注意的是,上述编译选项假定所使用的CUDA版本支持7.0的计算能力,也就是说,至少是CUDA9.0。如果在编译选项中指定了不被支持的计算能力,编译器会报错。另外,需要注意的是,过多地指定计算能力,会增加编译时间和可执行文件的大小。
nvcc有一种称为即时编译(just-in_time compilation)的机制,可以在运行可执行文件时从其中保留的PTX代码临时编译出一个cubin目标代码。要在可执行 文件中保留(或者说嵌入)一个这样的PTX代码。就必须用如下方式指定所保留PTX代码的虚拟架构:
-gencode arch=compute_XY,code=compute_XY
这里的两个计算能力都是虚拟架构的计算能力,必须完全一致。
例如,假如我们处于只 有 CUDA 8.0 的年代(不支持伏特架构),但希望编译出的二进制版本适用于尽可能多 的 GPU,则可以用如下的编译选项:
-gencode arch=compute_35,code=sm_35
-gencode arch=compute_50,code=sm_50
-gencode arch=compute_60,code=sm_60
-gencode arch=compute_60,code=compute_60
其中,前三行的选项分别对应 3 个真实架构的 cubin 目标代码,第四行的选项对应保留 的 PTX 代码。这样编译出来的可执行文件可以直接在伏特架构的 GPU 中运行,只不过 不一定能充分利用伏特架构的硬件功能。在伏特架构的 GPU 中运行时,会根据虚拟架构 为 6.0 的 PTX 代码即时地编译出一个适用于当前 GPU 的目标代码。
在学习 CUDA 编程时,有一个简化的编译选项可以使用:
-arch=sm_XY
它等价于
-gencode arch=compute_XY,code=sm_XY
-gencode arch=compute_XY,code=compute_X