Cuda Stream流分析
Stream
一般来说,cuda c并行性表现在下面两个层面上:
- Kernel level
- Grid level
Stream和event简介
Cuda stream是指一堆异步的cuda操作,他们按照host代码调用的顺序执行在device上。
典型的cuda编程模式我们已经熟知了:
- 将输入数据从host转移到device
- 在device上执行kernel
- 将结果从device上转移回host
Cuda Streams
所有的cuda操作(包括kernel执行和数据传输)都显式或隐式的运行在stream中,stream也就两种类型,分别是:
- 隐式声明stream(NULL stream)
- 显示声明stream(non-NULL stream)
异步且基于stream的kernel执行和数据传输能够实现以下几种类型的并行:
- Host运算操作和device运算操作并行
- Host运算操作和host到device的数据传输并行
- Host到device的数据传输和device运算操作并行
- Device内的运算并行
下面代码是常见的使用形式,默认使用NULL stream:
cudaMemcpy(..., cudaMemcpyHostToDevice);
kernel<<<grid, block>>>(...);
cudaMemcpy(..., cudaMemcpyDeviceToHost);
下面版本是异步版本的cudaMemcpy:
cudaError_t cudaMemcpyAsync(void* dst, const void* src, size_t count,cudaMemcpyKind kind, cudaStream_t stream = 0);
上面代码使用了默认stream,如果要声明一个新的stream则使用下面的API定义一个:
cudaError_t cudaStreamCreate(cudaStream_t* pStream);
Pinned memory的分配如下:
cudaError_t cudaMallocHost(void **ptr, size_t size);
cudaError_t cudaHostAlloc(void **pHost, size_t size, unsigned int flags);
在执行kernel时要想设置stream的话,只要加一个stream参数就好:
kernel_name<<<grid, block, sharedMemSize, stream>>>(argument list);
// 非默认的stream声明
cudaStream_t stream;
// 初始化
cudaStreamCreate(&stream);
// 资源释放
cudaError_t cudaStreamDestroy(cudaStream_t stream);
所有stram的执行都是异步的,需要一些API在必要的时候做同步操作:
cudaError_t cudaStreamSynchronize(cudaStream_t stream);
cudaError_t cudaStreamQuery(cudaStream_t stream);
看一下代码片段:
for (int i = 0; i < nStreams; i++) {
int offset = i * bytesPerStream;
cudaMemcpyAsync(&d_a[offset], &a[offset], bytePerStream, streams[i]);
kernel<<grid, block, 0, streams[i]>>(&d_a[offset]);
cudaMemcpyAsync(&a[offset], &d_a[offset], bytesPerStream, streams[i]);
}
for (int i = 0; i < nStreams; i++) {
cudaStreamSynchronize(streams[i]);
}
使用了三个stream,数据传输和kernel运算都被分配在了这几个并发的stream中。

kernel数目是依赖于device本身的,Fermi支持16路并行,Kepler是32。并行数是受限于shared memory,寄存器等device资源。
Stream Scheduling

C和P以及R和X是可以并行的,因为他们在不同的stream中,但是ABC,PQR以及XYZ却不行,比如,在B没完成之前,C和P都在等待。
Hyper-Q
Hyper-Q的技术, Kepler上出现了32个工作队列。实现了TPC上可以同时运行compute和graphic的应用。当然,如果超过32个stream被创建了,依然会出现伪依赖的情况。

Stream Priorities
对于CC3.5及以上版本,stream可以有优先级的属性:
cudaError_t cudaStreamCreateWithPriority(cudaStream_t* pStream, unsigned int flags, int priority);
该函数创建一个stream,赋予priority的优先级,高优先级的grid可以抢占低优先级执行。
cudaError_t cudaDeviceGetStreamPriorityRange(int *leastPriority, int *greatestPriority);
leastPriority是下限,gretestPriority是上限。数值较小则拥有较高优先级。如
Cuda Events
Event是stream用来标记strean执行过程的某个特定的点。其主要用途是:
- 同步stream执行
- 操控device运行步调
Creation and Destruction
// 声明
cudaEvent_t event;
// 创建
cudaError_t cudaEventCreate(cudaEvent_t* event);
// 销毁
cudaError_t cudaEventDestroy(cudaEvent_t event);
streeam的释放,在操作完成后自动释放资源。
Recording Events and Mesuring Elapsed Time
cudaError_t cudaEventRecord(cudaEvent_t event, cudaStream_t stream = 0);
等待event会阻塞调用host线程,同步操作调用下面的函数:
cudaError_t cudaEventSynchronize(cudaEvent_t event);
类似于cudaStreamSynchronize,等待event而不是整个stream执行完毕。使用API来测试event是否完成,该函数不会阻塞host:
cudaError_t cudaEventQuery(cudaEvent_t event);
该函数类似cudaStreamQuery。此外,还有专门的API可以度量两个event之间的时间间隔:
cudaError_t cudaEventElapsedTime(float* ms, cudaEvent_t start, cudaEvent_t stop);
返回start和stop之间的时间间隔,单位是毫秒。Start和stop不必关联到同一个stream上。
下面代码简单展示了如何使用event来度量时间:
// create two events
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
// record start event on the default stream
cudaEventRecord(start);
// execute kernel
kernel<<<grid, block>>>(arguments);
// record stop event on the default stream
cudaEventRecord(stop);
// wait until the stop event completes
cudaEventSynchronize(stop);
// calculate the elapsed time between two events
float time;
cudaEventElapsedTime(&time, start, stop);
// clean up the two events
cudaEventDestroy(start);
cudaEventDestroy(stop);
Stream Synchronization
由于所有non-default stream的操作对于host来说都是非阻塞的,就需要相应的同步操作。
从host的角度来看,cuda操作可以被分为两类:
- Memory相关的操作
- Kernel launch
Kernel launch对于host来说都是异步的,许多memory操作则是同步的,比如cudaMemcpy,cuda runtime也会提供异步函数来执行memory操作。
阻塞和非阻塞stream
使用cudaStreamCreate创建的是阻塞stream,也就是说,该stream中执行的操作会被早先执行的同步stream阻塞。
例如:
kernel_1<<<1, 1, 0, stream_1>>>();
kernel_2<<<1, 1>>>();
kernel_3<<<1, 1, 0, stream_2>>>();
可以通过下面的API配置生成非阻塞stream:
cudaError_t cudaStreamCreateWithFlags(cudaStream_t* pStream, unsigned int flags);
// flag为以下两种,默认为第一种,非阻塞便是第二种。
cudaStreamDefault: default stream creation flag (blocking)
cudaStreamNonBlocking: asynchronous stream creation flag (non-blocking)
Implicit Synchronization
Cuda有两种类型的host和device之间同步:显式和隐式。已经了解到显式同步API有:
- cudaDeviceSynchronize
- cudaStreamSynchronize
- cudaEventSynchronize
这三个函数由host显式的调用,在device上执行。
许多memory相关的操作都会影响当前device的操作,比如:
- A page-locked host memory allocation
- A device memory allocation
- A device memset
- A memory copy between two addresses on the same device
- A modification to the L1/shared memory confi guration
Explicit Synchronization
从grid level来看显式同步方式,有如下几种:
- Synchronizing the device
- Synchronizing a stream
- Synchronizing an event in a stream
- Synchronizing across streams using an event
可以使用cudaDeviceSynchronize来同步该device上的所有操作。通过使用cudaStreamSynchronize可以使host等待特定stream中的操作全部完成或者使用非阻塞版本的cudaStreamQuery来测试是否完成。
Cuda event可以用来实现更细粒度的阻塞和同步,相关函数为cudaEventSynchronize和cudaEventSynchronize,用法类似stream相关的函数。此外,cudaStreamWaitEvent提供了一种灵活的方式来引入stream之间的依赖关系:
cudaError_t cudaStreamWaitEvent(cudaStream_t stream, cudaEvent_t event);
该函数会指定该stream等待特定的event,该event可以关联到相同或者不同的stream,对于不同stream的情况,如下图所示:

Stream2会等待stream1中的event完成后继续执行。
Configurable Events
Event的配置可用下面函数:
cudaError_t cudaEventCreateWithFlags(cudaEvent_t* event, unsigned int flags);
cudaEventDefault
cudaEventBlockingSync
cudaEventDisableTiming
cudaEventInterprocess
Cuda Stream流分析的更多相关文章
- 流分析 Stream Analytics-实时数据流式处理,可处理来自数百万台 IoT 设备的数据
典型的物联网架构中,有实时数据分析的需求,在Azure中,流分析(stream analytics)就是这样的服务,它可以存在云中或者部署到边缘设备上. 流分析的基本概念: https://v.qq. ...
- CUDA 7 Stream流简化并发性
CUDA 7 Stream流简化并发性 异构计算是指高效地使用系统中的所有处理器,包括 CPU 和 GPU .为此,应用程序必须在多个处理器上并发执行函数. CUDA 应用程序通过在 streams ...
- 深度分析:java8的新特性lambda和stream流,看完你学会了吗?
1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...
- java8 Stream的实现原理 (从零开始实现一个stream流)
1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2 ...
- H264裸流分析中,能获取哪些信息?
从H264的裸流中,PPS,SPS中,一定可以获取到的,有图像的宽,高信息. 这部分信息的提取,用Stream eye 分析: 这里需要特别提一下这两个参数: pic_width_in_mbs_mi ...
- aac adts & LATM封装码流分析
本文继续上一篇文章的内容,介绍一个音频码流处理程序.音频码流在视频播放器中的位置如下所示. 本文中的程序是一个AAC码流解析程序.该程序可以从AAC码流中分析得到它的基本单元ADTS frame,并且 ...
- JavaSE复习(七)Stream流和方法引用
Stream流 全新的Stream概念,用于解决已有集合类库既有的弊端. 传统集合的多步遍历代码 几乎所有的集合(如 Collection 接口或 Map 接口等)都支持直接或间接的遍历操作.而当我们 ...
- 简洁方便的集合处理——Java 8 stream流
背景 java 8已经发行好几年了,前段时间java 12也已经问世,但平时的工作中,很多项目的环境还停留在java1.7中.而且java8的很多新特性都是革命性的,比如各种集合的优化.lambda表 ...
- 理解nodejs中的stream(流)
阅读目录 一:nodeJS中的stream(流)的概念及作用? 二:fs.createReadStream() 可读流 三:fs.createWriteStream() 可写流 回到顶部 一:node ...
随机推荐
- hdu1043 经典的八数码问题 逆向bfs打表 + 逆序数
题意: 题意就是八数码,给了一个3 * 3 的矩阵,上面有八个数字,有一个位置是空的,每次空的位置可以和他相邻的数字换位置,给你一些起始状态 ,给了一个最终状态,让你输出怎么变换才能达到目的. 思路: ...
- Linux中的.bash_ 文件详解
目录 .bash_history .bash_logout .bash_profile .bashrc 每个用户的根目录下都有四个这样的 bash文件,他们是隐藏文件,需要使用-a参数才会显示出来 . ...
- Linux-鸟菜-0-计算机概论
Linux-鸟菜-0-计算机概论 这一章在说计算机概论,额....,总的来说看完之后还是有点收获,回忆了下计算机基本知识.没有什么可上手操作的东西,全是概念,直接把最后的总结给截图过来吧,因为概念的话 ...
- 板载网卡MAC地址丢失后刷回方法[转]
部份客户在进行误操作后发现网卡MAC地址全部变成0,大部客户不知道如何重新将MAC地址写回去.就此问题我们介绍一下,希望可以帮到大家.修改MAC地址时,一定要在纯DOS环境下修改.目前使用U盘DOS引 ...
- SSM框架MavenWeb项目的测试
由于SSM项目的类都是由Spring容器托管,所以直接进行用new对象调用方法进行测试是不行不通的,会出现空指针异常NullPointExpection. 因为我们的对象由spring进行托管,调用的 ...
- 『动善时』JMeter基础 — 17、JMeter配置元件【HTTP请求默认值】
目录 1.HTTP请求默认值介绍 2.HTTP请求默认值界面 3.HTTP请求默认值的使用 (1)用于演示的项目说明 (2)测试计划内包含的元件 (3)说明HTTP请求默认值用法 4.总结 5.拓展知 ...
- mysql无列名注入
0x00 原理 mysql无列名注入是报错注入的一个变种,前提是已知表名,但是不知道列名,或者只知道部分列名,可通过报错注入拼接查询自身表,当自表被拼接时,由于存在重复属性列,会将列信息报错返回, ...
- Python批量图片去水印,提高工作效率
平常工作中,有时为了采用网络的一些素材,但这些素材往往被打了水印,如果我们不懂PS就无法去掉水印,或者无法批量去掉水印.这些就很影响我们的工作效率. 今天我们就一起来,用Python + OpenC ...
- [bug] flink on yarn 启动失败
参考 https://www.cnblogs.com/huangguoming/p/11732663.html
- [DB] Memcache
什么是Memcache Redis的前身 严格来说只能叫缓存,不支持持久化,停电后数据丢失 Strom.Spark Streaming实时计算的结果一般会保存在Redis中 JDBC是性能瓶颈 关系型 ...