1. 驱动程序 API 和运行时 API 之间的区别

驱动程序 API 和运行时 API 非常相似,并且在很大程度上可以互换使用。但是,两者之间存在一些值得注意的关键差异。

复杂性与控制

运行时 API 通过提供隐式初始化、上下文管理和模块管理来简化设备代码管理。这使得代码更简单,但也缺乏驱动程序 API 所具有的控制级别。

相比之下,驱动程序 API 提供更精细的控制,尤其是在上下文和模块加载方面。内核启动的实现要复杂得多,因为执行配置和内核参数必须通过显式函数调用来指定。然而,与运行时不同的是,在运行时,所有内核在初始化期间自动加载,并在程序运行期间保持加载状态,而使用驱动程序 API,可以仅保留当前需要的模块加载,甚至可以动态重新加载模块。驱动程序 API 也是语言独立的,因为它只处理 cubin 对象。

上下文管理

上下文管理可以通过驱动程序 API 完成,但在运行时 API 中未公开。相反,运行时 API 自身决定线程使用哪个上下文:如果通过驱动程序 API 使上下文成为调用线程的当前上下文,则运行时将使用该上下文,但如果没有此类上下文,它将使用“主上下文”。主上下文根据需要创建,每个进程每个设备一个,进行引用计数,然后在不再有对它们的引用时销毁。在一个进程中,运行时 API 的所有用户将共享主上下文,除非已为每个线程设置了当前上下文。运行时使用的上下文,即当前上下文或主上下文,可以使用 cudaDeviceSynchronize() 进行同步,并使用 cudaDeviceReset() 进行销毁。

然而,将运行时 API 与主上下文一起使用也有其缺点。例如,这可能会给为更大的软件包编写插件的用户带来麻烦,因为如果所有插件在同一进程中运行,它们将共享一个上下文,但可能无法相互通信。因此,如果其中一个插件在完成所有 CUDA 工作后调用 cudaDeviceReset(),则其他插件将失败,因为它们正在使用的上下文在他们不知情的情况下被销毁了。为了避免这个问题,CUDA 客户端可以使用驱动程序 API 创建和设置当前上下文,然后使用运行时 API 来处理它。但是,上下文可能会消耗大量资源,例如设备内存、额外的宿主线程以及设备上上下文切换的性能成本。当将驱动程序 API 与基于运行时 API 构建的库(例如 cuBLAS 或 cuFFT)结合使用时,这种运行时-驱动程序上下文共享非常重要。