CUDA:如何使用-arch和-code,SM与计算的区别

51
我仍然不确定在使用nvcc编译时如何正确指定代码生成的架构。我知道我的二进制文件中嵌入了机器代码和PTX代码,并且可以通过控制器开关-code-arch(或两者的组合,使用-gencode)来控制它。
现在,根据this,除了这两个编译器标志外,还有两种指定架构的方法:sm_XXcompute_XX,其中compute_XX是虚拟架构,而sm_XX是实际架构。-arch标志仅采用虚拟架构的标识符(例如compute_XX),而-code标志采用实际和虚拟架构的标识符。
文档说明-arch指定要为其编译输入文件的虚拟架构。但是,这些PTX代码不会自动编译为机器代码,而是一个“预处理步骤”。
现在,-code应该指定PTX代码的汇编和优化架构。但是,不清楚哪些PTX或二进制代码将嵌入到二进制文件中。例如,如果我指定-arch=compute_30 -code=sm_52,那么我的代码首先会被编译成特征级别为3.0的PTX,然后从中创建特征级别5.2的机器码?嵌入什么呢?
如果我只指定-code=sm_52,那么会发生什么?只会嵌入由V5.2 PTX代码创建的V5.2机器码吗?与-code=compute_52有什么区别?
1个回答

50

一些相关的问题和答案在这里这里

我仍然不确定在使用nvcc编译器构建时如何正确指定生成代码的架构。

完整的描述有点复杂,但它们旨在具有相对简单、易于记忆的规范用法。编译为代表您想要目标的GPU的架构(虚拟和真实)。一个相当简单的形式是:

-gencode arch=compute_XX,code=sm_XX

其中XX是您要针对的GPU的两位数字计算能力。如果您想要针对多个GPU,则只需为每个XX目标重复整个序列即可。这大致是CUDA示例代码项目采用的方法。(如果您想在可执行文件中包括PTX,请使用附加的-gencodecode选项指定与arch选项相同的PTX虚拟架构)。

另一个相当简单的形式,仅针对单个GPU时,只需使用:

-arch=sm_XX 

针对XX的描述相同。此表格将为指定的架构包含SASS和PTX。

现在,除了两个编译器标志外,还有两种指定架构的方式:sm_XX和compute_XX,其中compute_XX是虚拟架构,而sm_XX是真实架构。标志-arch只接受虚拟架构的标识符(例如compute_XX),而-code标志则同时接受真实和虚拟架构的标识符。

archcode作为开关的子开关使用或像您所描述的那样独立使用时,基本上是正确的。但是,例如,当-arch单独使用(没有-code)时,它代表另一种“简写”表示法,在这种情况下,您可以传递一个真实的架构,例如-arch=sm_52

然而,不清楚嵌入二进制代码将是哪个PTX,如果我指定例如-arch = compute_30 -code = sm_52,这是否意味着我的代码将首先编译为功能级别3.0 PTX,然后从中创建功能级别5.2的机器代码?嵌入了什么?

嵌入内容的确切定义因使用形式而异。但对于此示例:

-gencode arch=compute_30,code=sm_52

或者对于您所确定的等效情况:

-arch=compute_30 -code=sm_52

是的,这意味着:

  1. 将从您的源代码生成临时PTX代码,并使用cc3.0 PTX。
  2. ptxas工具将从该PTX生成符合cc5.2标准的SASS代码。
  3. SASS代码将嵌入到可执行文件中。
  4. PTX代码将被丢弃。

(我不确定为什么会实际指定这样的组合,但这是合法的。)

如果我只指定-code = sm_52,那会发生什么? 只有由V5.2 PTX代码创建的V5.2机器代码将被嵌入其中?与-code = compute_52有什么区别?

-code = sm_52 将从中间的PTX代码生成cc5.2 SASS代码。 SASS代码将被嵌入,PTX将被丢弃。 请注意,在此格式中仅指定此选项而不带 -arch 选项是非法的。(1)

-code = compute_52 将生成cc5.x PTX代码(仅限)并将该PTX嵌入可执行文件/二进制文件中。 请注意,在此格式中仅指定此选项而不带 -arch 选项是非法的。(1)

可以使用cuobjdump工具来识别给定二进制文件中确切的组件。

(1)当未使用 -gencode 开关并且未使用 -arch 开关时, nvcc 会假定默认值 -arch = sm_20 添加到编译命令中(这是针对CUDA 7.5的。默认的-arch 设置可能因CUDA版本而异)。 sm_20 是一个真正的架构,当在提供 -code 选项时也不合法在 -arch 选项上指定真实架构。


3
当我第一次尝试搞清楚如何最好地使用这些标志时,我使用了cuobjdump --dump-ptxcuobjdump --dump-sass来检查在目标文件中会发出哪些SASS(二进制机器代码)和PTX(中间架构无关代码)的版本。我建议其他人也采用这种实践方法。从实际角度来看,要生成一个支持多种架构的可执行文件,您需要为每种架构包含相应的SASS,以及仅针对“最近的架构”包含PTX(用于即时编译到可能的未来GPU上)。 - njuffa
1
好的,我理解得对吗:如果我想嵌入所有现有架构的机器码和最新计算能力的PTX代码,我可以这样做:-gencode arch=compute_20,code=sm_20 [...] -gencode arch=compute_53,code=sm_53 -gencode arch=compute_53,code=compute_53?所以只有-code会影响嵌入的内容! - bweber
是的,那样做应该可以。就像我说的,那更或多或少是示例项目所做的。您可能需要查看这些 makefile 或 VS 项目的设置方式。 - Robert Crovella
编译时使用multi-gencode会有冲突吗?例如,half函数__hmul只支持compute 5.2之后的版本。如果nvcc使用multi-gencode编译,如3.5、5.2、7.0,由于不同架构对半精度的支持,会导致冲突。 - Xiaolin Wu
你不能为不支持你正在使用的某些功能的架构编译代码。 - Robert Crovella
1
@XiaolinWu 如果你根据自动定义的宏__CUDA_ARCH__的值来进行编译条件判断,那么你就可以实现这个功能。在你的示例中,代码将被预处理三次,分别使用350、520和700(arch ×100)的上述宏值。你需要做的是例如 #if __CUDA_ARCH__ >= 520(使用 _hmul 的代码),#else(一些明智的操作:报告错误或者以不那么高效的方式进行计算)#endif。请参阅 https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#virtual-architecture-identification-macro 上的文档。 - kkm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接