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

时间:2018-02-08 01:19:40

标签: multithreading cuda api-design jcuda

注意:问题已经更新,以解决评论中提出的问题,并强调问题的核心是关于运行时和驱动程序API之间的相互依赖性 < / p>

CUDA运行时库(如CUBLAS或CUFFT)通常使用&#34; handle&#34;总结了这种图书馆的状态和背景。使用模式非常简单:

// 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-和Runtime上下文以及多个线程和设备进行互操作的细微细节。该文档列出了有关上下文处理的若干分散细节:

但是,有些信息似乎并不完全是最新的(例如,我认为应该使用cuCtxSetCurrent代替cuCtxPushCurrentcuCtxPopCurrent?),其中一些似乎来自&#34;主要背景&#34;之前的时间处理是通过驱动程序API公开的,有些部分过于简单,因为它们只显示最简单的使用模式,只做出关于多线程的模糊或不完整的陈述,或者不能应用于&#34; handle&#34;在运行时库中使用。

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

对于只能使用 Runtime API 在内部实现库的情况,事情可能很清楚:上下文管理完全由用户负责。如果他创建了自己的驱动程序上下文,则http://docs.nvidia.com/cuda/cublas/index.html#thread-safety2中规定的规则将适用。否则,Runtime API函数将负责处理主要上下文。

但是,可能存在库内部必须使用驱动程序API 的情况。例如,为了将PTX文件加载为CUmodule个对象,并从中获取CUfunction个对象。当库应该 - 对于用户 - 表现就像 Runtime 库一样,但内部必须使用 Driver API时,会出现一些问题如何实施上下文处理&#34;引擎盖&#34;。

到目前为止,我想到的是草图。

(它是&#34;伪代码&#34;因为它省略了错误检查和其他细节,并且......所有这些都应该用Java实现,但这里不应该相关)

1。&#34;处理&#34;基本上是一个包含以下信息的类/结构:

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句柄相同的行为,例如。但是我彻底测试它的可能性是有限的,因为我只有一个设备,因此无法测试关键的情况,例如有两个上下文,一个用于两个设备。

所以我的问题是:

  • 是否有任何既定模式来实施这样的&#34; Handle&#34;?
  • 是否有任何使用模式(例如,使用多个设备和每个设备一个上下文),可以覆盖上面描述的方法,但是将覆盖&#34;句柄& #34; CUBLAS的实现?
  • 更一般地说:是否有任何关于如何改善当前&#34; Handle&#34;的建议?实施?
  • 修辞:CUBLAS句柄处理的源代码是否在某处可用?

(我也看过documentation about the Runtime- and Driver context management,但我不确定是否可以从中获取有关如何为运行时库实现句柄的建议......)

(An&#34;更新&#34;已在此处删除,因为它是为了回应评论而添加的,并且不再相关)

1 个答案:

答案 0 :(得分:1)

很抱歉,我没有很快注意到这个问题-因为我们可能在此问题上进行了一些合作。另外,对于我来说,这个问题是否属于codereview.SX还是程序员.SX还不是很清楚,但让我们忽略所有这些问题。

我现在已经完成了您打算做的工作,而且可能更笼统了。因此,我既可以提供有关处理“句柄”的示例,也可以建议完全不必实现此句柄的前景。

该库是cuda-api-wrappers的扩展,也涵盖了驱动程序API和NVRTC;它尚未发布,但仍处于测试阶段this branch

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

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

是否存在用于实现这种“句柄”的既定模式?

是的。如果您阅读:

What is the difference between: Handle, Pointer and Reference

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

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

通用性和灵活性

是否存在上面概述的方法无法涵盖的使用模式(例如,具有多个设备且每个设备一个上下文),但将由CUBLAS的“句柄”实现覆盖?

我不确定CUBLAS到底是做什么的(老实说,我几乎从未使用过CUBLAS),但是如果设计和实施得当,它会 创建自己的上下文,并尽量不影响其余的代码,即可以:

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

您的班级不这样做。

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

是:

  • 请尽可能使用RAII。如果您的创建代码分配了资源(例如,通过CUDA驱动程序)-您返回的对象的析构函数应安全释放这些资源。
  • 允许同时使用引用类型和值类型的Handle,即它可能是我创建的handle,但也可能是我从其他地方获得的handle,这不是我的责任。如果让用户来释放资源,这将是微不足道的,但是如果您承担这种责任,这会有些棘手
  • 您假设如果有任何当前上下文,那就是您的句柄需要使用的上下文。谁说的?至少,如果需要,让用户传递上下文。
  • 除非确实需要,否则请避免自己编写底层的内容。您很可能会错过一些事情(推拉式弹出不是您可能会丢失的唯一一件事),并且您正在重复很多实际上是通用的并且不是特定于您的应用程序或库的工作。我在这里可能会有失偏颇,但是您现在可以在CUDA上下文,流,模块,设备等中使用不错的,类似于RAII的包装,而无需了解任何原始句柄。

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

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