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)结合使用时,这种运行时-驱动程序上下文共享非常重要。