nvcc编译分成device部分编译和host部分编译,host部分直接调用平台编译器进行编译Linux使用gcc,window使用cl.exe,这里主要讲解device部分的编译,此部分编译分两个阶段,第一阶段将源文件.cu文件的device部分编译成ptx文本指令,第二阶段将ptx文本指令编译成在真实架构上运行的二进制指令,第二阶段可能发生在生成可执行程序的过程中,也可能发生在运行可执行程序的过程中(just-in-time compilation)。在生成可执行程序的过程中可以根据nvcc选项选择是否将ptx文本指令(x.ptx中间文件中)、二进制指令(x.cubin中间文件)嵌入到可执行程序中,一般有3种嵌入方式:只嵌入x.ptx(第二阶段被忽略,全部依赖just-in-time compilation);只嵌入x.cubin(无法进行just-in-time compilation);两者都嵌入(运行过程中driver找到合适二进制指令镜像则加载之,否则进行just-in-time compilation再加载之)。
虚拟架构和真实架构
gpu设备的应用兼容性和cpu明显不同,cpu每一代指令集一般都兼容上一代指令集,所以已发布的应用可以运行在新一代的cpu硬件上,但GPU新一代指令集不兼容上一代的指令集。GPU使用virtual architectures来保证应用层兼容性(PTX可以看做虚拟的GPU架构,ptx是文本指令)。GPU虽然在真实架构上不具备二进制指令的兼容性,但是PTX可以跨代兼容,例如使用compute_30虚拟架构生成的PTX指令,可以用来生成sm_50的真实架构的二进制指令。低代PTX文本指令可以跨代生成高代二进制指令,但低代二进制指令却无法跨代运行在高代真实架构GPU,高代二进制指令更不可能运行在低代架构GPU上。
sm_30 and sm_32 | Basic features + Kepler support + Unified memory programming |
sm_35 | + Dynamic parallelism support |
sm_50, sm_52, and sm_53 | + Maxwell support |
sm_60, sm_61, and sm_62 | + Pascal support |
sm_70 | + Volta support |
上表中第二列Kepler、 Maxwell、 Pascla 、Volta都是NVDIA显卡架构名(不同于系列名,如GeForce、 Quadro是系列名,分别对应游戏和深度学习两个领域),各代架构对应不同的各代指令集。第一列对应显卡的functional capabilities,例如sm_35,3代表第3代架构,5代表第五发版本,同一代的二进制指令是兼容的,sm_30的真实架构的二进制指令可以运行在sm_35架构的GPU上。
Virtual Architecture Feature List
compute_30 and compute_32 | Basic features + Kepler support + Unified memory programming |
compute_35 | + Dynamic parallelism support |
compute_50, compute_52, and compute_53 | + Maxwell support |
compute_60, compute_61, and compute_62 | + Pascal support |
compute_70 | + Volta support |
上表对应GPU虚拟架构,类比真实架构进行理解。
NVCC使用
使用指令
nvcc x.cu --gpu-architecture=compute_50 --gpu-code=sm_50
效果如下图
最终只有对应真实架构sm_50的二进制的指令被嵌入到最用的可执行程序或者库文件中,如果运行在非sm_50的GPU上,将会报错如下:
addKernel launch failed: no kernel image is available for execution on the device
addWithCuda failed!
因为没有将PTX文本指令嵌入到可执行程序中,没法进行即时编译(just-in-time compilation)。
将PTX文本指令和二进制指令都嵌入到可执行程序中可以使用指令:
nvcc x.cu --gpu-architecture=compute_50 --gpu-code=compute_50,sm_50
或者省略--gpu-code
nvcc x.cu --gpu-architecture=sm_50
将一个版本的PTX指令和多种二进制指令嵌入可执行程序使用如下指令:
nvcc x.cu --gpu-architecture=compute_50 --gpu-code=compute_50,sm_50,sm_52
只将ptx文本指令嵌入到可执行文件可以使用如下指令:
nvcc x.cu --gpu-architecture=compute_50 --gpu-code=compute_50
或者省略--gpu-code
nvcc x.cu --gpu-architecture=compute_50
just-in-time compilation如下:
总结:compute_50这参数项用来指定生成ptx指令的版本,跟在--gpu-architecture选项和--gpu-code选项之后皆可;sm_50这类参数用来指定生成二进制代码的版本,不考虑shorthand的情况(详见官网,就是以上省略--gpu-code的情况),跟在--gpu-code选项之后;--gpu-architecture选项只能跟一个参数,不考虑shorthand的情况,只能用来指定生成的ptx版本;--gpu-code选项可以跟多个参数,不考虑shorthand的情况,用来指定生成的二进制文件的版本和最终嵌入到可执行文件中的内容,是只有ptx文本指令还是只有二进制指令,或者两者皆有。为何不混淆记忆,我们可以不使用shorthand的情况,即不省略--gpu-code选项。