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