CUDA从入门到放弃(七):流( Streams)

news/2024/4/29 23:29:54/文章来源:https://blog.csdn.net/shanglianlm/article/details/137064558

CUDA从入门到放弃(七):流( Streams)

应用程序通过流来管理并发操作,流是一系列按顺序执行的命令。不同的流可能无序或并发地执行命令,但此行为并不保证。流上的命令在依赖关系满足时执行,这些依赖可能来自同一流或其他流。同步调用(synchronize call)可以确保所有启动的命令已完成。

任何 CUDA 操作都存在于某个 CUDA 流中,要么是默认流(default stream),也称为空流(null stream),要么是明确指定的非空流。

1 流的基本概念

1-1 流的创建与销毁 Creation and Destruction of Streams

CUDA 流的定义、产生与销毁

cudaStream_t stream_1;
cudaStreamCreate(&stream_1); // 注意要传流的地址
cudaStreamDestroy(stream_1);

示例:

cudaStream_t stream[2];
for (int i = 0; i < 2; ++i)cudaStreamCreate(&stream[i]);
float* hostPtr;
cudaMallocHost(&hostPtr, 2 * size);for (int i = 0; i < 2; ++i) {cudaMemcpyAsync(inputDevPtr + i * size, hostPtr + i * size,size, cudaMemcpyHostToDevice, stream[i]);MyKernel <<<100, 512, 0, stream[i]>>>(outputDevPtr + i * size, inputDevPtr + i * size, size);cudaMemcpyAsync(hostPtr + i * size, outputDevPtr + i * size,size, cudaMemcpyDeviceToHost, stream[i]);
}for (int i = 0; i < 2; ++i)cudaStreamDestroy(stream[i]);

1-2 默认流 Default Stream

my_kernel<<<N_grid, N_block>>>(函数参数);
my_kernel<<<N_grid, N_block, N_shared>>>(函数参数);
my_kernel<<<N_grid, N_block, N_shared, stream_id>>>(函数参数);

如果用第一种调用方式,说明核函数没有使用动态共享内存,而且在默认流中执行;
如果用第二种调用方式,说明核函数在默认流中执行,但使用了 N_shared 字节的动态共享内存;
如果用第三种调用方式,则说明核函数在编号为stream_id 的 CUDA 流中执行,而且使用了 N_shared 字节的动态共享内存。

在使用非空流但不使用动态共享内存的情况下,必须使用上述第三种调用方式,并将 N_shared 设置为零:

my_kernel<<<N_grid, N_block, 0, stream_id>>>(函数参数); // 正确

1-3 流状态查询

为了实现不同 CUDA 流之间的并发,主机在向某个 CUDA 流中发布一系列命令之后必须马上获得程序的控制权,不用等待该 CUDA 流中的命令在设备中执行完毕。这样,就可以通过主机产生多个相互独立的 CUDA 流。

为了检查一个 CUDA 流中的所有操作是否都在设备中执行完毕, CUDA 运行时 API 提
供了如下函数:

__host__cudaError_t cudaLaunchHostFunc(cudaStream_t stream, cudaHostFn_t fn, void *userData)

cudaDeviceSynchronize()会等待直到所有主机线程的所有流中的所有前面命令都已完成。

__host__cudaError_t cudaStreamSynchronize(cudaStream_t stream)

cudaStreamSynchronize()接受一个流作为参数,并等待直到给定流中的所有前面命令都已完成。它可用于同步主机与特定流,同时允许其他流继续在设备上执行。

__host____device__cudaError_t cudaStreamWaitEvent (cudaStream_t stream, cudaEvent_t event, unsigned int flags)

cudaStreamWaitEvent()接受一个流和一个事件作为参数,并使得所有在调用cudaStreamWaitEvent()之后添加到给定流的命令延迟其执行,直到给定事件已完成。

cudaError_t cudaStreamQuery(cudaStream_t stream);

函数 cudaStreamQuery 不会阻塞主机,只是检查 CUDA 流 stream 中的所有操作是否都执行完毕。若是,返回 cudaSuccess,否则返回 cudaErrorNotReady。

2 重叠执行 Overlapping Behavior

2-1 在默认流中重叠主机和设备计算

虽然同一个 CUDA 流中的所有 CUDA 操作都是顺序执行的,但依然可以在默认流中重叠主机和设备的计算。

示例:

cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice);
cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice);
sum<<<grid_size, block_size>>>(d_x, d_y, d_z, N);
cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost);

cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice); 

sum<<<grid_size, block_size>>>(d_x, d_y, d_z, N);

顺序依次执行。
但是当主机发出

sum<<<grid_size, block_size>>>(d_x, d_y, d_z, N);

命令之后,不会等待该命令执行完毕,而会立刻得到程序的控制权。主机紧接着会发出从设备到主机传输数据的命令

cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost);

该命令会在CUDA流中等待前一个 CUDA 操作(即核函数的调用)执行完毕才会开始执行。
因此,主机在发出核函数调用的命令之后,可以执行主机中的某个计算任务,那么主机就会在设备执行核函数的同时去进行一些计算。这样,主机和设备就可以同时进行计算。

2-2 用非默认 CUDA 流重叠多个核函数的执行与数据传递

尽管在单个默认流中确实可以实现主机计算与设备计算的并行处理,但要实现多个核函数之间的并行执行,则必须借助多个CUDA流。这是因为,在相同的CUDA流内,CUDA操作在GPU设备上是按照顺序执行的。因此,同一个CUDA流内的核函数也必须在设备上依次执行,即便主机在发出每一个核函数调用指令后都立即恢复了程序的控制权。这样的设计确保了操作的顺序性和一致性,但同时也限制了并行度的提升。通过使用多个CUDA流可以有效地将不同的核函数任务分配到不同的执行流中,从而实现更高程度的并行计算,提升整体计算性能。

为了实现核函数执行与数据传输的并发(重叠),必须确保这两个操作在不同的非默认流中执行,且数据传输应使用 cudaMemcpyAsync 函数,这是 cudaMemcpy 的异步版本。cudaMemcpyAsync 通过 GPU 中的 DMA 实现直接内存访问,无需主机参与。

异步传输函数 cudaMemcpyAsync:

cudaError_t cudaMemcpyAsync(  void *dst,                    // 目标内存地址  const void *src,             // 源内存地址  size_t count,                // 传输的字节数  enum cudaMemcpyKind kind,   // 传输方向(主机到设备、设备到主机等)  cudaStream_t stream          // 所在的流  
);

这个函数与同步版本的 cudaMemcpy 相比,仅多出一个参数:流(stream)。通过将数据传输操作分配给特定流,并允许核函数在另一个流中执行,可以实现数据传输和核函数执行的并发执行,从而提高程序的整体性能。

示例:

for (int i = 0; i < 2; ++i)cudaMemcpyAsync(inputDevPtr + i * size, hostPtr + i * size,size, cudaMemcpyHostToDevice, stream[i]);
for (int i = 0; i < 2; ++i)MyKernel<<<100, 512, 0, stream[i]>>>(outputDevPtr + i * size, inputDevPtr + i * size, size);
for (int i = 0; i < 2; ++i)cudaMemcpyAsync(hostPtr + i * size, outputDevPtr + i * size,size, cudaMemcpyDeviceToHost, stream[i]);

3 主机函数(回调函数)Host Functions (Callbacks)

通过 cudaLaunchHostFunc(),可以在流的任意位置插入主机函数的调用。此函数会在流中所有先前命令完成后在主机上执行。

以下示例在每个流执行主机到设备内存复制、内核启动和设备到主机内存复制后,将主机函数 MyCallback 添加到流中。一旦设备到主机的内存复制完成,该回调函数将在主机上执行。

void CUDART_CB MyCallback(void *data){printf("Inside callback %d\n", (size_t)data);
}
...
for (size_t i = 0; i < 2; ++i) {cudaMemcpyAsync(devPtrIn[i], hostPtr[i], size, cudaMemcpyHostToDevice, stream[i]);MyKernel<<<100, 512, 0, stream[i]>>>(devPtrOut[i], devPtrIn[i], size);cudaMemcpyAsync(hostPtr[i], devPtrOut[i], size, cudaMemcpyDeviceToHost, stream[i]);cudaLaunchHostFunc(stream[i], MyCallback, (void*)i);
}

请注意,排入流的主机函数应避免直接或间接调用 CUDA API,以免发生死锁。

4 流优先级

在创建流时,可以使用 cudaStreamCreateWithPriority() 函数指定流的相对优先级。通过 cudaDeviceGetStreamPriorityRange() 函数,可以获取允许的优先级范围,该范围按从最高优先级到最低优先级的顺序排列。在运行时,高优先级流中的待处理任务将优先于低优先级流中的待处理任务执行。

以下代码示例获取当前设备的允许优先级范围,并使用可用的最高和最低优先级创建流。

// get the range of stream priorities for this device
int priority_high, priority_low;
cudaDeviceGetStreamPriorityRange(&priority_low, &priority_high);
// create streams with highest and lowest available priorities
cudaStream_t st_high, st_low;
cudaStreamCreateWithPriority(&st_high, cudaStreamNonBlocking, priority_high);
cudaStreamCreateWithPriority(&st_low, cudaStreamNonBlocking, priority_low);

参考资料
1 CUDA编程入门
2 CUDA编程入门极简教程
3 CUDA C++ Programming Guide
4 CUDA C++ Best Practices Guide
5 NVIDIA CUDA初级教程视频
6 CUDA专家手册 [GPU编程权威指南]
7 CUDA并行程序设计:GPU编程指南
8 CUDA C编程权威指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1027323.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟机-从头配置Ubuntu18.04(包括anaconda,cuda,cudnn,pycharm,ros,vscode)

最好先安装anaconda后cuda和cudnn&#xff0c;因为配置环境的时候可能conda会覆盖cuda的路径&#xff08;不确定这种说法对不对&#xff0c;这里只是给大家的建议&#xff09; 准备工作&#xff1a; 1.Ubuntu18.04&#xff0c;x86_64&#xff0c;amd64 虚拟机下载和虚拟机Ubu…

【氮化镓】位错对氮化镓(GaN)电子能量损失谱(EEL)的影响

本文献《Influence of dislocations on electron energy-loss spectra in gallium nitride》由C. J. Fall等人撰写&#xff0c;发表于2002年。研究团队通过第一性原理计算&#xff0c;探讨了位错对氮化镓&#xff08;GaN&#xff09;电子能量损失谱&#xff08;EEL&#xff09;…

探索 2024 年 Web 开发最佳前端框架

前端框架通过简化和结构化的网站开发过程改变了 Web 开发人员设计和实现用户界面的方法。随着 Web 应用程序变得越来越复杂&#xff0c;交互和动画功能越来越多&#xff0c;这是开发前端框架的初衷之一。 在网络的早期&#xff0c;网页相当简单。它们主要以静态 HTML 为特色&a…

ArcGIS Pro横向水平图例

终于知道ArcGIS Pro怎么调横向图例了&#xff01; 简单的像0一样 旋转&#xff0c;左转右转随便转 然后调整图例项间距就可以了&#xff0c;参数太多就随便试&#xff0c;总有一款适合你&#xff01; 要调整长度&#xff0c;就调整图例块的大小。完美&#xff01; 好不容易…

大型矿业集团安全知识竞赛主持词

男&#xff1a;尊敬的各位领导&#xff0c;员工同志们&#xff1a; 合&#xff1a;大家好&#xff01; 男&#xff1b;首先让我们以热烈的掌声对公司领导亲临比赛现场指导观看表示欢迎&#xff01; 男&#xff1b;继成功开展了荣辱观专题讲座、好矿嫂女红艺术展、安全谜语竞猜…

ArcGIS制作风向频率玫瑰图

风玫瑰图是气象科学专业统计图表,用来统计某个地区一段时期内风向、风速发生频率,又分为“风向玫瑰图”和“风速玫瑰图” ;因图形似玫瑰花朵,故名。风玫瑰图对于涉及城市规划、环保、风力发电等领域有着重要的意义。风玫瑰图能够直观的显现某地区不同方位风向的频率特征,进…

Python拆分PDF、Python合并PDF

WPS能拆分合并&#xff0c;但却是要输入编辑密码&#xff0c;我没有。故写了个脚本来做拆分&#xff0c;顺便附上合并的代码。 代码如下&#xff08;extract.py) #!/usr/bin/env python """PDF拆分脚本(需要Python3.10)Usage::$ python extract.py <pdf-fil…

TitanIDE与传统 IDE 比较

与传统IDE的比较 TitanIDE 和传统 IDE 属于不同时代的产物&#xff0c;在手工作坊时代&#xff0c;一切都是那么的自然&#xff0c;开发者习惯 Windows 或 MacOS 原生 IDE。不过&#xff0c;随着时代的变迁&#xff0c;软件行业已经步入云原生时代&#xff0c;TitanIDE 是顺应…

Switch 和 PS1 模拟器:3000+ 游戏随心玩 | 开源日报 No.174

Ryujinx/Ryujinx Stars: 26.1k License: MIT Ryujinx 是用 C# 编写的实验性任天堂 Switch 模拟器。 该项目旨在提供出色的准确性和性能、用户友好的界面以及稳定的构建。它已经通过了大约 4050 个测试&#xff0c;其中超过 4000 个可以启动并进入游戏&#xff0c;其中大约 340…

从小白-入门-进阶-高阶,四个阶段详细讲解单片机学习路线!

大家好&#xff0c;今天给大家介绍从小白-入门-进阶-高阶&#xff0c;四个阶段详细讲解单片机学习路线&#xff01;&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 单片机学习路…

第18次修改了可删除可持久保存的前端html备忘录

第17次修改了可删除可持久保存的前端html备忘录&#xff1a;增加年月日星期&#xff0c;增加倒计时&#xff0c;更改保存区名称可以多个备忘录保存不一样的信息&#xff0c;匹配背景主题&#xff1a;现代深色 <!DOCTYPE html> <html lang"zh"> <head&…

C语言例4-27:计算1+2+...+100之和(利用while语句实现)。

代码如下&#xff1a; //计算12...100之和&#xff08;利用while语句实现&#xff09;。 #include<stdio.h> int main(void) {int n1, sum0;while(n<100){ //复合语句作为当型循环结构的循环体sumsumn;n;}printf("sum %d\n",sum);retu…

Capture One Pro 22 for Mac/win:重塑RAW图像处理的艺术

在数字摄影的世界里&#xff0c;RAW图像处理软件无疑是摄影师们手中的魔法棒&#xff0c;而Capture One Pro 22无疑是这一领域的璀璨明星。这款专为Mac和Windows系统打造的图像处理软件&#xff0c;以其出色的性能、丰富的功能和极致的用户体验&#xff0c;赢得了全球摄影师的广…

ES6 学习(三)-- es特性

文章目录 1. Symbol1.1 使用Symbol 作为对象属性名1.2 使用Symbol 作为常量 2. Iterator 迭代器2.1 for...of循环2.2 原生默认具备Interator 接口的对象2.3 给对象添加Iterator 迭代器2.4 ... 解构赋值 3. Set 结构3.1 初识 Set3.2 Set 实例属性和方法3.3 遍历3.4 相关面试题 4…

代码学习记录29----贪心最后一天

随想录日记part29 t i m e &#xff1a; time&#xff1a; time&#xff1a; 2024.03.28 主要内容&#xff1a;今天是学习贪心算法最后一天&#xff0c;接下来是针对题目的讲解&#xff1a;1.单调递增的数字;2.监控二叉树; 3. 总结 738.单调递增的数字 968.监控二叉树 总结 To…

【直播课】2024年PostgreSQL CM认证实战培训课程于4月27日开课!

课程介绍 了解关注开源技术&#xff0c;学习PG以点带面 Linux/Andriod&#xff08;操作系统&#xff09;、Apache/Tomcat&#xff08;应用服务器&#xff09;、OpenStack/KVM&#xff08;虚拟化&#xff09;、Docker/K8S&#xff08;容器化&#xff09;、Hadoop&#xff08;大…

通过WSL在阿里云上部署Vue项目

参考&#xff1a; 阿里云上搭建网站-CSDN博客 云服务器重装 关闭当前运行实例 更换操作系统&#xff0c;还有其他的进入方式。 选择ubuntu系统&#xff08;和WSL使用相同的系统&#xff09;。 设置用户和密码。发送短信验证码。 新系统更新。秒速干净的新系统设置完成。 这…

玩电脑突然停电对电脑有影响吗

在现代社会中&#xff0c;电脑已成为我们日常生活和工作中不可或缺的一部分。然而&#xff0c;当我们正在专注于工作或娱乐时&#xff0c;突然停电可能会给我们带来不小的困扰。那么&#xff0c;玩电脑时突然停电会对电脑产生哪些影响呢&#xff1f;本文将深入探讨这一问题&…

踩坑uniapp中打包Andiord app,在真机调试时地图以及定位功能可以正常使用,打包成app后失效的问题

首先看到这是uni官网提出的&#xff0c;app上建议使用高德地图。 下面就用高德地图进行配置。 步骤一&#xff1a;登陆高德地图控制台 名称和类型根据自己情况填写选择即可 步骤二&#xff1a; 添加key 步骤三&#xff1a;取到SHA1 进入uniapp开发官网 点击应用名称&#…

Chrome 插件 tabs API 解析

Chrome.tabs API 解析 使用 chrome.tabs API 与浏览器的标签页系统进行交互&#xff0c;可以使用此 API 在浏览器中创建、修改和重新排列标签页 Tabs API 不仅提供操作和管理标签页的功能&#xff0c;还可以检测标签页的语言、截取屏幕截图&#xff0c;以及与标签页的内容脚本…