cuda driver-上下文管理

前言

CUcontext是CUDA(Compute Unified Device Architecture)中的一个概念,用于管理和跟踪与GPU设备的交互。它代表了一个CUDA上下文,即一个应用程序与GPU之间的通信环境。

1.CUcontext作用

在CUDA中,一个应用程序可以与多个GPU设备进行交互。每个GPU设备都有一个唯一的设备ID来标识它们。当应用程序需要与某个特定GPU设备进行通信时,它需要先创建一个与该设备关联的上下文(CUcontext)。
CUcontext提供了一种隔离和管理与GPU设备的交互的方式。通过创建和销毁上下文,应用程序可以切换和管理不同的GPU设备。上下文还提供了一些重要的功能,如内存管理、核函数的执行等。
在CUDA编程中,应用程序需要使用一些API函数来创建和操作上下文,如cuCtxCreate()用于创建上下文,cuCtxDestroy()用于销毁上下文等。在创建上下文时,还可以指定一些参数来配置上下文的属性,如与上下文关联的设备ID等。
总之,CUcontext是CUDA中用于管理和跟踪与GPU设备的交互的重要概念,它提供了一种隔离和管理GPU设备的方式,使应用程序能够有效地利用GPU的计算资源。

2.CUcontext常用API

APIDescription
cuCtxCreate用于创建上下文
cuCtxDestroy用于销毁上下文
cuDevicePrimaryCtxRetain自动管理上下文而无需显式地设置当前上下文
cuCtxGetCurrent获取当前上下文
cuCtxPushCurrent压入
cuCtxPopCurrent弹出

实例代码如下:

#include <cuda.h>
#include <stdio.h>
#include <string.h>

#define checkDriver(op) __check_cuda_driver((op), #op, __FILE__, __LINE__)

bool __check_cuda_driver(CUresult code, const char *op, const char *file, int line)
{

    if (code != CUresult::CUDA_SUCCESS)
    {
        const char *err_name = nullptr;
        const char *err_message = nullptr;
        cuGetErrorName(code, &err_name);
        cuGetErrorString(code, &err_message);
        printf("%s:%d  %s failed. \n  code = %s, message = %s\n", file, line, op, err_name, err_message);
        return false;
    }
    return true;
}

int main()
{
    // 检查cuda driver初始化
    if (!checkDriver(cuInit(0)))
    {
        return -1;
    }

    // 获取 cuda 驱动版本
    int driver_version = 0;
    if (!checkDriver(cuDriverGetVersion(&driver_version)))
    {
        return -1;
    }
    printf("CUDA Driver version is %d\n", driver_version);

    // 获取当前设备信息
    char device_name[100];
    CUdevice device = 0;
    if (!checkDriver(cuDeviceGetName(device_name, sizeof(device_name), device)))
    {
        return -1;
    }
    printf("Device %d name is %s\n", device, device_name);

    // 为设备创建上下文
    // CUcontext 其实是 struct CUctx_st*(是一个指向结构体CUctx_st的指针)
    CUcontext ctxA = nullptr;
    CUcontext ctxB = nullptr;
    // CUdevice device = 0;
    // 这一步相当于告知要某一块设备上的某块地方创建 ctxA 管理数据。输入参数 参考 https://www.cs.cmu.edu/afs/cs/academic/class/15668-s11/www/cuda-doc/html/group__CUDA__CTX_g65dc0012348bc84810e2103a40d8e2cf.html
    checkDriver(cuCtxCreate(&ctxA, CU_CTX_SCHED_AUTO, device));
    checkDriver(cuCtxCreate(&ctxB, CU_CTX_SCHED_AUTO, device));
    printf("ctxA = %p \n", ctxA);
    printf("ctxB = %p \n", ctxB);
    /*
        contexts 栈:
            ctxB -- top <--- current_context
            ctxA
            ...
     */
    // 获取当前上下文信息
    CUcontext current_context = nullptr;
    checkDriver(cuCtxGetCurrent(&current_context));
    printf("current_context = %p\n", current_context);

    // 可以使用上下文堆栈对设备管理多个上下文
    // 压入当前context
    // 将这个 ctxA 压入CPU调用的thread上。专门用一个thread以栈的方式来管理多个contexts的切换
    checkDriver(cuCtxPushCurrent(ctxA));
    checkDriver(cuCtxGetCurrent(&current_context)); // 获取current_context (即栈顶的context)
    printf("after pushing, current_context = %p\n", current_context);
    /*
        contexts 栈:
            ctxA -- top <--- current_context
            ctxB
            ...
    */

    // 弹出当前context
    CUcontext popped_ctx = nullptr;
    checkDriver(cuCtxPopCurrent(&popped_ctx));
    // 获取current_context(栈顶的)
    checkDriver(cuCtxGetCurrent(&current_context));
    // 弹出的是ctxA
    printf("after poping, popped_ctx = %p\n", popped_ctx);
    // current_context是ctxB
    printf("after poping, current_context = %p\n", current_context);

    checkDriver(cuCtxDestroy(ctxA));
    checkDriver(cuCtxDestroy(ctxB));
    // 更推荐使用cuDevicePrimaryCtxRetain获取与设备关联的context
    // 注意这个重点,以后的runtime也是基于此, 自动为设备只关联一个context
    checkDriver(cuDevicePrimaryCtxRetain(&ctxA, device));
    printf("ctxA = %p\n", ctxA);
    checkDriver(cuDevicePrimaryCtxRelease(device));
    return 0;
}

代码开始创建了两个上下文 ctxA 和 ctxB。通过调用 cuCtexCreate 函数来为特定设备(使用设备标识符 device)创建上下文。然后,代码使用 cuCtxGetCurrent 函数获取当前上下文,并打印其地址。可以看到,在刚创建上下文后,当前上下文与 ctxB 的地址相同。
接下来,代码通过 cuCtxPushCurrent 函数将 ctxA 压入上下文栈,成为当前上下文。然后使用 cuCtxPopCurrent 函数将当前上下文弹出,并用 popped_ctx 变量接收被弹出的上下文。再次调用 cuCtxGetCurrent 函数可以看到当前上下文变成了 ctxB,而 popped_ctx 中保存了被弹出的 ctxA。
最后,代码也演示了使用 cuDevicePrimaryCtxRetain 函数来在 device 上指定一个新地址对ctxA进行管理,自动管理上下文。
运行结果如下:

CUDA Driver version is 11040
Device 0 name is NVIDIA GeForce RTX 3060 Laptop GPU
ctxA = 0x5620c3abc020
ctxB = 0x5620c3f88a20
current_context = 0x5620c3f88a20
after pushing, current_context = 0x5620c3abc020
after poping, popped_ctx = 0x5620c3abc020
after poping, current_context = 0x5620c3f88a20
ctxA = 0x5620c3ac6f20

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
CUDA中,cuCreateContext函数用于创建一个新的上下文(context)。上下文是为了方便控制设备而提出的,它关联了对GPU的所有操作。在创建上下文时,可以使用cuDevicePrimaryCtxRetain函数将设备关联到主上下文,这样一个显卡就对应一个上下文。不同线程只要设备ID相同,上下文就是相同的。上下文也是线程安全的。\[1\] 另外,CUDA中还有表面引用(surface reference)的概念。表面引用是一种用于访问CUDA数组的机制。在使用表面引用之前,需要使用cudaBindSurfaceToArray函数将表面引用绑定到CUDA数组。表面引用只能被声明为静态全局变量,不能作为函数的参数传递。\[2\] 总结起来,CUDA中的上下文是为了方便控制设备而提出的,通过栈进行管理。每个线程都有一个上下文栈,栈顶是当前使用的上下文上下文关联了对GPU的所有操作,而表面引用是一种用于访问CUDA数组的机制。\[3\] #### 引用[.reference_title] - *1* *3* [CUDA Driver API 上下文管理](https://blog.csdn.net/weicao1990/article/details/123959648)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [CUDA学习(二十)](https://blog.csdn.net/weixin_33726318/article/details/89896214)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Attention is all you

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值