【发布时间】:2018-07-18 11:06:53
【问题描述】:
注意:该问题已更新以解决 cmets 中提出的问题,并强调该问题的核心是关于 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);
但是,关于这些句柄如何与驱动程序和运行时上下文以及多个线程和设备进行互操作,有许多微妙的细节。该文档列出了一些关于上下文处理的零散细节:
CUDA 编程指南http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#context中的上下文一般描述
多个上下文的处理,如http://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html#multiple-contexts 的 CUDA 最佳实践指南中所述
运行时和驱动程序 API 之间的上下文管理差异,在http://docs.nvidia.com/cuda/cuda-driver-api/driver-vs-runtime-api.html进行了解释
http://docs.nvidia.com/cuda/cublas/index.html#cublas-context 上对 CUBLAS 上下文/句柄的一般描述以及http://docs.nvidia.com/cuda/cublas/index.html#thread-safety2 上的线程安全
但是,有些信息似乎不是完全最新的(例如,我认为应该使用cuCtxSetCurrent 而不是cuCtxPushCurrent 和cuCtxPopCurrent?),其中一些似乎是以前的“主上下文”处理通过驱动程序 API 公开,有些部分过于简单化,因为它们只显示最简单的使用模式,只对多线程做出模糊或不完整的陈述,或者不能应用于“句柄”的概念在运行时库中使用。
我的目标是实现一个运行时库,它提供自己的“句柄”类型,并允许在上下文处理和线程安全方面与其他运行时库等效的使用模式。
如果库可以在内部单独使用 Runtime API 实现,事情可能很清楚:上下文管理完全由用户负责。如果他创建自己的驱动程序上下文,则将适用documentation about the Runtime- and Driver context management 中所述的规则。否则,运行时 API 函数将负责处理主要上下文。
但是,在某些情况下,库必须在内部使用驱动程序 API。例如,为了将 PTX 文件加载为CUmodule 对象,并从中获取CUfunction 对象。当库应该 - 对于用户 - 表现 像 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,但仅在上下文是主要上下文时:
void destroyHandle(Handle handle)
{
if (handle.usingPrimaryContext)
{
cuDevicePrimaryCtxRelease(handle.device);
}
}
从我目前的实验来看,这似乎展示了与 CUBLAS 句柄相同的行为,例如。但是我彻底测试的可能性是有限的,因为我只有一个设备,因此无法测试关键情况,例如具有两个上下文,一个用于两个设备中的每一个。
所以我的问题是:
- 是否有任何既定的模式来实现这样的“句柄”?
- 是否有任何使用模式(例如,使用多个设备和每个设备一个上下文)无法涵盖在上面概述的方法中,但会涵盖在“句柄”实现中CUBLAS?
- 更笼统地说:对于如何改进当前的“Handle”实施有什么建议吗?
- 修辞:CUBLAS 句柄处理的源代码是否在某处可用?
(我还查看了context handling in tensorflow,但我不确定是否可以从中得出有关如何为运行时库实现句柄的建议...)
(此处删除了“更新”,因为它是为响应 cmets 而添加的,应该不再相关)
【问题讨论】:
-
我相信这个问题背后的整个前提是不正确的。 AFAIK,cublas(有问题的示例库)是一个完全简单的运行时 API 库,它完全依赖于标准运行时 API 惰性上下文管理行为。它本身从不进行任何显式的上下文管理,不尝试与驱动程序 API 的互操作性相关的任何事情,并且句柄不包含任何上下文信息。见pastebin.com/8zHi1WgV
-
我不相信你的解释是正确的。不要将默认运行时 API 的功能与 cublas 中内置的功能相混淆。每当具有活动 CUDA 上下文的线程初始化运行时 API 时,运行时 API 都会绑定到该上下文。我链接到的内容表明,所有 cublas 都会在 init 调用的上下文中分配内存。如果两个线程启动驱动程序 API 上下文,然后初始化 cublas,则生成的句柄将不同,因为它们存在于不同的上下文中
-
所以你的第二条评论更接近目标。所有这些只是默认的运行时 API 行为,库中没有内置一些复杂的逻辑
-
这是正确的设计模式吗?绝对不。任何运行时 API 库的正确设计是本身不执行显式上下文管理。曾经。这就是所有“参考”NVIDIA 库的工作方式,也是您的工作方式。默认的运行时 API 行为(及其内置的线程安全)涵盖了 95% 的典型用例,另外 5% 是程序员选择通过驱动程序 API 自己管理上下文的地方。在这种情况下,必须正确管理事物的是程序员,而不是库
-
不,图书馆根本不需要了解上下文。这很简单——如果有上下文,运行时 API 会自动绑定到它。如果没有,它将创建一个主上下文,其他线程可以通过相同的 API 绑定到该上下文(这在运行时 API 中自动发生)。默认情况下,在上下文中创建的任何内容都不能移植到其他上下文中。如果您通过驱动程序 API 显式创建两个上下文,则这两个上下文中的 anything 不能在它们之间移植。这包括在这些上下文中通过运行时 API 发生的任何事情
标签: cuda api-design jcuda cuda-context