TensorRT笔记二(RuntimeAPI)

一、Runtime API与Driver API

  • Driver API更为底层一些,需要做显式的device初始化,以及context、module的管理,但是使用更为灵活,提供一些Runtime API所没有的功能
  • Runtime API是封装了Driver API的更高级更友好的API,且进行懒加载,第一个Runtime API调用时灰进行culnit初始化,第一个需要context的API调用时,会进行context关联并创建context并设置当前context,通过调用cuDevicePrimaryCtxRetain实现
  • Runtime API更友好执行核函数,.cpp和.cu文件无缝对接
  • Runtime API对应cuda_runtime.h和libcudart.so,随cuda toolkit发布;Driver API对应cuda.h和libcuda.so,随显卡驱动发布

二、CUDA内存模型

1. 分类

  • 主机内存:Host Memory,CPU内存,内存
  • 设备内存:Device Memory,GPU内存,显存
  •   - 设备内存又分为:
      - 全局内存(3):Global Memory
      - 寄存器内存(1):Register Memory
      - 纹理内存(2):Texture Memory
      - 共享内存(2):Shared Memory
      - 常量内存(2):Constant Memory
      - 本地内存(3):Local Memory
      - 距离芯片越近,速度越快,空间越小,价格越高(括号中数字表示到计算芯片的距离)
    

对于整个Host Memory,操作系统分为两个大类(逻辑分区):

  • Pageable Memory,可分页内存
  • Page lock Memory(Pinned Memory),页锁定内存

2. Pinned Memory(Page lock Memory,页锁定内存)

  • Pinned Memory具有锁定特性,稳定不会被交换
  • Pageable Memory没有锁定特性,对于第三方设备(GPU)去访问时,因为无法感知内存是否被交换,可能得不到正确的数据
  • Pageable Memory性能低于Pinned Memory,可能降低程序优先级然后把内存交换给别人用
  • Pageable Memory策略能使用内存假象(虚拟内存),如实际8GB但可以使用16GB,提高程序运行数量
  • GPU可以直接访问Pinned Memory,而不是Pageable Memory,通过使用DMA(Direct Memory Access)技术
  • Pegaable Memory要传给GPU,先传给Pinned Memory,再到GPU

3.CUDA内存分配

  • 对于GPU访问来说,距离计算单元越近,效率越高,故访问速度PinnedMemory < GlobalMemory < ShareMemory
  • 通过new、malloc分配的是Pageable Memory
  • 通过cudaMalloc分配的是GPU内存(GlobalMemory),分配到setdevice指定的设备上
  • 通过cudaMallocHost分配的是Page Lock Memory(即PinnedMemory,页锁定内存)
  • 尽量多使用PinnedMemory来储存Host数据,或者显式处理Host到Device时,用PinnedMemory做缓存,提高性能

4. CUDA内存复制

(1)如果Host不是页锁定内存

//Device To Host的内存复制过程为
pinned = cudaMallocHost
copy Device to pinned
copy pinned to Host
free pinned
//Host To Device的内存复制过程为
pinned = cudaMallocHost
copy Host to pinned
copy pinned to device
free pinned

(2)如果Host是页锁定内存

//Device To Host
copy Device to Host
//Host To Device
copy Host to Device

三、stream-流

1.流介绍

  • 流是一种基于context之上的任务管道抽象,可以当作一个队列,一个context可以创建n个流
  • 流是异步控制的主要方式
  • nullptr表示默认流,每个线程都有自己的默认流
  • 指令发出后,流队列储存的是指令参数,不能加入队列后立即释放参数指针,否则会导致执行该指令时指针失效而出错

2.cudaStream

//使用流复制内存
int device_id = 0;
checkRuntime(cudaSetDevice(device_id));

cudaStream_t stream = nullptr;
checkRuntime(cudaStreamCreate(&stream));

//在GPU上开辟Pageable Memory空间
float* memory_device = nullptr;
checkRuntime(cudaMalloc(&memory_device, 100 * sizeof(float)));

//在CPU上开辟空间并且存放数据,并把数据复制到GPU
float* memory_host = new float[100];
memory_host[2] = 520.25;
//Async使用流
checkRuntime(cudaMemcpyAsync(memory_device, memory_host, 100 * sizeof(float)), cudaMemcpyHostToDevice, stream); //异步复制操作,主线程不需要等待复制结束才继续

//在CPU上开辟Pinned Memory空间,并将GPU上的数据复制回来
float* memory_page_locked = nullptr;
checkRuntime(cudaMallocHost(&memory_page_locked, 100 * sizeof(float)));
checkRuntime(cudaMemcpyAsync(memory_page_locked, memory_device, 100 * sizeof(float), cudaMemcpyDeviceToHost, stream));//异步复制操作,主线程不需要等待复制结束才继续

//通过cudaStreamSynchronize函数,等待stream中所有指令执行完毕,也就是队列为空
checkRuntime(cudaStreamSynchronize(stream));

//释放内存
checkRuntime(cudaFreeHost(memory_page_locked));
checkRuntime(cudaFree(memory_device));
checkRuntime(cudaStreamDestroy(stream));
delete [] memory_host;

3.Event

向stream中加入Event,监控是否到达某个检查点

  • cudaEventCreate,创建事件
  • cudaEventRecord,记录事件,即stream中加入某个事件,队列执行到该事件修改其状态
  • cudaEventQuery,查询事件当前状态
  • cudaEventElapsedTime,计算两个事件间执行时间间隔,如统计某些核函数执行时间
  • cudaEventSynchronize,同步某个事件,等待事件到达
  • cudaSreamWaitEvent,等待流中的某个事件
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值