CUDA运行API:RuntimeAPI

本文介绍CUDA编程的基本概念,包括运行时API、内存模型、流管理、核函数使用、共享内存优化等,并通过实例展示了仿射变换及YOLOv5后处理的实现。

目录

1.1-hello-runtime

1.2 Memory

1.3 Stream:

1.4 核函数

1.5 共享内存

1.6 仿射变换:Warpaffine

1.7 Yolov5后处理

 1.8 error:


  • Cuda开头的函数都属于RuntimeAPI
  • RuntimeAPI,与driver最大区别是懒加载:
  1.  即:第一个runtime API调用时,会进行cuInit初始化,避免驱动api的初始化窘境
  2. 即:第一个需要contextAPI调用时,会进行context关联并创建context和设置当前context调用cuDevicePrimaryCtxRetain实现
  • 绝大部分api需要context,例如查询当前显卡名称、参数、内存分配、释放等

 

  •  使用cuDevicePrimaryCtxRetain为每个设备设置context,不再手工管理context,并且不提供直接管理contextAPI(可Driver API管理,通常不需要)
  • 更友好的使用核函数,.cpp和.cu文件无缝对接

本节主要知识点:核函数的使用、线程束布局、内存模型、流的使用

主要可以实现:归纳求和、仿射变换、矩阵变换、模型后处理

 1.1-hello-runtime

CUDA运行时API开始,以及与CUDA驱动API的context关系解释


// CUDA运行时头文件
#include <cuda_runtime.h>

// CUDA驱动头文件
#include <cuda.h>
#include <stdio.h>
#include <string.h>

#define checkRuntime(op)  __check_cuda_runtime((op), #op, __FILE__, __LINE__)

bool __check_cuda_runtime(cudaError_t code, const char* op, const char* file, int line){
    if(code != cudaSuccess){    
        const char* err_name = cudaGetErrorName(code);    
        const char* err_message = cudaGetErrorString(code);  
        printf("runtime error %s:%d  %s failed. \n  code = %s, message = %s\n", file, line, op, err_name, err_message);   
        return false;
    }
    return true;
}

int main(){

    CUcontext context = nullptr;
    cuCtxGetCurrent(&context); //获取当前的context函数
    printf("Current context = %p,当前无context\n", context);

    // cuda runtime是以cuda为基准开发的运行时库
    // cuda runtime所使用的CUcontext是基于cuDevicePrimaryCtxRetain函数获取的
    // 即,cuDevicePrimaryCtxRetain会为每个设备关联一个context,通过cuDevicePrimaryCtxRetain函数可以获取到
    // 而context初始化的时机是懒加载模式,即当你调用一个runtime api时,会触发创建动作
    // 也因此,避免了cu驱动级别的init和destroy操作。使得api的调用更加容易
    int device_count = 0;
    checkRuntime(cudaGetDeviceCount(&device_count));
    printf("device_count = %d\n", device_count);

    // 取而代之,是使用setdevice来控制当前上下文,当你要使用不同设备时
    // 使用不同的device id
    // 注意,context是线程内作用的,其他线程不相关的, 一个线程一个context stack
    int device_id = 0;
    printf("set current device to : %d,这个API依赖CUcontext,触发创建并设置\n", device_id);
    checkRuntime(cudaSetDevice(device_id));

    // 注意,是由于set device函数是“第一个执行的需要context的函数”,所以他会执行cuDevicePrimaryCtxRetain
    // 并设置当前context,这一切都是默认执行的。注意:cudaGetDeviceCount是一个不需要context的函数
    // 你可以认为绝大部分runtime api都是需要context的,所以第一个执行的cuda runtime函数,会创建context并设置上下文
    cuCtxGetCurrent(&context);
    printf("SetDevice after, Current context = %p,获取当前context\n", context);

    int current_device = 0;
    checkRuntime(cudaGetDevice(&current_device));
    printf("current_device = %d\n", current_device);
    return 0;
}

1.2 Memory

 主要类型:pinned memory、global memory 、shared memory等

整个Host Memory内存条而言,操作系统区分为两个大类(逻辑区分,物理上是同一个东西):

  • Pageable memory,可分页内存   普通房间,内存不够时,将会被移到cpu内存中
  • Page lock memory,页锁定内存  vip房间

Memory总结:

1. pinned memory 具有锁定特性,是稳定不会被交换的(相当于每次去这个房间都一定能找到你)
2. pageable memory 没有 锁定特性 ,对于第三方设备(比如 GPU ),去访问时,因为无法感知内存是否被交换,可能得不到正确的数据(每次去房间找,说不准你的房间被人交换了)
3. pageable memory 的性能比 pinned memory 差,很可能 降低你程序的优先级 然后把内存交换给别人用
4. pageable memory 策略能使用内存假象,实际 8GB 但是可以使用 15GB ,提高程序运行数量(不是速度)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值