注意:该问题已更新,以解决注释中提出的问题,并强调问题的核心是有关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);
但是,关于这些句柄如何与驱动程序和运行时上下文以及多个线程和设备进行互操作的方法,有许多细微的细节。该文档列出了有关上下文处理的几个分散的详细信息:
但是,在某些情况下,库在内部必须使用Driver API
。例如,为了将PTX文件加载为
CUmodule
对象,并从中获取
CUfunction
对象。当库应该-对于用户-
behave像Runtime库一样,但是在内部必须使用Driver 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(...);
}
这里,有人可能会争辩说,调用方有责任确保所需的上下文是最新的。但是,如果为primary
上下文创建了句柄,则该上下文将自动变为当前。 4。
句柄销毁时,这意味着必须调用cuDevicePrimaryCtxRelease
,但是当上下文是主要上下文时,则only
:
void destroyHandle(Handle handle)
{
if (handle.usingPrimaryContext)
{
cuDevicePrimaryCtxRelease(handle.device);
}
}
例如,到目前为止,根据我的实验,此似乎公开了与CUBLAS句柄相同的行为。但是我进行彻底测试的可能性是有限的,因为我只有一个设备,因此无法测试关键案例,例如有两个上下文,一个用于两个设备。
所以我的问题是:
- 是否存在用于实现这种“句柄”的既定模式?
- [上面是否有使用模式(例如,多个设备和一个设备一个上下文)可以not覆盖,但可以用CUBLAS的“句柄”实现覆盖]]
- 更笼统地说:是否有关于如何改进当前“句柄”实现的建议?
- 修辞:CUBLAS句柄处理的源代码在某处可用吗?
((我也看过context handling in tensorflow,但是我不确定是否可以从中得出有关如何为运行时库实现句柄的建议...]
(此处已删除“更新”,因为它是为回应评论而添加的,并且不再相关)注:该问题已更新,以解决注释中提出的问题,并强调该问题的核心是有关运行时之间的相互依赖性-...
< [
在我看来,您正在寻找一个简单的面向对象的,
RAII的包装,
cublasHandle_t
的包装类似于(用半伪代码):
class handle
{
cublasHandle_t handle_;
public:
handle() { cublasCreate(& handle_); }
~handle() { cublasDestroy(handle_); }
template <class F, class Args...>
auto operator()(F && f, Args&&... args)
{
std::forward<F>(f)(handle_, std::forward<Args>(args)...);
}
};
// Usage:
handle h; // will be cleaned up automatically
h(cublasSscall, ...); // -> cublasSscall(handle_, ...);
我为我必须在C ++程序中使用(从外部库中使用的)任何C样式类型执行此操作。它具有很大的优势:
它可以自动执行资源管理,无需手动清理。- 不太冗长,因为您可以将参数存储在包装器内,从而避免每次将参数传递给库函数调用。
[您可以添加额外的钩子,以使包装器在其余C ++代码库中正常运行,例如,如果分配失败,则cudaMalloc包装器可能会引发异常,而不是返回错误代码...[如果您希望我可以复制粘贴此方法的完整生产示例,该示例将用于cudaEvent_t,cudaStream_t等(不幸的是,无法链接到存储库)。