第三天 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

猜你喜欢

转载自blog.csdn.net/qq_38973721/article/details/129400066