CUDA驱动程序API中等同于cudaSetDevice的函数是什么?

3

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

我正在研究驱动程序API,但找不到等效的函数。我可以做的是

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

除了设置设备外,它还创建了一个上下文,所以并不等同。运行时APIcudaSetDevice本身不会创建上下文。在运行时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很高兴。 - ritter
换句话说:使用驱动程序API时,在使用CUDA-aware MPI时必须创建上下文。 - ritter
在运行时API中,从CUDA 4.0开始,如果在相关设备上不存在上下文,则cudaSetDevice会创建一个上下文。 - talonmies
2个回答

5
您可以在有关驱动程序API的编程指南附录中找到相关信息,但简短版本如下:
- `cuCtxCreate` 作为第一个 `cudaSetDevice` 调用(它在驱动程序上下文堆栈上创建上下文) - `cuCtxPushCurrent()` 和 `cuCtxPopCurrent()` 对(或 `cuCtxSetCurrent` 取决于您使用的API版本)作为任何后续 `cudaSetDevice` 调用(即将先前创建的上下文推送或选择为所有后续API调用的活动上下文,直到上下文从驱动程序上下文堆栈中弹出或取消选择)。

1
不要忘记 cuCtxSetCurrent(),它自 CUDA 4.x 起取代了 cuCtxPush/PopCurrent。它更接近于 cudaSetDevice(),只是它以上下文为参数而不是设备ID,并且在第一次调用时不会创建新的上下文。 - kunzmi
你是对的。cudaSetDevice会创建一个上下文。我通过查看分配给进程的虚拟内存量来验证了这一点。 - ritter
为什么在一台有两个GPU的机器上可以执行以下操作:cudaSetDevice(0); cudaMalloc(ptr,16); cudaSetDevice(1)?(所有命令都没有返回错误;已检查) - ritter
我记得将设备设置在一个保存状态的上下文中以进行强制退出。这变得更加灵活了吗?分配的内存会发生什么?(这里使用的是CUDA 5.5) - ritter
1
调用cudaSetDevice(deviceId)会创建一个新的上下文,如果‘deviceID’没有上下文存在。随后对cudaMalloc或内核启动的调用将在最后使用cudaSetDevice设置的设备上进行;因此,在您之前的评论中发生的情况是,ptr在设备0上分配,随后的调用将转到设备1。如果您现在在内核中使用ptr,则会失败(如果未启用统一虚拟寻址),因为它在另一个设备上分配。但是,如果再次调用cudaSetDevice(0),这次不会创建上下文,您可以再次访问ptr。 - kunzmi

1
实际上,cudaSetDevice() 不完全像创建或检索上下文一样,就像调用 cuCtxCreate() 一样。它们非常相似,但是CUDA运行时API使用了一个特殊的上下文。这个上下文被称为设备的主要上下文。有特定的驱动程序API函数可以用于处理此特殊上下文:
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函数来减少引用计数;但是-不要在没有设置另一个当前上下文的情况下这样做。
  • 你可以替换-替换-替换当前上下文,或者从上下文堆栈中推送然后最终弹出当前上下文。替换作用于堆栈顶部(所以它就像弹出,然后推送)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接