6

我想了解 cuda 上下文是如何在 cuda 运行时 API 应用程序中创建并与内核相关联的?

我知道它是由驱动程序 API 在后台完成的。但我想了解创作的时间线。

首先,我知道 cudaRegisterFatBinary 是第一个 cuda api 调用,它在运行时注册了一个 fatbin 文件。紧随其后的是一些在驱动层调用 cuModuleLoad 的 cuda 函数注册 API。但是,如果我的 Cuda 运行时 API 应用程序调用 cudaMalloc,那么提供给这个函数的指针是如何与上下文相关联的,我认为它应该是事先创建的。如何获得这个已经创建的上下文的句柄并将未来的运行时 API 调用与它相关联?请揭开内部运作的神秘面纱。

引用 NVIDIA 的文档

CUDA 运行时 API 调用在绑定到当前主机线程的 CUDA 驱动程序 API CUcontext 上运行。

如果在 CUDA Runtime API 调用时不存在绑定到当前线程的 CUDA Driver API CUcontext,则 CUDA Runtime 将在执行调用之前隐式创建一个新的 CUcontext。

如果 CUDA 运行时创建 CUcontext,则将使用 CUDA 运行时 API 函数 cudaSetDevice、cudaSetValidDevices、cudaSetDeviceFlags、cudaGLSetGLDevice、cudaD3D9SetDirect3DDevice、cudaD3D10SetDirect3DDevice 和 cudaD3D11SetDirect3DDevice 指定的参数创建 CUcontext。请注意,如果在 CUcontext 绑定到当前主机线程时调用这些函数,它们将失败并显示 cudaErrorSetOnActiveProcess。

CUcontext 的生命周期由引用计数机制管理。CUcontext 的引用计数最初设置为 0,并由 cuCtxAttach 递增并由 cuCtxDetach 递减。

如果 CUDA 运行时创建了 CUcontext,则 CUDA 运行时将在函数 cudaThreadExit 中减少该 CUcontext 的引用计数。如果一个 CUcontext 由 CUDA Driver API 创建(或由 CUDA Runtime API 库的单独实例创建),则 CUDA Runtime 不会增加或减少该 CUcontext 的引用计数。

所有 CUDA 运行时 API 状态(例如,全局变量的地址和值)都与它的底层 CUcontext 一起移动。特别是,如果 CUcontext 从一个线程移动到另一个线程(使用 cuCtxPopCurrent 和 cuCtxPushCurrent),那么所有 CUDA Runtime API 状态也将移动到该线程。

但我不明白的是 cuda 运行时如何创建上下文?为此使用了哪些 API 调用?nvcc 编译器是在编译时插入一些 API 调用来执行此操作,还是完全在运行时完成?如果前者是真的,那么什么运行时 API 用于此上下文管理?后者是真的,它究竟是如何完成的?

如果上下文与主机线程相关联,我们如何访问该上下文?它是否自动与线程处理的所有变量和指针引用相关联?

最终如何在上下文中完成模块加载?

4

1 回答 1

3

CUDA 运行时维护要加载的模块的全局列表,并在每次将使用 CUDA 运行时的 DLL 或 .so 加载到进程中时添加到该列表中。但是在创建设备之前,模块并没有真正加载。

上下文创建和初始化是由 CUDA 运行时“懒惰地”完成的——每次调用像 cudaMemcpy() 这样的函数时,它都会检查 CUDA 是否已经初始化,如果没有,它会创建一个上下文(在之前由 cudaSetDevice() 指定的设备,或者如果 cudaSetDevice() 从未调用过,则为默认设备)并加载所有模块。从那时起,上下文与该 CPU 线程相关联,直到它被 cudaSetDevice() 更改。

您可以使用驱动程序 API 中的上下文/线程管理函数,例如 cuCtxPopCurrent()/cuCtxPushCurrent(),以使用来自不同线程的上下文。

你可以调用 cudaFree(0); 强制这种延迟初始化发生。

我强烈建议在应用程序初始化时这样做,以避免竞争条件和未定义的行为。继续在您的应用程序中尽早枚举和初始化设备;完成后,在 CUDA 4.0 中,您可以从任何 CPU 线程调用 cudaSetDevice() ,它将选择由初始化代码创建的相应上下文。

于 2011-09-24T18:43:49.573 回答