CUDA 应用程序的图灵兼容性指南

构建用于 NVIDIA 图灵 GPU 的 CUDA 应用程序指南。

1. 图灵兼容性

1.1. 关于本文档

本应用说明,《CUDA 应用程序图灵兼容性指南》,旨在帮助开发人员确保他们的 NVIDIA® CUDA® 应用程序能在基于 NVIDIA® 图灵架构的 GPU 上运行。本文档为已经熟悉 CUDA C++ 编程并希望确保其软件应用程序与图灵兼容的开发人员提供指导。

1.2. 图灵上的应用程序兼容性

NVIDIA CUDA C++ 编译器 nvcc 可用于生成特定于架构的 cubin 文件和向前兼容的每个内核的 PTX 版本。每个 cubin 文件都针对特定的计算能力版本,并且仅与相同主版本号的 GPU 架构向前兼容。例如,针对计算能力 3.0 的 cubin 文件在所有计算能力 3.x (Kepler) 设备上都受支持,但在计算能力 5.x (Maxwell) 或 6.x (Pascal) 设备上不受支持。因此,为了确保与应用程序发布后推出的 GPU 架构的向前兼容性,建议所有应用程序都包含其内核的 PTX 版本。

注意

对于给定的架构,包含 cubin 和 PTX 代码的 CUDA 运行时应用程序将自动默认使用 cubin,严格将 PTX 路径用于向前兼容性目的。

已经包含内核 PTX 版本的应用程序应该可以在基于图灵的 GPU 上按原样工作。但是,仅通过 cubin 文件支持特定 GPU 架构的应用程序将需要更新以提供图灵兼容的 PTX 或 cubin。

1.3. Volta 和图灵之间的兼容性

图灵架构基于 Volta 的指令集架构 ISA 7.0,并使用新指令对其进行了扩展。因此,任何在 Volta 上运行的二进制文件都将能够在图灵上运行(向前兼容性),但图灵二进制文件将无法在 Volta 上运行。请注意,使用超过 64KB 共享内存(通过显式选择加入,请参阅《CUDA C++ 编程指南》)的 Volta 内核将无法在图灵上启动,因为它们将超出图灵的共享内存容量。

大多数为 Volta 编译的应用程序应该可以在图灵上高效运行,除非应用程序大量使用 Tensor Cores,或者重新编译可以利用新的图灵特定指令。Volta 的 Tensor Core 指令在图灵上只能达到峰值性能的一半。因此,建议显式为图灵重新编译。

1.4. 验证现有应用程序的图灵兼容性

第一步是检查图灵兼容的设备代码(至少是 PTX)是否已编译到应用程序中。以下部分展示了如何为使用不同 CUDA Toolkit 版本构建的应用程序完成此操作。

1.4.1. 使用 CUDA Toolkit 8.0 或更早版本的应用程序

使用 CUDA Toolkit 版本 2.1 到 8.0 构建的 CUDA 应用程序与图灵兼容,只要它们构建为包含其内核的 PTX 版本。要测试 PTX JIT 是否适用于您的应用程序,您可以执行以下操作

首次使用上述环境变量标志启动 CUDA 应用程序时,CUDA 驱动程序会将每个使用的 CUDA 内核的 PTX JIT 编译为本机 cubin 代码。

如果您设置了上面的环境变量,然后启动您的程序并且它运行正常,那么您已成功验证了图灵兼容性。

注意

完成测试后,请务必取消设置 CUDA_FORCE_PTX_JIT 环境变量。

1.4.2. 使用 CUDA Toolkit 9.x 的应用程序

使用 CUDA Toolkit 9.x 构建的 CUDA 应用程序与图灵兼容,只要它们构建为包含 Volta 原生 cubin 格式(参见 Volta 和图灵之间的兼容性)或 PTX 格式(参见 使用 CUDA Toolkit 8.0 或更早版本的应用程序)或两者的内核。

1.4.3. 使用 CUDA Toolkit 10.0 的应用程序

使用 CUDA Toolkit 10.0 构建的 CUDA 应用程序与图灵兼容,只要它们构建为包含 Volta 原生或图灵原生 cubin 格式(参见 Volta 和图灵之间的兼容性),或 PTX 格式(参见 使用 CUDA Toolkit 8.0 或更早版本的应用程序),或两者的内核。

1.5. 构建支持图灵的应用程序

当 CUDA 应用程序启动内核时,CUDA 运行时会确定系统中每个 GPU 的计算能力,并使用此信息自动找到可用的最佳匹配的内核 cubin 或 PTX 版本。如果支持目标 GPU 架构的 cubin 文件可用,则使用它;否则,CUDA 运行时将加载 PTX 并在启动之前将 PTX JIT 编译为 GPU 的原生 cubin 格式。如果两者都不可用,则内核启动将失败。

用于构建应用程序以获得图灵原生 cubin 或至少 PTX 支持的方法取决于所使用的 CUDA Toolkit 版本。

提供原生 cubin 的主要优点如下

  • 它可以为最终用户节省 JIT 编译仅作为 PTX 提供的内核的时间。编译到应用程序中的所有内核都必须在加载时具有本机二进制文件,否则它们将从 PTX 实时构建,包括来自链接到应用程序的所有库的内核,即使这些内核永远不会被应用程序启动。尤其是在使用大型库时,这种 JIT 编译可能需要大量时间。CUDA 驱动程序将缓存由于 PTX JIT 而生成的 cubin,因此这对于给定用户来说主要是一次性成本,但最好尽可能避免花费时间。

  • PTX JIT 编译的内核通常无法利用较新 GPU 的架构功能,这意味着原生编译的代码可能更快或具有更高的精度。

1.5.1. 使用 CUDA Toolkit 8.0 或更早版本的应用程序

CUDA Toolkit 8.0 或更早版本中包含的编译器生成 Maxwell 和 Pascal 等早期 NVIDIA 架构的原生 cubin 文件,但它们无法生成 Volta 或图灵架构的原生 cubin 文件。为了在使用 CUDA Toolkit 8.0 或更早版本时允许支持 Volta、图灵和未来的架构,编译器必须生成每个内核的 PTX 版本。

以下是可用于构建 mykernel.cu 以在 Maxwell 或 Pascal 设备上原生运行以及在图灵设备上通过 PTX JIT 运行的编译器设置。

注意

compute_XX 指的是 PTX 版本,而 sm_XX 指的是 cubin 版本。-gencode= 命令行选项的 arch= 子句指定前端编译目标,并且必须始终是 PTX 版本。code= 子句指定后端编译目标,可以是 cubin 或 PTX 或两者兼有。只有 code= 子句指定的后端目标版本将保留在生成的二进制文件中;至少必须有一个是 PTX 才能提供图灵兼容性。

Windows

nvcc.exe -ccbin "C:\vs2010\VC\bin"
  -Xcompiler "/EHsc /W3 /nologo /O2 /Zi /MT"
  -gencode=arch=compute_50,code=sm_50
  -gencode=arch=compute_52,code=sm_52
  -gencode=arch=compute_60,code=sm_60
  -gencode=arch=compute_61,code=sm_61
  -gencode=arch=compute_61,code=compute_61
  --compile -o "Release\mykernel.cu.obj" "mykernel.cu"

Mac/Linux

/usr/local/cuda/bin/nvcc
  -gencode=arch=compute_50,code=sm_50
  -gencode=arch=compute_52,code=sm_52
  -gencode=arch=compute_60,code=sm_60
  -gencode=arch=compute_61,code=sm_61
  -gencode=arch=compute_61,code=compute_61
  -O2 -o mykernel.o -c mykernel.cu

或者,您可能熟悉简化的 nvcc 命令行选项 -arch=sm_XX,它是以下更明确的 -gencode= 命令行选项的简写等效项,如上所述。-arch=sm_XX 扩展为以下内容

-gencode=arch=compute_XX,code=sm_XX
-gencode=arch=compute_XX,code=compute_XX

但是,虽然 -arch=sm_XX 命令行选项确实默认包含 PTX 后端目标,但它一次只能指定单个目标 cubin 架构,并且不可能在同一 nvcc 命令行上使用多个 -arch= 选项,这就是为什么上面的示例显式使用 -gencode=

1.5.2. 使用 CUDA Toolkit 9.x 的应用程序

对于 CUDA Toolkit 的 9.x 版本,nvcc 可以生成 Volta 架构(计算能力 7.0)的原生 cubin 文件。当使用 CUDA Toolkit 9.x 时,为了确保 nvcc 将为所有最新的 GPU 架构生成 cubin 文件以及 PTX 版本以实现与未来 GPU 架构的向前兼容性,请在 nvcc 命令行上指定适当的 -gencode= 参数,如下例所示。

Windows

nvcc.exe -ccbin "C:\vs2010\VC\bin"
  -Xcompiler "/EHsc /W3 /nologo /O2 /Zi /MT"
  -gencode=arch=compute_50,code=sm_50
  -gencode=arch=compute_52,code=sm_52
  -gencode=arch=compute_60,code=sm_60
  -gencode=arch=compute_61,code=sm_61
  -gencode=arch=compute_70,code=sm_70
  -gencode=arch=compute_70,code=compute_70
  --compile -o "Release\mykernel.cu.obj" "mykernel.cu"

Mac/Linux

/usr/local/cuda/bin/nvcc
  -gencode=arch=compute_50,code=sm_50
  -gencode=arch=compute_52,code=sm_52
  -gencode=arch=compute_60,code=sm_60
  -gencode=arch=compute_61,code=sm_61
  -gencode=arch=compute_70,code=sm_70
  -gencode=arch=compute_70,code=compute_70
  -O2 -o mykernel.o -c mykernel.cu

注意

compute_XX 指的是 PTX 版本,而 sm_XX 指的是 cubin 版本。-gencode= 命令行选项的 arch= 子句指定前端编译目标,并且必须始终是 PTX 版本。code= 子句指定后端编译目标,可以是 cubin 或 PTX 或两者兼有。只有 code= 子句指定的后端目标版本将保留在生成的二进制文件中;至少应该有一个是 PTX,以提供与未来架构的兼容性。

另请注意,CUDA 9.0 删除了对计算能力 2.x (Fermi) 设备的支持。任何 compute_2x 和 sm_2x 标志都需要从您的编译器命令中删除。

1.5.3. 使用 CUDA Toolkit 10.0 的应用程序

对于 CUDA Toolkit 的 10.0 版本,nvcc 可以生成图灵架构(计算能力 7.5)的原生 cubin 文件。当使用 CUDA Toolkit 10.0 时,为了确保 nvcc 将为所有最新的 GPU 架构生成 cubin 文件以及 PTX 版本以实现与未来 GPU 架构的向前兼容性,请在 nvcc 命令行上指定适当的 -gencode= 参数,如下例所示。

Windows

nvcc.exe -ccbin "C:\vs2010\VC\bin"
  -Xcompiler "/EHsc /W3 /nologo /O2 /Zi /MT"
  -gencode=arch=compute_50,code=sm_50
  -gencode=arch=compute_52,code=sm_52
  -gencode=arch=compute_60,code=sm_60
  -gencode=arch=compute_61,code=sm_61
  -gencode=arch=compute_70,code=sm_70
  -gencode=arch=compute_75,code=sm_75
  -gencode=arch=compute_75,code=compute_75
  --compile -o "Release\mykernel.cu.obj" "mykernel.cu"

Mac/Linux

/usr/local/cuda/bin/nvcc
  -gencode=arch=compute_50,code=sm_50
  -gencode=arch=compute_52,code=sm_52
  -gencode=arch=compute_60,code=sm_60
  -gencode=arch=compute_61,code=sm_61
  -gencode=arch=compute_70,code=sm_70
  -gencode=arch=compute_75,code=sm_75
  -gencode=arch=compute_75,code=compute_75
  -O2 -o mykernel.o -c mykernel.cu

注意

compute_XX 指的是 PTX 版本,而 sm_XX 指的是 cubin 版本。-gencode= 命令行选项的 arch= 子句指定前端编译目标,并且必须始终是 PTX 版本。code= 子句指定后端编译目标,可以是 cubin 或 PTX 或两者兼有。只有 code= 子句指定的后端目标版本将保留在生成的二进制文件中;至少应该有一个是 PTX,以提供与未来架构的兼容性。

1.5.4. 独立线程调度兼容性

Volta 和图灵架构在 warp 中的线程之间具有独立的线程调度。如果开发人员对 warp 同步性进行了假设,1 则此功能可能会改变与以前的架构相比参与执行代码的线程集。有关详细信息和纠正措施,请参阅《CUDA C++ 编程指南》中的“计算能力 7.0”。为了帮助迁移,Volta 和图灵开发人员可以使用以下编译器选项组合选择加入 Pascal 调度模型。

nvcc -arch=compute_60 -code=sm_70 ...

2. 修订历史

版本 1.0

  • 首次公开发布。

版本 1.1

  • 使用 CUDA C++ 而不是 CUDA C/C++

  • 更新了对《CUDA C++ 编程指南》和《CUDA C++ 最佳实践指南》的引用。

3. 通知

3.1. 通知

本文档仅供参考,不得视为对产品的特定功能、条件或质量的保证。NVIDIA Corporation(“NVIDIA”)对本文档中包含的信息的准确性或完整性不作任何明示或暗示的陈述或保证,并且对本文档中包含的任何错误不承担任何责任。NVIDIA 对因使用此类信息或因其使用而可能导致的侵犯第三方专利或其他权利的行为不承担任何责任。本文档不构成对开发、发布或交付任何材料(如下所定义)、代码或功能的承诺。

NVIDIA 保留随时对此文档进行更正、修改、增强、改进和任何其他更改的权利,恕不另行通知。

客户应在下订单前获取最新的相关信息,并应验证此类信息是否为最新且完整。

NVIDIA 产品根据订单确认时提供的 NVIDIA 标准销售条款和条件进行销售,除非 NVIDIA 和客户的授权代表签署的单独销售协议(“销售条款”)另有约定。NVIDIA 在此明确反对将任何客户通用条款和条件应用于购买本文档中引用的 NVIDIA 产品。本文档未直接或间接地形成任何合同义务。

NVIDIA 产品并非设计、授权或保证适用于医疗、军事、航空、航天或生命维持设备,也不适用于 NVIDIA 产品的故障或失灵可能合理预期会导致人身伤害、死亡或财产或环境损害的应用。NVIDIA 对在上述设备或应用中包含和/或使用 NVIDIA 产品不承担任何责任,因此,此类包含和/或使用由客户自行承担风险。

NVIDIA 不作任何陈述或保证,保证基于本文档的产品将适用于任何特定用途。NVIDIA 不一定会执行每个产品的所有参数的测试。客户全权负责评估和确定本文档中包含的任何信息的适用性,确保产品适用于并符合客户计划的应用,并为该应用执行必要的测试,以避免应用或产品的默认设置。客户产品设计中的缺陷可能会影响 NVIDIA 产品的质量和可靠性,并可能导致超出本文档中包含的附加或不同的条件和/或要求。NVIDIA 对可能基于或归因于以下原因的任何默认设置、损坏、成本或问题不承担任何责任:(i) 以任何违反本文档的方式使用 NVIDIA 产品或 (ii) 客户产品设计。

本文档未授予任何 NVIDIA 专利权、版权或 NVIDIA 其他知识产权的明示或暗示的许可。NVIDIA 发布的关于第三方产品或服务的信息不构成 NVIDIA 授予使用此类产品或服务的许可,也不构成对其的保证或认可。使用此类信息可能需要获得第三方在其专利或其他知识产权下的许可,或获得 NVIDIA 在其专利或 NVIDIA 其他知识产权下的许可。

只有在事先获得 NVIDIA 书面批准的情况下,才允许复制本文档中的信息,并且复制时不得进行更改,必须完全符合所有适用的出口法律和法规,并附带所有相关的条件、限制和通知。

本文档和所有 NVIDIA 设计规范、参考板、文件、图纸、诊断程序、列表和其他文档(统称为“材料”,单独称为“材料”)均“按原样”提供。NVIDIA 对材料不作任何明示、暗示、法定或其他形式的保证,并且明确否认所有关于不侵权、适销性和适用于特定用途的暗示保证。在法律未禁止的范围内,在任何情况下,NVIDIA 均不对任何损害(包括但不限于任何直接、间接、特殊、偶然、惩罚性或后果性损害)负责,无论其因何种原因引起,也无论责任理论如何,即使 NVIDIA 已被告知可能发生此类损害。尽管客户可能因任何原因而遭受任何损害,但 NVIDIA 对本文所述产品的客户承担的累计和累积责任应根据产品的销售条款的规定进行限制。

3.2. OpenCL

OpenCL 是 Apple Inc. 的商标,已授权 Khronos Group Inc. 使用。

3.3. 商标

NVIDIA 和 NVIDIA 徽标是 NVIDIA Corporation 在美国和其他国家/地区的商标或注册商标。其他公司和产品名称可能是与其相关的各自公司的商标。

1

Warp 同步指的是这样一种假设,即同一 warp 中的线程在每个指令处同步,并且可以例如在没有显式同步的情况下通信值。