1.CUDA Stream
(1)分类
- 是隐式流(默认流,NULL流);所有的CUDA操作都默认运行在默认流里面,并且这里面的GPU task和CPU端都是同步的。
- 显示流:显示流里面的GPU task和CPU端计算是异步的,不同显示流内的GPU task也是异步的。
(2)相关的API
- 定义:cudaStream_t stream
- 创建:cudaStreaCreate(&stream)
- 数据传输时:cudaMemcpyAsyn(dst, src, size, type, stream)
- kernel在流中执行调用时:kernel_name<<<grid,block,shareMemSize,stream>>>(argument list)
- 同步流和查询流是否完成:
- cudaError_t cudaStreamSynchronize(cudaStream_t stream)
- cudaError_t cudaStreamQuery(cudaStream_t stream)
- 流的销毁:cudaError_t cudaStreamDestroy(cudaStream_t stream)
(3)CUDA Stream优点
- CPU计算和Kernel计算并行
- CPU计算和数据传输并行
- 数据传输和Kernel计算并行
- Kernel计算并行
需要注意的:
- 显示流里的GPU task与CPU端task的执行是异步的,使用stream一定要注意同步;
- 上面的cudaStreamSynchronize是同步一个流,而cudaDeviceSynchronize是同步该设备(后面有解释)上的所有流;
(4)数据传输和GPU计算重叠的demo
这其中要求数据量和计算量足够大,不然达不到上图最下面这个效果,也没有流的优势了。
但是上图中为什么H2D和D2H为什么没有重叠呢?
因为CPU和GPU之间的数据传输是经过PCIe总线的,PCIe上的操作是顺序的。
带有双工PCIe总线的设备可以重叠两个数据传输,但他们必须在不同呢的流和不同方向上。
(5)CUDA优先级
- 要求:GPU算力3.5以上,即Kepler架构及以上
- API:cudaError_t cudaStreamCreateWithPriority(cudaStream_t *pStream, unsigned int flags, int priority)
- cudaError_t cudaDeviceGetStreamPriorityRange(int *leastPriority)
- 特点:只对kernel有效,较低的整数值表示较高的流优先级,很少用。
(6)CUDA Stream为什么有效
多流为什么会有效,流越多越好么?
- PCIe总线传输速度慢,是瓶颈,会导致传输数据的时候GPU处于空闲等待状态。多流可以实现数据传输与kernel计算的并行。
- 一个kernel往往用不了整个GPU的算力。多流可以让多个kernel同时计算,充分利用GPU算力。
- 不是流越多越好。GPU内可同时并行执行的流数量是有限的。
计算密集型:耗时在计算,一次访存,数十次甚至上百次计算
访存密集型:耗时在访存,一次访存,几次计算
GPU一般处理简单可并行计算,大部分kernel都是访存密集型
CUDA加速,kernel合并,将小任务合并成大任务(但不是大任务变成更大哈),更有效。
(6)默认流的表现
单线程内,默认流的执行是同步的(本部分讨论的默认流是设置了多个流的情况)
但是在编译的时候加上–defual-stream per-thread 后,默认流的执行也是异步的了。
当然在这两种情况中,显示流一直是异步的。
多线程情况下,编译的时候不加上述参数的话,就是多线程共享一个默认流。
加了上述参数进行编译的话,就是每个线程有一个默认流。
2.CUDA Event
在stream中插入一个事件,类似于打一个标记位,用来记录stream是否执行到当前位置。Event有两个状态,已被执行和未被执行。
- 定义:cudaEvent_t event
- 创建:cudaError_t cudaEventCreate(cudaEvent_t* event);
- 插入流中:cudaError_t cudaEventRecord(cudaEvent_t event, cudaStream_t stream = 0);
- 销毁:cudaError_t cudaEventDestroy(cudaEvent_t event);
- 同步和查询
- cudaError_t cudaEventSynchronize(cudaEvent_t event);
- cudaError_t cudaEventQuery(cudaEvent_t event);
- 进阶同步函数cudaError_t cudaStreamWaitEvent(cudaStream_t stream, cudaEvent_t event);
(1)最常使用–测时间
3.CUDA 同步操作
CUDA中的显式同步按粒度可以分为四类
(1)device synchronize 影响很大
(2)stream synchronize 影响单个流和CPU
(3)event synchronize 影响CPU,更细粒度的同步
(4)cudaError_t cudaStreamWaitEvent(cudaStream_t stream, cudaEvent_t event);
(太复杂,基本不用)—
4.NVVP操作
NVIDIA Visual Profiler(NVVP)是NVIDIA推出的跨平台的CUDA程序性能分析工具。
• 随CUDA安装,不需要额外安装。
• 可自定义配置+图形化界面,可以快速找到程序中的性能瓶颈。
• 以时间线的形式展示CPU和GPU操作。
• 可以查看数据传输和kernel的各种软件参数(速度,kernel启动时间等)和硬件参数(L1 cache命中率等)。
(1)Windows中使用
打开nvvp
• File->New Session
• 在File里选择CUDA程序bin
• 选择执行
(2)Linux中使用
- 方法一:使用命令nvprof ./cuda_bin
- 方法二:nvprof -o cuda_bin.nvvp ./cuda_bin,将cuda_bin.nvvp传回windows,使用NVVP打开(要求cuda版本一致)