3.4_模块与函数

3.4 模块与函数

模块是代码与一同加载的数据的集合,类似于Windows中的动态链接库(DLL)和Linux中的动态共享对象(DSO)。CUDA运行时和CUDA上下文不显式支持模块,模块只在CUDA驱动程序API中可用[1]。

CUDA中没有类似于对象文件的此类可被整合到模块中的中间结构。取而代之的是,nvcc直接生成可以被加载为CUDA模块的文件:

·面向特定SM版本的.cubin文件
·通过驱动程序可以编译为硬件执行代码的.ptx

数据不需要以这些形式发送给最终的用户。CUDA提供加载模块的API,模块以能够嵌入在可执行文件或其他地方的NULL结尾字符串的形式表示[2]。

一旦CUDA模块被加载,应用程序便可以查询包含在其中的资源。资源类型分如下几种:

·全局变量

  • 函数 (内核)
    ·纹理引用

一点值得注意:所有的资源在模块加载时就被创建,所以查询函数不会因为缺少资源而失败。

同上下文一样,CUDA运行时隐藏了模块的管理细节。所有的模块在CUDART初始化时加载。对于拥有大量GPU代码的应用程序,选择CUDA驱动程序API而不选择CUDA运行时的一个首要原因是:前者可以显式的管理加载和卸载模块。

模块通过调用nvcc创建。nvcc可以创建哪种类型的模块,取决于命令行的参数,参看表3-3。由于cubin已被编译至特定的GPU架构,它们不需要即时编译,而且可以非常快地加载。但是它们既不是向后兼容的(例如被编译到SM 2.x的cubin不能在SM 1.x架构上运行)也不是向前兼容的(例如编译到SM 2.x架构上的cubin不能在SM 3.x架构上运行)。所以只有预先准确知道目标GPU架构(以及相应cubin版本),才可以用没有嵌入相同版本PTX代码模块的cubin文件作为备用。

表3-3 nvcc模块类型

PTX是一种中间语言,将作为驱动程序即时编译的输入文件。因为这项编译会消耗大量的时间,如果硬件和设备没有改变过,驱动程序

对给定的PTX模块会保存编译过的模块并复用它们。如果驱动和硬件改变,所有的PTX模块也需要重新编译。

通过fatbins选项,CUDA运行时会自动操作使用合适的cubin版本,前提是cubin可用,不然会编译PTX。不同的版本以字符串的形式被嵌入在nvcc发出的主机C++代码中。应用程序使用驱动程序API具有对模块的细粒度控制的优势。例如,它可以作为资源嵌入在可执行文件中,在运行时加密或生成,但是这一使用cubins(如果不可用,则可以编译PTX)的过程必须显式实现。

一旦模块被加载,应用程序就可以查询其中包含的资源:全局资源、函数(内核)和纹理引用。还是如前文所述:所有的这些资源在模块加载时就被创建,所以查询函数(表3-4)不可能由于缺少资源而失败。

表3-4 模块查询函数

[1] 如果CUDA在即时编译中添加来自源代码的多次请求能力(像 OpenCL能做到的一样),英伟达公司可能会发现在CUDA运行时中暴露模块是合适的。
[2] cuModuleLoadDataEx()函数在4.2节中有详细描述。