等效于 cudaSetDevice 的 CUDA 驱动程序 API

Posted

技术标签:

【中文标题】等效于 cudaSetDevice 的 CUDA 驱动程序 API【英文标题】:CUDA driver API equivalent for cudaSetDevice 【发布时间】:2013-08-21 16:53:42 【问题描述】:

与运行时 API 函数 cudaSetDevice 等效的 CUDA 驱动程序 API 是什么?

我正在查看驱动程序 API,但找不到等效函数。我能做的是

cuDeviceGet(&cuDevice, device_no);
cuCtxCreate(&cuContext, 0, cuDevice);

这是不等价的,因为除了设置设备之外,它还创建了一个上下文。运行时 API cudaSetDevice 本身不会创建上下文。在运行时 API 中,CUDA 上下文是使用第一个需要设备状态的 CUDA 调用隐式创建的。

此问题的背景:CUDA 感知 MPI (MVAPICH2 1.8/9) 初始化需要在调用 MPI_init 之前设置 CUDA 设备。使用 CUDA 运行时 API,这可以通过

来完成
cudaSetDevice(device_no);
MPI_init();

但是,我不想使用对 CUDA 运行时的调用,因为我的应用程序的其余部分纯粹使用驱动程序 API,我想避免也链接到运行时。

在 MPI 初始化之前创建上下文有什么问题?原则上什么都没有。只是想知道驱动 API 中是否存在等效调用。

【问题讨论】:

再想想。我认为没有等效的调用,因为在驱动程序 API 中没有设置设备。相反,正如我发布的那样:创建设备句柄并创建上下文(隐式设置设备)。有了这个 MVAPICH2 很开心。 换句话说:使用驱动程序 API 必须在使用 CUDA 感知 MPI 时创建上下文。 在运行时 API 中,从 CUDA 4.0 开始,cudaSetDevice确实创建一个上下文,如果相关设备上尚不存在该上下文。 【参考方案1】:

您可以在有关驱动程序 API 的编程指南附录中找到相关信息,但简短版本如下:

cuCtxCreate 充当第一个 cudaSetDevice 调用(即它在驱动程序上下文堆栈上创建上下文) cuCtxPushCurrent()cuCtxPopCurrent() 对(或 cuCtxSetCurrent,取决于您使用的 API 版本)充当任何后续的 cudaSetDevice 调用(即它推送或选择先前创建的上下文作为活动上下文对于所有后续 API 调用,直到上下文从驱动程序上下文堆栈中弹出或取消选择)

【讨论】:

不要忘记 cuCtxSetCurrent() 从 Cuda 4.x 开始替换 cuCtxPush/PopCurrent(不确定确切的版本)。它也更接近于 cudaSetDevice(),除了它将上下文作为参数而不是 deviceID,并且在第一次调用时不会创建新的上下文。 你是对的。 cudaSetDevice 创建一个上下文。我通过查看分配给进程的虚拟内存量来检查这一点。 为什么可以这样做:cudaSetDevice(0); cudaMalloc(ptr,16); cudaSetDevice(1) 在 2 GPU 机器上? (所有命令均未返回错误;已检查) 我记得将设备设置在保持状态为 barf 的上下文中。这变得更加灵活了吗?分配的内存会发生什么? (这里使用 CUDA 5.5) 调用 cudaSetDevice(deviceId) 创建一个新的上下文,如果'deviceID'不存在上下文。在最后一次使用 cudaSetDevice 设置的设备上发生对 cudaMalloc 或内核启动的调用;所以在您之前的评论中发生的情况是,ptr 在设备 0 上分配,随后的调用将转到设备 1。如果您现在在内核中使用 ptr,这将失败(如果未启用统一虚拟寻址),因为它是分配在另一台设备上。但是如果你再次调用 cudaSetDevice(0),这次没有创建上下文,你可以再次访问 ptr。【参考方案2】:

实际上,cudaSetDevice() 与创建检索上下文并不完全一样,就像调用了 cuCtxCreate() 一样。它非常相似,但是 CUDA 运行时 API 使用了 special 上下文。此上下文称为设备的主要上下文。有specific driver API functions 用于处理这种特殊情况:

CUresult cuDevicePrimaryCtxGetState ( CUdevice dev, unsigned int* flags, int* active );
CUresult cuDevicePrimaryCtxRelease ( CUdevice dev );
CUresult cuDevicePrimaryCtxReset ( CUdevice dev );
CUresult cuDevicePrimaryCtxRetain ( CUcontext* pctx, CUdevice dev );
CUresult cuDevicePrimaryCtxSetFlags ( CUdevice dev, unsigned int  flags );

那么,为什么要实现cudaSetDevice() 的等价物,这将涉及(忽略错误检查)类似:

CUcontext* primary_context;
cuDevicePrimaryCtxRetain(&primary_context, device_id);
cuCtxSetCurrent(primary_context);

注意事项:

您应该在某个时候调用 Release 函数来减少引用计数;但是 - 不要在没有设置另一个当前上下文的情况下这样做。 您可以替换-替换-替换当前上下文,或者从上下文堆栈中推送并最终弹出当前上下文。替换在堆栈顶部工作(所以它就像先弹出,然后压入)。

【讨论】:

以上是关于等效于 cudaSetDevice 的 CUDA 驱动程序 API的主要内容,如果未能解决你的问题,请参考以下文章

等效于 CUDA 中的 async_work_group_copy

等效于 CUDA 中的屏障(CLK_GLOBAL_MEM_FENCE)

Cuda learn record two

在 cudaStreamSynchronize 之前我需要 cudaSetDevice 吗?

cudaSetDevice() 编号是不是跨进程一致?

从 CUDA 中的指针加载图像并导出 dll