第三天 CUDA Context (含代码)

1. Context是什么?

context类似于CPU上的进程,管理由Driver层分配的资源的生命周期

多线程分配调用的GPU资源同属一个context下,通常与CPU的一个进程对应。

Context上下文是设备与特定进程相关联的所有状态

例如Kernel Code会对GPU的使用造成不同的状态如:内存映射、分配、加载等

Context主要用来保存所管理数据来控制和使用设备

GPU中的Context相当于CPU的Program,一块GPU上可以有多个Context,但他们之间是相互间隔的

它表示执行某些任务(例如 CUDA 计算、图形、H.264 编码等)共同需要和实例化的所有状态(数据、变量、条件等)

CUDA 上下文被实例化以在 GPU 上执行 CUDA 计算事件,由 CUDA 运行时 API 隐式或由 CUDA 驱动程序 API 显式执行。

2. Context能做什么?

  1. 持有分配的内存列表
  2. 持有加载进该设备的Kernel Code
  3. CPU与GPU之间的Unified Memory

3. 如何使用Context?

CUDA Driver需要Context

开始时使用cuCtxCreate() 创建,结束时cuCtxDestory销毁

用粗Device PrimaryctxRetain()创建上下文

cuCtxGetCurrent()获取当前上下文

可以使用堆栈管理多个上下文cuCtxPushCurrent()压入cuCtxPopCurrent()压出

对ctxA使用cuCtxPushCurrent()和cuCtxCreate()都相当于将ctxA放在栈顶(让他成为current context)

CUDA runtime可以自动创建,是基于cuDevicePrimaryCtxRetain()创建的


示例代码


#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){    // 条件成立, 返回值为flase
        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的初始化
    checkDriver(cuInit(0));

    // 为设备创建上下文
    CUcontext ctxA = nullptr; // CUcontext 其实是 struct CUctx_st*(是一个指向结构体CUctx_st的指针)
    CUcontext ctxB = nullptr;
    CUdevice device = 0;
    checkDriver(cuCtxCreate(&ctxA, CU_CTX_SCHED_AUTO, device)); // 这一步相当于告知要某一块设备上的某块地方创建 ctxA 管理数据
    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)); // 这个时候current_context 就是上面创建的context
    printf("current_context = %p\n", current_context);

    // 可以使用上下文堆栈对设备管理多个上下文
    // 压入当前context
    checkDriver(cuCtxPushCurrent(ctxA)); // 将这个 ctxA 压入CPU调用的thread上。专门用一个thread以栈的方式来管理多个contexts的切换
    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)); // 将当前的context pop掉,并用popped_ctx承接它pop出来的context
    checkDriver(cuCtxGetCurrent(&current_context)); // 获取current_context(栈顶的)
    printf("after poping, popped_ctx = %p\n", popped_ctx); // 弹出的是ctxA
    printf("after poping, current_context = %p\n", current_context); // current_context是ctxB

    checkDriver(cuCtxDestroy(ctxA));
    checkDriver(cuCtxDestroy(ctxB));

    // 更推荐使用cuDevicePrimaryCtxRetain获取与设备关联的context
    // 注意这个重点,以后的runtime也是基于此, 自动为设备只关联一个context
    checkDriver(cuDevicePrimaryCtxRetain(&ctxA, device)); // 在 device 上指定一个新地址对ctxA进行管理
    printf("ctxA = %p\n", ctxA);
    checkDriver(cuDevicePrimaryCtxRelease(device));
    return 0;
}

参考文献

  • https://www.cs.cmu.edu/afs/cs/academic/class/15668-s11/www/cuda-doc/html/group__CUDA__CTX_g65dc0012348bc84810e2103a40d8e2cf.html
### 关于CuBLAS无当前CUDA上下文的警告 当运行PyTorch程序时遇到`UserWarning: attempting to run cuBLAS without a current CUDA context`,这通常表明在调用cuBLAS操作之前未正确初始化CUDA环境[^1]。此问题可能由以下几个原因引起: #### 原因分析 1. **CUDA上下文缺失**:某些情况下,PyTorch未能自动创建所需的CUDA上下文,尤其是在多线程或多GPU环境中。 2. **异步执行冲突**:如果代码中有多个并发流(streams),可能导致部分操作无法及时绑定到正确的CUDA设备上。 3. **不兼容的操作顺序**:特定张量操作可能会触发内部错误逻辑,尤其是涉及CPU与GPU之间的数据传输。 #### 解决方案 以下是几种常见的解决方案来消除该警告并确保正常运行: ##### 方法一:显式设置默认CUDA设备 通过手动指定默认使用的CUDA设备可以强制建立必要的上下文关系: ```python import torch torch.cuda.set_device(0) # 设置第一个可用的GPU作为默认设备 ``` ##### 方法二:启用同步模式 为了防止潜在的时间序列错乱问题,可以在调试阶段开启全局同步机制: ```python with torch.cuda.device(0): # 使用具体编号替换'0' result = some_tensor.matmul(another_tensor).cuda() ``` 注意这里利用了`with`语句块限定作用范围内的所有计算都发生在所选设备之上[^2]。 ##### 方法三:更新至最新版本库文件 有时此类问题是由于软件本身的缺陷造成的;因此建议定期检查官方发布页面获取最新的稳定版安装包,并按照说明完成升级过程。 另外值得注意的是,在实际部署过程中还应该考虑到目标平台的具体配置情况以及资源分配策略等因素的影响[^3]。 ```python # 示例代码片段展示如何安全地切换不同硬件支持下的矩阵乘法实现方式 if torch.cuda.is_available(): with torch.no_grad(): output = input_data @ weight_matrix # 自动选择最优路径处理大规模运算任务 else: raise RuntimeError('No supported backend found!') ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小鹏AI

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

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

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

打赏作者

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

抵扣说明:

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

余额充值