CUDA C++ 编程指引:编译 | CUDA
本系列参考自 CUDA C++ Programming Guide。
离线编译
nvcc 可编译同时包含主机代码和设备代码:
- 分离主机和设备代码;
- 将设备代码编译成汇编形式(PTX 代码),再编译为二进制形式(cubin);
- 将
<<<...>>>
中的代码转化为 CUDA 运行库中的函数调用; - nvcc 会借助其他编译器将主机端代码编译出来;
- 主机端代码和设备端代码被编译好后,nvcc 会将两段代码链接起来。
在线编译
如果在编译过程中,不将设备端代码编译为 cubin 文件,而是停在 PTX 代码上。设备驱动会负责在运行时,使用 PTX 代码生成二进制代码。这个过程被称作在线编译(JIT Compilation, Just-In-Time Compilation)。
当设备驱动为即时编译 PTX 代码时,它自动缓存生成的二进制文件副本,以避免在以后调用应用时重复编译。当设备驱动升级后该缓存(称为计算缓存)自动失效,所以应用能够从设备驱动内置的新的即时编译器获益。
兼容性
二进制兼容性
生成 cubin 对象时,使用编译器选项 -code
指定目标架构:例如用 -code=sm_35
编译时,为计算能力为 3.5 的设备生成二进制代码。
首先,不同架构上的二进制代码不能互相兼容。其次,二进制兼容性保证向后兼容,但不保证向前兼容,也不保证跨越主修订号的向后兼容。具体来说,为计算能力为 x.y 生成的 cubin 对象只能保证在计算能力为 x.z 的设备上执行,这里 z>=y。
注意,上述二进制代码的兼容性原则只限于桌面款显卡。
PTX 兼容性
PTX 代码的兼容性远强于二进制代码。只要不涉及到不同架构上的特性差异,PTX 可以在任何架构上运行。不过 PTX 代码在两种情况下其兼容性会受限:
- 若 PTX 代码使用了较高级别架构的特有特性,则无法在较低架构上运行。
- 若 PTX 在较低架构上生成,则虽然能够在所有更高级别的架构上运行,但无法充分利用这些架构的硬件特性,造成性能无法最大化的问题。
在编译时,可以通过 -arch
来指定生成的 PTX 代码的版本,如 -arch=compute_30
,然后使用 -code
进行 PTX 代码的生成,该选项需要与 -arch
的选项兼容,如 -arch=sm_30
。
这里注意 -arch
不表示生成 PTX 代码,只是代表虚拟架构,具体生成代码还需要 -code
的指定。
应用程序兼容性
为了保证应用程序的兼容性,最好是将代码编译成 PTX 代码,然后依靠各个计算能力的驱动程序在线编译成对应平台的二进制代码 cubin。
-gencode
选项可以同时生成多种架构的代码:
1 | nvcc x.cu |
使用上述编译指令后,会生成 3.5/5.0/6.0 的 cubin 文件,以及 6.0 的 PTX 代码。对于主机端代码,会自动编译,并在运行时决定调用哪一个版本的执行。
另外,在程序中可以使用 __CUDA_ARCH__
宏来指定计算能力(只能用于修饰设备端代码)。计算能力 3.5 在程序中对应的 __CUDA_ARCH__
为 350。
驱动兼容性
在开发 CUDA 应用时,开发人员应该关注两种版本号:计算能力描述了基本规范和计算设备特性,CUDA 驱动 API 版本描述了驱动 API 和运行时 API 支持的特性。
驱动 API 的版本在驱动头文件中定义为 CUDA VERSION
。开发人员可用其检查其应用是否要比当前版本更新的驱动。这非常重要,因为驱动 API 是向后兼容的,这意味着特定版本编译的应用、插件和库(包括运行时库)能够在以后发布的驱动上工作。但是驱动 API 不是向前兼容的,这意味着特定版本编译的应用、插件和库(包括运行时库)不能够在以前发布的驱动上工作。
注意:
- 由于一个系统只能装一个版本的驱动。因此驱动版本要足够高(至少程序所需的版本),否则程序跑不起来。
- 默认情况下,nvcc 编译程序时,库和插件是静态编译的。静态编译不要求库和插件的驱动版本和 CUDA 运行库保持一致。但是动态链接则要求版本一致。
参考
CUDA C++ Programming Guide
《CUDA C Programming Guide》(《CUDA C 编程指南》)导读
《CUDA 并行程序设计:GPU 编程指南》
CUDA C++ 编程指引:编译 | CUDA