注意:该问题已更新以解决评论中提出的问题,并强调该问题的核心是关于 Runtime- 和 Driver API 之间的相互依赖关系
CUDA 运行时库(如 CUBLAS 或 CUFFT)通常使用“句柄”的概念来总结此类库的状态和上下文。使用模式非常简单:
// Create a handle
cublasHandle_t handle;
cublasCreate(&handle);
// Call some functions, always passing in the handle as the first argument
cublasSscal(handle, ...);
// When done, destroy the handle
cublasDestroy(handle);
但是,关于这些句柄如何与驱动程序和运行时上下文以及多个线程和设备进行互操作,有许多微妙的细节。该文档列出了有关上下文处理的几个分散的详细信息:
在http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#context的 CUDA 编程指南中对上下文的一般描述
运行时和驱动程序 API 之间的上下文管理差异,在http://docs.nvidia.com/cuda/cuda-driver-api/driver-vs-runtime-api.html中进行了解释
http://docs.nvidia.com/cuda/cublas/index.html#cublas-context的 CUBLAS 上下文/句柄的一般描述及其线程安全http://docs.nvidia.com/cuda/cublas/index .html#thread-safety2
然而,一些信息似乎并不完全是最新的(例如,我认为应该使用cuCtxSetCurrent
而不是cuCtxPushCurrent
and cuCtxPopCurrent
?),其中一些似乎来自“主要上下文”处理通过驱动程序 API 公开之前的时间,并且某些部分过于简单化,因为它们仅显示最简单的使用模式,仅对多线程进行模糊或不完整的陈述,或者不能应用于运行时库中使用的“句柄”概念。
我的目标是实现一个运行时库,它提供自己的“句柄”类型,并允许在上下文处理和线程安全方面与其他运行时库等效的使用模式。
对于库可以在内部仅使用Runtime API实现的情况,事情可能很清楚:上下文管理完全由用户负责。如果他创建自己的驱动程序上下文,则将适用文档中有关运行时和驱动程序上下文管理的规则。否则,运行时 API 函数将负责处理主要上下文。
但是,可能存在库内部必须使用Driver API的情况。例如,为了将 PTX 文件作为CUmodule
对象加载,并从中获取CUfunction
对象。当库应该(对于用户而言)表现得像运行时库,但内部必须使用驱动程序API 时,就会出现一些问题,即必须如何“在后台”实现上下文处理。
到目前为止,我所想出的都在这里勾勒出来。
(它是“伪代码”,因为它省略了错误检查和其他细节,并且......所有这些都应该在 Java 中实现,但这在这里不应该相关)
1. “句柄”基本上是一个包含以下信息的类/结构:
class Handle
{
CUcontext context;
boolean usingPrimaryContext;
CUdevice device;
}
2.创建时,必须涵盖两种情况: 可以在调用线程的驱动程序上下文为当前时创建。在这种情况下,它应该使用这个上下文。否则,它应该使用当前(运行时)设备的主要上下文:
Handle createHandle()
{
cuInit(0);
// Obtain the current context
CUcontext context;
cuCtxGetCurrent(&context);
CUdevice device;
// If there is no context, use the primary context
boolean usingPrimaryContext = false;
if (context == nullptr)
{
usingPrimaryContext = true;
// Obtain the device that is currently selected via the runtime API
int deviceIndex;
cudaGetDevice(&deviceIndex);
// Obtain the device and its primary context
cuDeviceGet(&device, deviceIndex);
cuDevicePrimaryCtxRetain(&context, device));
cuCtxSetCurrent(context);
}
else
{
cuCtxGetDevice(device);
}
// Create the actual handle. This might internally allocate
// memory or do other things that are specific for the context
// for which the handle is created
Handle handle = new Handle(device, context, usingPrimaryContext);
return handle;
}
3.当调用库的内核时,相关句柄的上下文对于调用线程来说是当前的:
void someLibraryFunction(Handle handle)
{
cuCtxSetCurrent(handle.context);
callMyKernel(...);
}
在这里,有人可能会争辩说,调用者负责确保所需的上下文是当前的。但是,如果句柄是为主要上下文创建的,那么该上下文将自动变为当前上下文。
4.当句柄被销毁时,这意味着cuDevicePrimaryCtxRelease
必须调用它,但前提是上下文是主上下文:
void destroyHandle(Handle handle)
{
if (handle.usingPrimaryContext)
{
cuDevicePrimaryCtxRelease(handle.device);
}
}
例如,从我迄今为止的实验来看,这似乎暴露了与 CUBLAS 句柄相同的行为。但是我彻底测试这个的可能性是有限的,因为我只有一个设备,因此无法测试关键情况,例如有两个上下文,两个设备中的每一个都有一个。
所以我的问题是:
- 是否有任何既定的模式来实现这样的“句柄”?
- 是否有任何使用模式(例如,使用多个设备和每个设备一个上下文)无法被上面概述的方法覆盖,但会被 CUBLAS 的“句柄”实现覆盖?
- 更笼统地说:是否有关于如何改进当前“处理”实施的建议?
- 修辞:CUBLAS 句柄处理的源代码在某处可用吗?
(我还查看了tensorflow 中的上下文处理,但我不确定是否可以从中得出有关如何为运行时库实现句柄的建议......)
(此处删除了“更新”,因为它是根据评论添加的,应该不再相关)