15

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

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

然而,一些信息似乎并不完全是最新的(例如,我认为应该使用cuCtxSetCurrent而不是cuCtxPushCurrentand 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 中的上下文处理,但我不确定是否可以从中得出有关如何为运行时库实现句柄的建议......)

(此处删除了“更新”,因为它是根据评论添加的,应该不再相关)

4

1 回答 1

1

很抱歉,我没有早点注意到这个问题——因为我们可能在这方面进行了一些合作。另外,我不太清楚这个问题是属于这里,codereview.SX 还是programmers.SX,但让我们忽略所有这些。

我现在已经完成了你打算做的事情,而且可能更普遍。因此,我可以提供一个如何处理“句柄”的示例,此外,还可以提出完全不必实现这一点的前景。

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

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

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

是否有任何既定的模式来实现这样的“句柄”?

是的。如果您阅读:

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

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

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

通用性和灵活性

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

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

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

你的班级没有这样做。

更笼统地说:是否有关于如何改进当前“处理”实施的建议?

是的:

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

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

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

于 2020-10-09T08:52:23.337 回答