NVCC的offline compilation和just-in-time compilation

这篇博客讲的比较清楚:http://blog.sina.com.cn/s/blog_178a6cbd50102xczh.html

任何一种程序设计语言都需要相应的编译器将其编译为二进制代码,进而在目标机器上得到执行。对于异构计算而言,这一过程与传统程序设计语言是有一些区别的。为什么?因为CUDA它本质上不是一种语言,而是一种异构计算的编程模型,使用CUDA C写出的代码需要在两种体系结构完全不同的设备上执行:1、CPU;2、GPU。因此,CUDA C的编译器所做的工作就有点略多了。一方面,它需要将源代码中运行在GPU端的代码编译得到能在CUDA设备上运行的二进制程序。另一方面,它也需要将源代码中运行在CPU端的程序编译得到能在主机CPU上运行的二进制程序。最后,它需要把这两部分有机地结合起来,使得两部分代码能够协调运行。
CUDA C为我们提供了这样的编译器,它便是NVCC。严格意义上来讲,NVCC并不能称作编译器,NVIDIA称其为编译器驱动(Compiler Driver),本节我们暂且使用编译器来描述NVCC。使用nvcc命令行工具我们可以简化CUDA程序的编译过程,NVCC编译器的工作过程主要可以划分为两个阶段:离线编译(Offline Compilation)和即时编译(Just-in-Time Compilation)。

离线编译(Offline Compilation)
    下面这幅图简单说明了离线编译的过程:
NVCC编译器:离线编译与即时编译

在CUDA源代码中,既包含在GPU设备上执行的代码,也包括在主机CPU上执行的代码。因此,NVCC的第一步工作便是将二者分离开来,这一过程结束之后:
        1. 运行于设备端的代码将被NVCC工具编译为PTX代码(GPU的汇编代码)或者cubin对象(二进制GPU代码);
        2. 运行于主机端的代码将被NVCC工具改写,将其中的内核启动语法(如<<<...>>>)改写为一系列的CUDARuntime函数,并利用外部编译工具(gcc for linux,或者vc compiler for windows)来编译这部分代码,以得到运行于CPU上的可执行程序。
        完事之后,NVCC将自动把输出的两个二进制文件链接起来,得到异构程序的二进制代码。
即时编译(Just-in-time Compile)
        任何在运行时被CUDA程序加载的PTX代码都会被显卡的驱动程序进一步编译成设备相关的二进制可执行代码。这一过程被称作即时编译(just-in-time compilation)。即时编译增加了程序的装载时间,但是也使得编译好的程序可以从新的显卡驱动中获得性能提升。同时到目前为止,这一方法是保证编译好的程序在还未问世的GPU上运行的唯一解决方案。
        在即时编译的过程中,显卡驱动将会自动缓存PTX代码的编译结果,以避免多次调用同一程序带来的重复编译开销。NVIDIA把这部分缓存称作计算缓存(compute cache),当显卡驱动升级时,这部分缓存将会自动清空,以使得程序能够自动获得新驱动为即时编译过程带来的性能提升。

有一些环境变量可以用来控制即时编译过程:
        1. 设置CUDA_CACHE_DISABLE为1将会关闭缓存功能
        2. CUDA_CACHE_MAXSIZE变量用于指定计算缓存的字节大小,默认情况下它的值是32MB,它最大可以被设置为4GB。任何大于缓存最大值得二进制代码将不会被缓存。在需要的情况下,一些旧的二进制代码可能被丢弃以腾出空间缓存新的二进制代码。
        3. CUDA_CACHE_PATH变量用于指定计算缓存的存储目录地址,它的缺省值如下:

        4. 设置CUDA_FORCE_PTX_JIT为1会强制显卡驱动忽略应用程序中的二进制代码并且即时编译程序中的嵌入PTX代码。如果一个内核函数没有嵌入的PTX代码,那么它将会装载失败。该变量可以用来确认程序中存在嵌入的PTX代码。同时,使用即时编译(just-in-time Compilation)技术也可确保程序的向前兼容性。

猜你喜欢

转载自blog.csdn.net/kinggang2017/article/details/78860418