Mar*_*o13 15 cuda api-design jcuda
注意:问题已更新,以解决评论中提出的问题,并强调问题的核心是运行时和驱动程序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);
Run Code Online (Sandbox Code Playgroud)
但是,有许多关于这些句柄如何与Driver-和Runtime上下文以及多个线程和设备进行互操作的细微细节.该文档列出了有关上下文处理的若干分散细节:
"CUDA编程指南"中对上下文的一般描述,请访问http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#context
处理多个上下文,如"CUDA最佳实践指南"中所述,网址为http://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html#multiple-contexts
运行时和驱动程序API之间的上下文管理差异,请参阅http://docs.nvidia.com/cuda/cuda-driver-api/driver-vs-runtime-api.html
CUBLAS上下文的一般描述为/处理http://docs.nvidia.com/cuda/cublas/index.html#cublas-context和他们的线程安全http://docs.nvidia.com/cuda/cublas/index的.html#线程安全2
然而,一些信息似乎并不完全是最新的(例如,我认为应该使用cuCtxSetCurrent
而不是cuCtxPushCurrent
和cuCtxPopCurrent
?),其中一些似乎是在"主要上下文"处理通过驱动程序API公开之前的一段时间,有些部分过于简单,因为它们只显示最简单的使用模式,只制作关于多线程的模糊或不完整的语句,或者不能应用于运行时库中使用的"句柄"的概念.
我的目标是实现一个提供自己的"句柄"类型的运行时库,并允许在上下文处理和线程安全方面与其他运行时库等效的使用模式.
对于只能使用Runtime API在内部实现库的情况,事情可能很清楚:上下文管理完全由用户负责.如果他创建自己的驱动程序上下文,则将适用有关运行时和驱动程序上下文管理的文档中陈述的规则.否则,Runtime API函数将负责主要上下文的处理.
但是,可能存在库在内部必须使用Driver API的情况.例如,为了将PTX文件作为CUmodule
对象加载,并从中获取CUfunction
对象.当库 - 应该 - 对于用户 - 表现得像运行时库,但内部必须使用驱动程序 API时,会出现一些关于如何在"引擎盖下"实现上下文处理的问题.
到目前为止我想到的是在这里勾勒出来的.
(它是"伪代码",因为它省略了错误检查和其他细节,并且...所有这些应该用Java实现,但这不应该在这里相关)
1. "句柄"基本上是一个包含以下信息的类/结构:
class Handle
{
CUcontext context;
boolean usingPrimaryContext;
CUdevice device;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
3.在调用库的内核时,关联句柄的上下文对于调用线程是最新的:
void someLibraryFunction(Handle handle)
{
cuCtxSetCurrent(handle.context);
callMyKernel(...);
}
Run Code Online (Sandbox Code Playgroud)
在这里,人们可以争辩说调用者负责确保所需的上下文是最新的.但是,如果为主要上下文创建了句柄,则此上下文将自动变为当前上下文.
4.当句柄被销毁时,这意味着cuDevicePrimaryCtxRelease
必须被调用,但仅当上下文是主要上下文时:
void destroyHandle(Handle handle)
{
if (handle.usingPrimaryContext)
{
cuDevicePrimaryCtxRelease(handle.device);
}
}
Run Code Online (Sandbox Code Playgroud)
从我到目前为止的实验来看,这似乎暴露了与CUBLAS句柄相同的行为.但我彻底测试这种情况的可能性是有限的,因为我只有一个设备,因此无法测试关键情况,例如有两个上下文,两个设备各一个.
所以我的问题是:
(我还看了一下tensorflow中的上下文处理,但我不确定是否可以从中获取有关如何为运行时库实现句柄的建议...)
(这里删除了"更新",因为它是为了回应评论而添加的,并且不再相关)
很抱歉我没有早点注意到这个问题 - 因为我们可能在这方面进行了一些合作。另外,我不太清楚这个问题是否属于这里,在 codereview.SX 还是在programmers.SX,但让我们忽略所有这些。
我现在已经完成了您想要做的事情,而且可能更普遍。因此,我可以提供一个如何处理“句柄”的示例,而且还提出了根本不必实现此操作的前景。
该库是cuda-api-wrappers的扩展,还涵盖了驱动程序 API 和 NVRTC;它还不是发布级,但在这个分支上处于测试阶段。
现在,回答您的具体问题:
是否有任何既定的模式来实现这样的“句柄”?
是的。如果您阅读:
您会注意到句柄被定义为“对对象的不透明引用”。它与指针有一些相似之处。因此,一个相关的模式是PIMPL 习惯用法的变体:在常规 PIMPL 中,您编写一个实现类,并且面向外的类仅保存指向实现类的指针并将方法调用转发给它。当您在某些第三方库或驱动程序中拥有不透明对象的不透明句柄时,您可以使用该句柄将方法调用转发到该实现。
这意味着,您的面向外的类不是句柄,它代表您拥有句柄的对象。
是否有任何使用模式(例如,多个设备和每个设备一个上下文)无法用上面概述的方法覆盖,但可以用 CUBLAS 的“句柄”实现覆盖?
我不确定 CUBLAS 到底在幕后做什么(说实话,我几乎从未使用过 CUBLAS),但如果它设计良好并实现良好,它将创建自己的上下文,并尽量不影响你的代码的其余部分,即它总是会这样做:
你们班不这样做。
更一般地说:对于如何改进当前的“Handle”实施有什么建议吗?
是的:
反问:CUBLAS 句柄处理的源代码在某处可用吗?
据我所知,NVIDIA 尚未发布它。