解读CUDA C Programming Guide 第三章第1节(共6节)(3/5)

本系列为《解读CUDA C Programming Guide》.

本书旨在介绍进行CUDA并行优化的C编程指导。共5章,内容分别是:

  • Introduction
  • Programming Model
  • Programming Interface
  • Hardware Implementation
  • Performance Guidelines

本文简单解读第三章:Programming Interface.

本章主要内容包括:

  1. Compilation with NVCC
  2. CUDA C Runtime
  3. Versioning and Compatibility
  4. Compute Modes
  5. Mode Switches
  6. Tesla Compute Cluster Mode for Windows

 CUDA C为熟悉C语言编程的用户在设备上进行CUDA编程提供了非常简单的方式。

它包含了一个C语言和运行库的最小扩展集合。

核心语言扩展集已经在Programming model里介绍过了。即,允许程序员用C来定义核函数,并使用一些新的语义规则,在每次调用核函数的时候,来定义grid(网格,指线程)和block(块,指线程块)维度。

本章内容比较多且复杂,因此分为几个小节来介绍。首先介绍 Compilation with NVCC

Compilation with NVCC

  • Compilation Workflow
  • Binary Compatibility
  • PTX Compatibility
  • Application Compatibility
  • C/C++ Compatibility
  • 64-bit Compatibility

我们可以使用CUDA指令集结构(PTX)来构造核函数。然而,通常更高效的方法是使用高级编程语言来定义核函数。不过,使用任意方法,核函数都必须被nvcc编译为二进制代码来在设备上运行。

Nvcc是一个编译器,它提供了简单而熟悉的命令行选择,并通过调用编译过程中的一系列工具来执行这些命令。这部分概述了nvcc的workflow和命令选择。更详细完整的描述可在nvcc的用户指导手册中找到。

Compilation workflow

Offline Compilation

源文件nvcc编译可包含一系列的主机代码和设备代码。主机代码是在CPU上运行的代码,设备代码是在GPU上运行的代码。Nvcc的基本workflow包含了从主机代码中分离出设备代码,然后再

  1. 将设备代码编译为整体形式(PTX code)或者是二进制形式(cubin);
  2. 修改主机代码为<<<…>>>形式,来调用核函数。

已经修改好的主机代码既可以是C代码(用其他工具来编译),也可以是直接被nvcc的主机编译器来编译。

Just-in-time Compilation

什么是Just-in-Time compilation呢?

任何在运行时载入的PTX代码被设备driver编译成二进制代码。换句话说,就是实时编译。

实时编译提高了应用的载入时间(which is bad),但是它允许应用从任何新的设备driver带来的新编译器的改进中得到好处。另外,它是唯一一种方式,可让应用运行于设备,但当它编译的时候并不存在于设备。

当设备对某些应用的PTX代码进行实时编译的时候,它会自动地缓存一份已生成的二进制代码,这样做的好处是当连续调用应用时,可避免重复编译。这份缓存会在设备driver升级的时候自动失效。

环境变量可用来控制Just-in-Time compilation。

Binary Compilation

二进制代码是特定于体系结构的。使用指定目标体系结构的编译器选项-code生成cubin对象:例如,使用-code = sm_35进行编译将为计算能力为3.5的设备生成二进制代码。

从一个次要修订版本到下一个修订版本,都保证了二进制兼容性,但不能保证从一个次要修订版本到上一个修订版本或跨主要版本。换句话说,为计算能力X.y生成的立方对象将仅在计算能力X.z的设备上执行,其中z≥y。

仅台式机支持二进制兼容性。 Tegra不支持此功能。另外,不支持桌面和Tegra之间的二进制兼容性。

 PTX Compatibility

某些PTX指令仅在具有更高计算能力的设备上受支持。例如,包含warp shuffle的代码必须使用-arch = compute_30(或更高版本)进行编译。

为某些特定计算能力生成的PTX代码始终可以编译为具有更大或相等计算能力的二进制代码。请注意,从较早的PTX版本编译的二进制文件可能未使用某些硬件功能。例如,从针对计算能力6.0(Pascal)生成的PTX编译而来的计算能力7.0(Volta)的二进制目标设备将不使用Tensor Core指令,因为这些在Pascal上不可用。结果,最终二进制文件的性能可能比使用最新版本的PTX生成二进制文件时的性能差。

Application Compatibility

要在具有特定计算功能的设备上执行代码,应用程序必须加载与二进制计算和PTX兼容性中所述的与此计算功能兼容的二进制或PTX代码。特别是,为了能够在具有更高计算能力的未来架构上执行代码(尚无法为其生成二进制代码),应用程序必须加载为这些设备及时编译的PTX代码(请参阅Just-in-Time compilation)。

哪些PTX和二进制代码嵌入CUDA C应用程序中,由-arch和-code编译器选项或ngencc用户手册中详细介绍的-gencode编译器选项控制。

例如,

nvcc x.cu
 -gencode arch=compute_35,code=sm_35
 -gencode arch=compute_50,code=sm_50
 -gencode arch=compute_60,code=\'compute_60,sm_60\'
嵌入与计算能力3.5和5.0兼容的二进制代码(第一和第二个-gencode选项)和PTX和与计算能力6.0兼容的二进制代码(第三-gencode选项)。
生成主机代码以在运行时自动选择最合适的代码加载并执行,在上面的示例中,将为

‣3.5具有计算能力3.5和3.7的设备的二进制代码,
‣5.0二进制代码,用于具有5.0和5.2计算能力的设备,
‣6.0二进制代码,用于具有6.0和6.1计算能力的设备,
‣PTX代码在运行时被编译为具有7.0或更高版本的计算能力的设备的二进制代码。

例如,x.cu可以有一个优化的代码路径,该路径使用了warp shuffle操作,只有计算能力3.0或更高版本的设备才支持。 __CUDA_ARCH__宏可用于根据计算能力来区分各种代码路径。仅针对设备代码定义。例如,使用-arch = compute_35进行编译时,__ CUDA_ARCH__等于350。

使用驱动程序API的应用程序必须编译代码以分离文件,并在运行时显式加载和执行最合适的文件。

Volta体系结构引入了独立线程调度功能,该功能改变了在GPU上调度线程的方式。对于依赖于先前体系结构中SIMT调度的特定行为的代码,独立线程调度可能会更改参与线程的集合,从而导致错误的结果。为了在实施独立线程调度中详细介绍的纠正措施时帮助迁移,Volta开发人员可以使用编译器选项组合arch = compute_60 -code = sm_70选择加入Pascal的线程调度。

nvcc用户手册列出了-arch,-code和-gencode编译器选项的各种简写形式。例如,-arch = sm_35是-arch = compute_35 -code = compute_35,sm_35的简写(与-gencode arch = compute_35,code = \'compute_35,sm_35 \'相同)。

 C/C++ Compatibility

编译器的前端根据C ++语法规则处理CUDA源文件。主机代码支持完全C ++。但是,如C/C++ Language Support.中所述,设备代码仅完全支持C ++的子集。

64-Bit Compatibility

nvcc的64位版本以64位模式编译设备代码(即,指针为64位)。只有以64位模式编译的主机代码才支持以64位模式编译的设备代码。

同样,nvcc的32位版本以32位模式编译设备代码,而以32位模式编译的主机代码仅支持以32位模式编译的设备代码。

nvcc的32位版本也可以使用-m64编译器选项以64位模式编译设备代码。

nvcc的64位版本也可以使用-m32编译器选项以32位模式编译设备代码。

发布了239 篇原创文章 · 获赞 41 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/NXHYD/article/details/104882812