我想了解如何在 cuda 运行时 API 应用程序中创建 cuda 上下文并与内核关联?
我知道这是由驱动程序 API 在幕后完成的。但我想了解一下创作的时间线。
首先,我知道 cudaRegisterFatBinary 是第一个 cuda api 调用,它向运行时注册一个 fatbin 文件。接下来是一些cuda函数注册API,它们在驱动层中调用cuModuleLoad。但是,如果我的 Cuda 运行时 API 应用程序调用 cudaMalloc,如何向该函数提供与上下文关联的指针,我认为应该事先创建该上下文。如何获取已创建的上下文的句柄并将未来的运行时 API 调用与其关联?请揭开内部运作的神秘面纱。
引用 NVIDIA 的文档
CUDA 运行时 API 调用在 CUDA 驱动程序 API CUcontext 上运行,
绑定到当前主机线程。
如果不存在绑定到当前的 CUDA Driver API CUcontext
调用 CUDA Runtime API 时的线程,需要
CUcontext 那么 CUDA Runtime 将隐式创建一个新的 CUcontext
在执行调用之前。
如果 CUDA 运行时创建 CUcontext,则 CUcontext 将是
使用 CUDA Runtime API 指定的参数创建
函数 cudaSetDevice、cudaSetValidDevices、cudaSetDeviceFlags、
cudaGLSetGLDevice、cudaD3D9SetDirect3DDevice、
cudaD3D10SetDirect3DDevice 和 cudaD3D11SetDirect3DDevice。注意
如果这些函数在以下情况下将失败并显示 cudaErrorSetOnActiveProcess
当 CUcontext 绑定到当前主机线程时调用。
CUcontext 的生命周期由引用计数管理
机制。 CUcontext的引用计数最初设置为0,
并且通过 cuCtxAttach 递增并通过 cuCtxDetach 递减。
如果 CUcontext 是由 CUDA 运行时创建的,则 CUDA 运行时
将减少函数中该 CUcontext 的引用计数
cudaThread退出。如果 CUcontext 是由 CUDA 驱动程序 API 创建的(或
由 CUDA Runtime API 库的单独实例创建),
那么 CUDA 运行时将不会增加或减少引用
该 CUcontext 的计数。
所有 CUDA Runtime API 状态(例如,全局变量的地址和
值)与其底层 CUcontext 一起移动。特别是,如果一个
CUcontext 从一个线程移动到另一个线程(使用 cuCtxPopCurrent
和 cuCtxPushCurrent) 那么所有 CUDA Runtime API 状态都将移动到
那个线程也是如此。
但我不明白的是cuda运行时如何创建上下文?为此使用了哪些 API 调用? nvcc 编译器是否插入一些 API 调用来在编译时执行此操作,还是完全在运行时完成?如果前者为真,那么哪些运行时 API 用于此上下文管理?后者是真的,具体是如何完成的?
如果上下文与主机线程关联,我们如何访问该上下文?它是否自动与线程处理的所有变量和指针引用相关联?
最终模块加载是如何在上下文中完成的?
CUDA 运行时维护要加载的模块的全局列表,并在每次将使用 CUDA 运行时的 DLL 或 .so 加载到进程中时添加到该列表中。但在创建设备之前,模块实际上并未加载。
上下文创建和初始化是由 CUDA 运行时“延迟”完成的——每次调用像 cudaMemcpy() 这样的函数时,它都会检查 CUDA 是否已初始化,如果没有,它会创建一个上下文(在先前由 cudaSetDevice() 指定的设备,或者如果从未调用 cudaSetDevice() 则为默认设备)并加载所有模块。从那时起,上下文就与该 CPU 线程关联,直到它被 cudaSetDevice() 更改为止。
您可以使用驱动程序 API 中的上下文/线程管理函数(例如 cuCtxPopCurrent()/cuCtxPushCurrent())来使用来自不同线程的上下文。
您可以调用 cudaFree(0);强制进行这种惰性初始化。
我强烈建议在应用程序初始化时这样做,以避免竞争条件和未定义的行为。继续并尽早在您的应用程序中枚举并初始化设备;完成后,在 CUDA 4.0 中,您可以从任何 CPU 线程调用 cudaSetDevice(),它将选择由初始化代码创建的相应上下文。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)