|
前言
c++图像算法CUDA加速
文章中一些概念目前理解不是很深,暂时当作笔记。
<hr/>1 NVCC
内核可以使用称为PTX的CUDA指令集体系结构编写,然而,使用诸如C++等高级编程语言通常更有效。在这两种情况下,内核都必须通过nvcc编译成二进制代码才能在设备上执行。nvcc是一个编译器驱动程序,它简化了编译C++或PTX代码的过程:它提供了简单而熟悉的命令行选项,并通过调用实现不同编译阶段的工具集合来执行它们。
2 编译
编译分为脱机编译和即时编译
(1)CUDA编译轨迹
CUDA编译工作如下:
1、输入程序经过预处理,用于设备编译,并编译为CUDA二进制(cubin)和/或PTX中间代码,这些代码放在fatbinary中。
2、输入程序再次被预处理以进行主机编译,并被合成以嵌入fatbinary,并将CUDA特定的C++扩展转换为标准C++结构。
3、然后,C++主机编译器将带有嵌入式fatbinary的合成主机代码编译成主机对象。
每当主机程序启动设备代码时,CUDA运行时系统都会检查嵌入式fatbinary,以获得当前GPU的适当fatbinary图像。
如下图:CUDA编译轨迹
(2)脱机编译(Offline Compilation )
使用nvcc编译的源文件(.cu)可以包括主机代码和设备代码的混合物。nvcc的基本工作流程包括
1、将设备代码与主机代码分离;
2、将设备代码编译成汇编形式(PTX代码)和/或二进制形式(cubin对象);
3、并通过必要的CUDA运行时函数调用替换Kernels中引入的<<<…>>>语法来修改主机代码,以从PTX代码和/或cubin对象加载和启动每个编译的内核。
修改后的主机代码可以作为C++代码输出,该代码可以使用其他工具编译,也可以通过让nvcc在最后一个编译阶段调用主机编译器直接作为目标代码输出。
然后,应用程序可以:链接到编译的主机代码(这是最常见的情况),或者忽略修改的主机代码(如果有),使用CUDA驱动程序API加载并执行PTX代码或cubin对象。
(3)即时编译(Just-in-Time Compilation )
1 应用程序在运行时加载的任何PTX或NVVM IR代码都由设备驱动程序进一步编译为二进制代码。这称为实时编译。实时编译增加了应用程序加载时间,但允许应用程序从每个新设备驱动程序带来的任何新编译器改进中受益。这也是应用程序在编译应用程序时能够在不存在的设备上运行的唯一方式(程序兼容性)。
2 当设备驱动程序即时为某些应用程序编译一些PTX或NVVM IR代码时,它会自动缓存生成的二进制代码的副本,以避免在应用程序的后续调用中重复编译。缓存在设备驱动程序升级时自动失效,因此应用程序可以从内置于设备驱动程序中的新实时编译器的改进中受益。
3 作为使用nvcc编译CUDA C++设备代码的替代方案,NVRTC可用于在运行时将CUDA C++的设备代码编译为PTX。NVRTC是CUDA C++的运行时编译库;更多信息可在NVRTC用户指南中找到。
<hr/>3 兼容性
(1)二进制兼容性(Binary Compatibility)
二进制代码是特定于架构的。一个cubin对象是使用指定目标架构的编译器选项-代码生成的:
例如,使用 -code=sm_35 进行编译会为具有计算能力3.5的设备生成二进制代码。
二进制兼容性保证从一个小修订到下一个修订,而不是从一个小修订到前一个修订或跨主要修订。换句话说,为计算能力 X.y 生成的cubin对象将只在具有计算能力 X.z 的设备上执行,其中 z>=y。
注意:仅desktop支持二进制兼容性。Tegra不支持此功能。此外,desktop和Tegra之间的二进制兼容性也不受支持。
(2) PTX兼容性(PTX Compatibility)
某些PTX指令仅在具有较高计算能力的设备上受支持。例如,只有计算能力为3.0及更高的设备才支持 Warp Shuffle Functions函数。arch编译器选项指定将C++编译为PTX代码时假定的计算能力。因此,例如,包含warp shuffle Functions 的代码必须使用-arch=compute_30(或更高版本)编译。
为某些特定计算能力生成的PTX代码始终可以编译为具有更大或同等计算能力的二进制代码。请注意,从早期PTX版本编译的二进制文件可能不使用某些硬件功能。例如,从为计算能力6.0(Pascal)生成的PTX编译的计算能力7.0(Volta)的二进制目标设备将不会使用Tensor Core指令,因为这些指令在Pascal上不可用。因此,最终二进制文件的性能可能比使用最新版本的PTX生成的二进制文件更差。
(3) 应用程序兼容性( Application Compatibility)
(1)要在具有特定计算能力的设备上执行代码,应用程序必须加载与此计算能力兼容的二进制或PTX代码,如二进制兼容性和PTX兼容性中所述。特别是,为了能够在具有更高计算能力的未来架构上执行代码(尚未生成二进制代码),应用程序必须加载将为这些设备即时编译的PTX代码。
CUDA C++应用程序中嵌入的PTX和二进制代码由 nvcc 的 -arch 和 -code 编译器选项或- gencode 编译器选项控制。例如
nvcc x.cu
-gencode arch=compute_50,code=sm_50
-gencode arch=compute_60,code=sm_60
-gencode arch=compute_70,code=\&#34;compute_70,sm_70\&#34;
嵌入与计算能力5.0和6.0兼容的二进制代码以及与计算能力7.0兼容的PTX和二进制代码。(2)生成主机代码,在运行时自动选择最适合加载和执行的代码,在上面的例子中,这将是:
用于计算能力为5.0和5.2的设备的5.0二进制代码,,
用于计算能力为6.0和6.1的设备的6.0二进制代码,
用于计算能力为7.0和7.5的设备的7.0二进制代码,
为具有计算能力8.0和8.6的设备在运行时编译为二进制代码的PTX代码。
.cu 可以有一个优化的代码路径,例如使用 warp shuffle 操作。__CUDA_ARCH__ 宏可以用于基于计算能力来区分各种代码路径。它仅为设备代码而定义。例如,当使用 -arch=compute_35 进行编译时,__CUDA_ARCH__等于350。
(3)使用驱动程序API的应用程序必须编译代码来分离文件,并在运行时显式地加载和执行最合适的文件。
Volta架构引入了独立的线程调度,它改变了线程在GPU上的调度方式。对于依赖于以前体系结构中SIMT调度的特定行为的代码,独立的线程调度可能会改变参与的线程集,从而导致不正确的结果。为了帮助迁移,Volta开发人员可以使用编译器选项组合 -arch=compute_60 -code=sm_70 选择加入Pascal的线程调度。
(4) c++兼容性(C++ Compatibility )
编译器的前端根据C-++语法规则处理CUDA源文件。主机代码支持完整的C++。但是,如C++语言支持中描述的设备代码只完全支持C++的一个子集。
(5)64位兼容性(64-Bit Compatibility )
1 64位版本的nvcc在64位模式下编译设备代码(即,指针是64位的)。64位模式下编译的设备代码只支持64位模式下编译的主机代码。
2 同样地,32位版本的nvcc以32位模式编译设备代码,而以32位模式编译的设备代码只支持以32位模式编译的主机代码。
3 32位版本的nvcc可以在64位模式下编译设备代码,还可以使用-m64编译器选项。
4 64位版本的nvcc可以在32位模式下编译设备代码,还可以使用-m32编译器选项。 |
|