如何实现 CUDA 驱动程序 API 库的句柄



注意:该问题已更新,以解决评论中提出的问题,并强调问题的核心是关于运行时和驱动程序 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);

但是,有许多关于这些句柄如何与驱动程序和运行时上下文以及多个线程和设备进行互操作的微妙细节。该文档列出了有关上下文处理的几个分散的详细信息:

  • 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#thread-safety2

但是,有些信息似乎并不完全是最新的(例如,我认为应该使用cuCtxSetCurrent而不是cuCtxPushCurrentcuCtxPopCurrent?),其中一些似乎是在通过驱动程序API公开"主要上下文"处理之前的时间,有些部分过于简化,因为它们只显示最简单的使用模式, 仅对多线程进行模糊或不完整的陈述,或者不能应用于运行时库中使用的"句柄"概念。


我的目标是实现一个运行时库,该库提供自己的"句柄"类型,并允许在上下文处理和线程安全性方面等效于其他运行时库的使用模式。

对于库可以在内部仅使用运行时 API实现的情况,事情可能很清楚:上下文管理完全由用户负责。如果他创建了自己的驱动程序上下文,则将应用有关运行时和驱动程序上下文管理的文档中所述的规则。否则,运行时 API 函数将负责处理主要上下文。

但是,可能存在库在内部必须使用驱动程序 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 的"句柄"实现所涵盖?
  • 更一般地说:对于如何改进当前的"Handle"实现,是否有任何建议?
  • 修辞:CUBLAS 句柄处理的源代码是否在某处可用?

(我还看了一下 tensorflow 中的上下文处理,但我不确定是否可以从中得出有关如何实现运行时库句柄的建议......

(此处已删除"更新",因为它是为了响应评论而添加的,应该不再相关)

抱歉我没有早点注意到这个问题 - 因为我们可能已经在这方面进行了一些合作。另外,我不太清楚这个问题是否属于这里,关于代码审查。SX 或程序员。SX,但让我们忽略所有这些。

我现在已经完成了你的目标,而且可能更广泛。因此,我可以提供一个如何处理"句柄"的示例,此外,建议根本不需要实现这一点的前景。

该库是cuda-api-wrappers的扩展,也涵盖了Driver API和NVRTC;它还没有发布级,但它在这个分支上处于测试阶段。

现在,回答您的具体问题:

围绕原始"句柄"编写类的模式

是否有任何已建立的模式来实现这样的"句柄"?

是的。如果您阅读:

句柄、指针和引用之间有什么区别

您会注意到句柄被定义为"对对象的不透明引用"。它与指针有一些相似之处。因此,一个相关的模式是 PIMPL 习惯用法的变体:在常规 PIMPL 中,您编写一个实现类,而面向外部的类仅包含指向实现类的指针,并将方法调用转发给它。当某些第三方库或驱动程序中的不透明对象具有不透明句柄时,可以使用句柄将方法调用转发到该实现。

这意味着,面向外部的类不是句柄,它表示您具有句柄的对象。

通用性和灵活性

是否有任何使用模式(例如,使用多个设备和每个设备一个上下文)无法通过上面概述的方法涵盖,但会被 CUBLAS 的"句柄"实现所涵盖?

我不确定 CUBLAS 到底在引擎盖下做了什么(老实说,我几乎从未使用 CUBLAS),但如果它设计和实施得当,它会 创建自己的上下文,并尽量不要影响代码的其余部分,即它应该这样做:

  1. 将我们的 CUBLAS 上下文推送到堆栈的顶部
  2. 做实际工作
  3. 弹出上下文堆栈的顶部。

您的类不这样做。

更一般地说:对于如何改进当前的"Handle"实现,是否有任何建议?

是的:

  • 只要有可能且相关,请使用 RAII。如果您的创建代码分配了资源(例如通过 CUDA 驱动程序) - 您返回的对象的析构函数应该安全地释放这些资源。
  • 允许引用类型和值类型使用句柄,即它可能是我创建的句柄,但它也可能是我从其他地方获得的句柄,不是我的责任。如果您将其留给用户来释放资源,这是微不足道的,但如果您承担该责任,则有点棘手
  • 假设如果存在任何当前上下文,那就是句柄需要使用的上下文。谁说的?至少,如果用户愿意,可以让用户传入上下文。
  • 避免自己编写其中的低级部分,除非你真的必须这样做。你很可能会错过一些东西(推送和弹出并不是你可能缺少的唯一东西),并且你重复了很多实际上是通用的,而不是特定于你的应用程序或库的工作。我在这里可能有偏见,但您现在可以对 CUDA 上下文、流、模块、设备等使用漂亮的、RAII-ish 的包装器,甚至不知道任何原始句柄。

修辞:CUBLAS 句柄处理的源代码是否在某处可用?

据我所知,NVIDIA尚未发布它。

相关内容

  • 没有找到相关文章

最新更新