如何将CUDA驱动程序API与CUDA运行时API混合使用?

3

根据CUDA文档所述:

  1. 如果通过驱动程序API创建并使上下文成为当前上下文,则随后的运行时调用将接管此上下文而不是创建新的上下文。
  2. 如果已初始化运行时(如CUDA Runtime中所述),则可以使用cuCtxGetCurrent()检索在初始化期间创建的上下文。此上下文可以由随后的驱动程序API调用使用。

我能够做到第一个要点。 我可以从cuda驱动程序创建上下文。 然后,我可以在不调用 cudaSetDevice()的情况下使用cuda运行时函数,该函数会隐式地创建一个新的主上下文。

但是,我想使用第二个选项。 即首先初始化运行时,然后执行 cuCtxGetCurrent()并在cuda驱动程序api中使用它。 这根本不起作用。 我总是会引发错误,称上下文已被销毁或无效。 我做错了什么?

以下是我的示例代码:

#define CUDA_DRIVER_API
#include <cuda.h>
#include <cuda_runtime.h>
#include <helper_cuda.h>
#include <iostream>
CUcontext check_current_ctx()
{
    CUcontext context{0};
    unsigned int api_ver;
    checkCudaErrors(cuCtxGetCurrent(&context));
    fprintf(stdout, "current context=%p\n", context);
    checkCudaErrors( cuCtxGetApiVersion(context, &api_ver));
    fprintf(stdout, "current context api version = %d\n", api_ver);
    return context;
}
auto inital_runtime_context()
{
    int current_device = 0;
    int device_count = 0;
    int devices_prohibited = 0;
    CUcontext current_ctx{0};

    cudaDeviceProp deviceProp;
    checkCudaErrors(cudaGetDeviceCount(&device_count));;
    if (device_count == 0) {
        fprintf(stderr, "CUDA error: no devices supporting CUDA.\n");
        exit(EXIT_FAILURE);
    }

    // Find the GPU which is selected by Vulkan
    while (current_device < device_count) {
        cudaGetDeviceProperties(&deviceProp, current_device);
        if ((deviceProp.computeMode != cudaComputeModeProhibited)) {
            checkCudaErrors(cudaSetDevice(current_device));
            checkCudaErrors(cudaGetDeviceProperties(&deviceProp, current_device));
            printf("GPU Device %d: \"%s\" with compute capability %d.%d\n\n",
                current_device, deviceProp.name, deviceProp.major,
                deviceProp.minor);
            CUcontext current_ctx;
            cuCtxGetCurrent(&current_ctx);
            std::cout << "current_ctx=" << current_ctx << "\n";
            return current_device;

        } else {
            devices_prohibited++;
        }

        current_device++;
    }

    if (devices_prohibited == device_count) {
        fprintf(stderr,
            "CUDA error:"
            " No Vulkan-CUDA Interop capable GPU found.\n");
        exit(EXIT_FAILURE);
    }

    return -1;
}
void test_runtime_driver_op()
{
    inital_runtime_context();
    check_current_ctx();

}

它报告如下:
GPU Device 0: "GeForce RTX ..." with compute capability 7.5

current_ctx=0x6eb220
current context=0x6eb220
CUDA error at ... code=201(CUDA_ERROR_INVALID_CONTEXT) "cuCtxGetApiVersion(context, &api_ver)" 


你可能需要实际包含一个API调用,比如 cudafree(0),以使运行时API创建上下文。有可能你现有的代码没有强制进行延迟上下文创建。 - talonmies
@talonmies 非常感谢!这真的有效。但是文档是错误的吗?因为根据文档,cudaSetDevice()应该已经创建了cuda上下文。你能否把它总结成一个答案?然后我会接受它。 - Wang
我不认为文档有错,但是运行时API中上下文创建的确切时间和方式一直有些模糊。 - talonmies
这里有一些进一步的“轻松阅读”:CUDA运行时当前设备和驱动程序上下文堆栈如何交互? - einpoklum
2个回答

4
您遇到错误的原因在于,至少在这种情况下,在使用驱动程序API绑定上下文时,尚未发生延迟的运行时API上下文创建。确保您获取由运行时创建的上下文的规范方法始终是:
cudaSetDevice(current_device);
cudaFree(0);

关于这一点,文档一直存在歧义,而且语义似乎随着时间的推移而略微改变,但是这种调用方式对我来说总是有效的。


1

@talonmies的回答是正确的,它可以起作用,但是 - 如果你想从驱动程序方面纠正问题,你可以尝试这样做:

// ...
CUcontext check_current_ctx()
{
    CUcontext context{0};
    checkCudaErrors( cuCtxGetCurrent(&context) );
    CUdevice device;
    checkCudaErrors( cuCtxGetDevice(&device) );
    CUcontext primary_context;
    checkCudaErrors( cuDevicePrimaryCtxRetain(&primary_context, device) );
    unsigned int flags;
    int active;
    CUresult primary_state_check_result = 
        cuDevicePrimaryCtxGetState(device, unsigned &flags, &active);

    // etc. etc.
}

现在您将能够检查:
  1. 当前设备的当前上下文是否为主要上下文(即运行时API的上下文)。
  2. 通过比较 primary_state_check_resultCUDA_ERROR_DEINITIALIZED 以及 CUDA_ERROR_NOT_INITIALIZED 来确定该主要上下文是否已初始化。

然后尝试获取API版本。


我还应该提到,我编写了一个 C++ 封装层,涵盖了驱动程序和运行时 API,并允许无缝使用它们两个;请参见 cuda-api-wrappers 库的 this branch。它们目前尚未发布(截至撰写本文的时间),但您非常欢迎测试使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接