(番外)如何将cuda数据存入std::queue实现异步效果

文章目录

  • 1、std::queue列队如何实现异步?
  • 2、std::queue可以存储哪些数据类型?
    • 2.1、queue如何存放位于cuda上的数据
    • 2.2、如何从queue读取位于cuda上的数据?
    • 2.3、注意:需要的最大显存
  • 3、一种更优的方法

1、std::queue列队如何实现异步?

std::queue是一种队列容器,对于需要先进先出的任务非常实用。比如下面是一个列队的使用用例:

#include <iostream>
#include <queue>

int main() {
    // 创建一个 int 类型的队列
    std::queue<int> myQueue;

    // 向队列中添加一些元素,此时myQueue中存放的数据为->[30,20,10]->
    myQueue.push(10);
    myQueue.push(20);
    myQueue.push(30);

    // 访问队列的头部元素
    std::cout << "Front of the queue: " << myQueue.front() << std::endl;

    // 删除队列的头部元素,此时myQueue中存放的数据为->[30,20]->
    myQueue.pop();

    // 再次访问队列的头部元素,此时myQueue中存放的数据为->[30,20]->
    std::cout << "Front of the queue after pop: " << myQueue.front() << std::endl;

    // 检查队列是否为空,此时myQueue中存放的数据为->[30,20]->
    if (myQueue.empty()) {
        std::cout << "Queue is empty." << std::endl;
    } else {
        std::cout << "Queue is not empty." << std::endl;
    }

    // 获取队列的大小,此时myQueue中存放的数据为->[30,20]->
    std::cout << "Size of the queue: " << myQueue.size() << std::endl;

    return 0;
}

实际列队对于异步处理非常有帮助。假设有任务需要依次进入step1和step2两个阶段,如果想要提高任务的运行效率可以使用异步的方法,即step1产生结果后送入列队,然后开始新的step1。step2则会循环判断列队是否有值,若有则获取队列中的值执行step2。相当于在step1和step2中间建立了一个蓄水池,step1不断将结果扔向蓄水池,step2则在蓄水池有水(值)时执行step2。这样大大提高了整个任务的效率。

2、std::queue可以存储哪些数据类型?

std::queue 是 C++ 标准库中的容器之一,它可以存储任何 C++ 数据类型,包括内置数据类型(如 int、float、char 等)、自定义结构体、类对象等。当然除此之外我们还可以将指向数据的指针储存到queue中,不用关心指针指向的具体数据的类型、位置等。

现在我们要讨论一下,如果一个在step1产生的结果保存在cuda上,为了保证step1和step2的异步特性,如何将位于cuda上的数据放入列队?

2.1、queue如何存放位于cuda上的数据

我们需要知道,在使用显存时,通常需要先创建一个指针,然后分配显存并将其地址储存给指针:

// 假设在显存中会储存一个100个浮点数
int size=100
float *decodeed_cuda_buffer;

// 分配length * sizeof(float)个字节显存空间,并将空间地址储存在指针decodeed_cuda_buffer中
cudaMalloc((void **)(&decodeed_cuda_buffer), length * sizeof(float));

其中decodeed_cuda_buffer指针是位于host上的,而decodeed_cuda_buffer指向的内存是位于device上的。

所以我们可以在queue中储存指针decodeed_cuda_buffer,如图所示。首先通过cudaMalloc申请到新的显存后,将地址储存给指针*p;然后通过*p对显存数据进行操作(一般是写入新数据);最后将指针*p送入queue。这样在queue中就储存了多个显存空间的地址,依次储存我们写入的数据。

在这里插入图片描述
实现代码如下,我们希望每次分配新的显存,并将地址给p_cuda_men;然后对cuda显存数据操作后,将其地址p_cuda_men送入queue。最终queue中存放多个cuda空间地址:

std::queue<float *> gpu_buffers_queue;  // 创建一个列队
float *p_cuda_men;  // 储存cuda显存地址
// 分配显存
for (int i=0;i<count;i++) {
    // part1:分配一块新的显存空间,并将地址储存在p_cuda_men中。
	cudaMalloc((void **)(&p_cuda_men), length * sizeof(float));
	operate(p_cuda_men)  // decodeed_cuda_buffer指向的显存的内容已改变
	// part2:将本次分配的显存块的地址放入列队
	gpu_buffers_queue.push(p_cuda_men);
}

一种错误的方法如下,显存只分配一次。可以看到for循环中只是修改了p_cuda_men指向的显存的值,p_cuda_men储存的地址没有改变。即循环push的是同一个显存地址,就是唯一一次cudaMalloc分配的显存地址。最终在gpu_buffers_queue中存放的是完全一样的显存地址,而显存中的值则是最后一次operate修改后的值:

std::queue<float *> gpu_buffers_queue;  // 创建一个列队
// 实例化一个float *并分配显存。decodeed_cuda_buffer指向分配内存的地址

// part1:创建新的指针,指向分配的显存。即该指针存放本次分配的显存块的地址。
float *p_cuda_men;
cudaMalloc((void **)(&p_cuda_men), length * sizeof(float));

// 分配显存
for (int i=0;i<count;i++) {
    // operate只是对唯一的一块显存进行操作
	operate(p_cuda_men)  
	// part2:将显存地送入列队
	gpu_buffers_queue.push(p_cuda_men);
}

2.2、如何从queue读取位于cuda上的数据?

看图,通过queue.front()可以获取到最先送入queue的指针p4,可以通过如cudaMemcpy(cpu_float_vector, p4, size * sizeof(float), cudaMemcpyDeviceToHost);等操作对该显存数据进行操作。

非常重要:cuda的显存分配后需要通过cudaFree(p4)手动释放。在queue中存放的是指向不同显存空间的指针,所以通过p=queue.front()获取到显存地址后,并对该地址数据进行相关操作,比如cudaFree§进行释放。否则只分配不释放,显存占用累计最终OOM。
在这里插入图片描述

如果是下面这种,最后没有cudaFree§。等下一次循环时,没有变量储存p4指向的空间,我们再也无法通过cudaFree()释放p4显存,导致一直占用:

loop:
	p=queue.front();  // 第一次循环:获取到*p4
	operate(p);  // 对p指向的显存数据进行某种需要的操作
	queue.pop();  // 弹出*p4,现在queue=[*p1, *p2, *p3]

合理的写法是:

loop:
	p=queue.front();  // 第一次循环:获取到*p4
	operate(p);  // 对p指向的显存数据进行某种需要的操作
	queue.pop();  // 弹出*p4,现在queue=[*p1, *p2, *p3]
	cudaFree(p);   // 释放p4指向的显存空间

2.3、注意:需要的最大显存

在上面的step1和step2异步中,step1向queue中写入,step2从queue读出。step1每写入依次代表分配了一块显存,而step2每读出一次则会销毁这块显存。

  • 如果step1的处理速度小于step2,则step2经常性等待step1向queue中存入数据。所以分配的显存可能就只有1~2次(不严谨)。

  • 如果step1的处理速度大于step2,则queue中存入比读取更快,导致queue的size越来越大,储存的指针数据越来越多。每个指针数据代表分配的一块显存,可想而知显存的占用也会越来越高(可用nvidia-smi查看)。所以实际运用中需要:1)多步任务中,需要合理分段,让各段的处理速度相当;2)限制queue长度,超过长度时新的数据直接覆盖queue的尾端(即最新放入的)。

在限制queue长度后,该程序需要使用的最大显存就是长度*每个显存块的size

3、一种更优的方法

上面的方法需要不断地创建释放显存,可能并不优雅。现在以下方法(理论上,未测试)优化:

  • 创建一个cuda列队类,初始化的时候定义列队大小N并创建N个显存块。在使用时循环将N个显存块用来接收数据,即将旧数据覆盖储存新的数据即可。但是需要相应的逻辑代码控制列队的写和读是先入先出,且保证数据是有效的(防止读取的显存块是旧数据或者没有数据)。
  • 使用共享内存。

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

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

相关文章

基于SWOT法的信阳本土房地产企业现状及对策分析

目 录 摘要 1 Abstract 1 1绪论 2 2信阳市房地产企业概述 2 2.1中小城市房地产企业概念 2 2.2信阳本土房地产企业定位 3 2.2.1信阳市概况 3 2.2.2信阳市城市规划 3 2.2.3信阳房地产企业概况 4 2.3信阳市本土房地产企业特点 5 2.4研究信阳市本土房地产企业的必要性 6 3运用SWOT…

KBP310-ASEMI新能源整流桥KBP310

编辑&#xff1a;ll KBP310-ASEMI新能源整流桥KBP310 型号&#xff1a;KBP310 品牌&#xff1a;ASEMI 封装&#xff1a;KBP-4 正向电流&#xff08;Id&#xff09;&#xff1a;3A 反向耐压&#xff08;VRRM&#xff09;&#xff1a;1000V 正向浪涌电流&#xff1a;60A …

波司登高德康:创新引领品质为先 以匠心擦亮民族品牌

从“缝缝补补又三年”到国际潮流轮转&#xff0c;从“国民面料”的确良到功能性、高科技产品频出&#xff0c;从“绿蓝黑灰”走向五彩缤纷……百姓的衣着&#xff0c;是美好生活的生动注脚&#xff0c;也见证着我国服装产业从小到大、由大变强的变迁。 当前&#xff0c;我国经济…

基于SpringBoot的“农机电招平台”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“农机电招平台”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 农机机主注册界面图 农机界面图 …

2024计算机软考基本介绍、考试时间、考试科目等2024年软考新变化政策 证书的作用

专栏系列文章推荐&#xff1a; 2024高级系统架构设计师备考资料&#xff08;高频考点&真题&经验&#xff09;https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】&#xff08;2024年软考高级…

《JAVA与模式》之合成模式

系列文章目录 文章目录 系列文章目录前言一、合成模式二、安全式合成模式的结构三、透明式合成模式的结构四、两种实现方法的选择前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享…

力扣刷题104.二叉树的最大深度

给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&#xff1a;root [1,null,2] 输出…

SpringCloud远程调用为啥要采用HTTP,而不是RPC?

一个简单HTTP请求处理 RPC Restful&#xff08;HTTP&#xff09; RPC和HTTP的区别 关于SpringCloud远程调用采用HTTP而非RPC。 首先SpringCloud开启Web服务依赖于内部封装的Tomcat容器&#xff0c;而今信息飞速发展&#xff0c;适应大流量的微服务&#xff0c;采用Tomcat处…

在表格中循环插入表单

<template><div class"key">{{ruleForm.casesRange}}<el-form label-position"top" :model"ruleForm" refruleForm><el-form-item label"这个表格怎么写"><el-table :data"tableData" border>…

数据结构之队列详解(C语言手撕)

&#x1f389;个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名乐于分享在学习道路上收获的大二在校生 &#x1f648;个人主页&#x1f389;&#xff1a;GOTXX &#x1f43c;个人WeChat&#xff1a;ILXOXVJE &#x1f43c;本文由GOTXX原创&#xff0c;首发CSDN&…

JavaWeb--Maven

一&#xff1a;概述 1.简介 Maven 是专门用于管理和构建 Java 项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化的项目结构 提供了一套标准化的构建流程&#xff08;编译&#xff0c;测试&#xff0c;打包&#xff0c;发布 …… &#xff09; 提供了一套…

批量PDF转HTML:高效管理与优化文档格式

随着数字化时代的快速发展&#xff0c;PDF文件因其跨平台兼容性和良好的排版效果而被广泛应用。然而&#xff0c;在文本批量管理的场景中&#xff0c;我们可能需要将PDF文件转换为HTML格式&#xff0c;以便更好地进行编辑、搜索和定制。本文将为您介绍一种高效的方法&#xff0…

绝对位置编码与相对位置编码区别

本文作者&#xff1a; slience_me 文章目录 绝对位置编码与相对位置编码区别绝对位置编码相对位置编码区别和举例&#xff1a; 绝对位置编码与相对位置编码区别 绝对位置编码&#xff08;Absolute Positional Encoding&#xff09;和相对位置编码&#xff08;Relative Positio…

Vue.js+SpringBoot开发医院门诊预约挂号系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 功能性需求2.1.1 数据中心模块2.1.2 科室医生档案模块2.1.3 预约挂号模块2.1.4 医院时政模块 2.2 可行性分析2.2.1 可靠性2.2.2 易用性2.2.3 维护性 三、数据库设计3.1 用户表3.2 科室档案表3.3 医生档案表3.4 医生放号…

Hadoop伪分布式配置--没有DataNode或NameNode

一、原因分析 重复格式化NameNode 二、解决方法 1、输入格式化NameNode命令&#xff0c;找到data和name存放位置 ./bin/hdfs namenode -format 2、删除data或name&#xff08;没有哪个删哪个&#xff09; sudo rm -rf data 3、重新格式化NameNode 4、重新启动即可。

美国CPC认证证书详细介绍

美国CPC认证证书详细介绍 一、引言 美国CPC认证证书&#xff08;Childrens Product Certificate&#xff09;是针对儿童产品的一项重要认证。随着人们对儿童安全问题的日益关注&#xff0c;越来越多的家长和消费者开始关注儿童产品的质量和安全性。CPC认证证书作为一种权威的认…

多线程的典型例子——阻塞队列

文章目录 一、什么是阻塞队列&#xff1f;二、阻塞队列的功能2.1 线程安全2.2 具有阻塞功能 三、阻塞队列的作用(生产者——消费者模型的作用)3.1 生产者-消费者模型3.2 解耦合3.3 削锋填谷什么是消息队列什么是中间件&#xff1f; 四、阻塞队列的具体使用4.1 使用标准库提供的…

上拉电阻和上拉能力

大家好&#xff0c;我是记得诚。 关于上下拉电阻&#xff0c;之前写过一篇文章&#xff1a;聊一聊上拉电阻、下拉电阻、使用场景及阻值选择 有个做测试的读者&#xff0c;想转行硬件&#xff0c;之前一直在学习&#xff0c;也加入了我的硬件工程师小密圈 今天问了我一个问题…

Vue3全家桶 - VueRouter - 【6】导航守卫

导航守卫 查看以下情形&#xff1a; 点击主页链接时&#xff0c;默认情况下可直接进入指定页面&#xff0c;如下图&#xff0c;但是问题是该跳转的界面是需要用户登录后方可访问的&#xff1b; 可设置导航守卫来检测用户是否登录&#xff0c;如果已登录&#xff0c;则进入后台…

代码随想录day18(2)二叉树:翻转二叉树(leetcode226)

题目要求&#xff1a;将一棵二叉树翻转 思路&#xff1a;若想要翻转二叉树&#xff0c;只需要用swap函数将左右孩子节点翻转即可。注意前序和后序遍历均可&#xff0c;但是对于中序来说会将某些结点的左右孩子翻转了两次&#xff08;画图很明显&#xff09;&#xff0c;硬要用…