当前位置: 首页 > news >正文

7.5.tensorRT高级(2)-RAII接口模式下的生产者消费者多batch实现

目录

    • 前言
    • 1. RAII接口模式封装生产者消费者
    • 2. 问答环节
    • 总结

前言

杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。

本次课程学习 tensorRT 高级-RAII 接口模式下的生产者消费者多 batch 实现

课程大纲可看下面的思维导图

在这里插入图片描述

1. RAII接口模式封装生产者消费者

这节课我们利用上节课学到的 RAII + 接口模式对我们的消费者生产者进行封装

我们来看代码

infer.hpp

#ifndef INFER_HPP
#define INFER_HPP#include <memory>
#include <string>
#include <future>class InferInterface{
public:virtual std::shared_future<std::string> forward(std::string pic) = 0;
};std::shared_ptr<InferInterface> create_infer(const std::string& file);#endif // INFER_HPP

infer.cpp

#include "infer.hpp"
#include <thread>
#include <queue>
#include <mutex>
#include <future>using namespace std;struct Job{shared_ptr<promise<string>> pro;string input;
};class InferImpl : public InferInterface{
public:virtual ~InferImpl(){worker_running_ = false;cv_notify_one();if(worker_thread_.joinable())worker_thread_.join();}bool load_model(const string& file){// 尽量保证资源哪里分配哪里释放,哪里使用,这样使得程序足够简单,而不是太乱// 线程内传回返回值的问题promise<bool> pro;worker_running_ = true;worker_thread_ = thread(&InferImpl::worker, this, file, std::ref(pro));return pro.get_future().get();}virtual shared_future<string> forward(string pic) override{// printf("使用 %s 进行推理\n", context_.c_str());// 往队列抛任务Job job;job.pro.reset(new promise<string>());job.input = pic;lock_guard<mutex> l(job_lock_);qjobs_.push(job);// 被动通知,一旦有新的任务需要推理,通知我即可// 发生通知的家伙cv_.notify_one();return job.pro->get_future();}// 实际执行模型推理的部分void worker(string file, promise<bool>& pro){// worker内实现,模型的加载,使用,释放string context = file;if(context.empty()){pro.set_value(false);return;}else{pro.set_value(true);}int max_batch_size = 5;vector<Job> jobs;int batch_id = 0;while(worker_running_){// 等待接受的家伙// 在队列取任务并执行的过程unique_lock<mutex> l(job_lock_);cv_.wait(job_lock_, [&](){// true 退出等待// false 继续等待return !qjobs_.empty() || !worker_running_;});// 程序发送终止信号if(!worker_running_)break;while(jobs.size() < max_batch_size && !qjobs_.empty()){jobs.emplace_back(qjobs_.front());qjobs.pop();}// 可以在这里一次拿一批出来,最大拿 maxbatchsize 个 job 进行一次性处理// jobs inference -> batch inference// 执行 batch 推理for(int i = 0; i < jobs.size(); ++i){auto& job = jobs[i];char result[100];sprintf(result, "%s : batch-> %d[%d]", job.input.c_str(), batch_id, jobs.size());job.pro->set_value(result);}batch_id++;jobs.clear();// 模拟推理耗时this_thread::sleep_for(chrono::milliseconds(1000));}// 释放模型printf("释放: %s\n", context.c_str());context.clear();printf("Worker done.\n");}
private:atomic<bool> worker_running_{false};thread worker_thread_;queue<Job> qjobs_;mutex job_lock_;condition_variable cv_;
};shared_ptr<InferInterface> create_infer(const string& file){shared_ptr<InferImpl> instance(new Infer());if(!instance->load_model(file))instance.reset();return instance;
}

main.cpp

#include "infer.hpp"int main(){auto infer = create_infer("a");if(infer == nullptr){printf("failed.\n");return -1;}// 串行// auto fa = infer->forward("A").get();// auto fb = infer->forward("B").get();// auto fc = infer->forward("C").get();// printf("%s\n", fa.c_str());// printf("%s\n", fb.c_str());// printf("%s\n", fc.c_str());// 并行auto fa = infer->forward("A");auto fb = infer->forward("B");auto fc = infer->forward("C");printf("%s\n", fa.get().c_str());printf("%s\n", fb.get().c_str());printf("%s\n", fc.get().c_str());    printf("Program done.\n");return 0;
}

上述示例代码相对复杂,结合了 RAII 和接口模式来实现模拟模型推理,具体是一个消费者-生产者模式的异步批处理机制,我们来简单解读下 infer.cpp 中具体干了些啥(form chatGPT

1. 数据结构和类定义

  • Job 结构体:这是一个任务结构,包含了一个 promise 对象(用于在工作线程中设置结果)和输入数据,promise 又通过 shared_ptr 封装了一层,可以让结构体传递效率更高
  • InferImpl 类,这是 InferInterface 的实现类,包含了异步处理的核心逻辑

2. InferImpl 类的方法和成员

  • 析构函数:在对象销毁时,将 worker_running_ 标志设置为 false,并通过条件变量唤醒工作线程。然后等待工作线程结束
  • load_model 方法:模型加载函数,它实际上启动了工作线程,并传递了一个 promise 对象来设置是否成功加载了模型
  • forward 方法:这是暴露给使用者的接口,用于提交一个新的推理任务。这个方法将任务添加到队列中,并通过条件变量唤醒工作线程
  • worker 方法:这是工作线程的核心函数,它从队列中取出任务并批量处理它们,然后使用 promise 设置结果
  • 私有成员
    • worker_running_:一个原子布尔标志,表示工作线程是否正在运行
    • worker_thread_:工作线程对象
    • qjobs_:包含待处理任务的队列
    • job_lock_:保护任务队列的互斥锁
    • cv_:条件变量,用于在有新任务到来或工作线程需要停止时唤醒工作线程

3. 工厂函数

  • create_infer 函数:RAII 的体现,这个函数创建了一个 InferImpl 的实例,并尝试加载模型。如果加载失败,它将返回一个空的智能指针。

这个示例清晰地展示了如何使用 RAII 和接口模式来实现一个异步批处理机制,同时也展示了如何使用 C++11 的并发特性(如 threadpromisecondition_variable 等)来实现这种机制。

2. 问答环节

博主对多线程相关的知识不怎么了解,因此疯狂询问 chatGPT,故此做个记录方便下次查看,以下内容来自于博主和 chatGPT 之间的对话

问题1:work_running_ 为什么是 atomic<boll> 类型,为什么不直接使用 bool 类型?什么是 atomic<bool> 类型?🤔

回答atomic<bool> 是 C++11 引入的原子类型,它是为了支持多线程环境中无锁的线程安全操作。使用原子类型可以确保对该类型的读写操作是原子的,即它们不会被其他线程中的操作中断。

当多个线程可能同时访问和修改一个变量时,直接使用基本数据类型(如 bool)可能会导致不确定的行为或数据竞态(race conditions)。数据竞态通常是多线程编程中的一个问题,当两个或更多的线程同时访问同一资源,并至少有一个线程修改该资源时,结果是不确定的。

使用 atomic<bool> 的好处:

1. 原子操作atomic<bool> 保证了对其的读写操作是原子的,这意味着每个读或写操作都是不可中断的。

2. 内存顺序:原子操作还提供了内存顺序语义,这确保了在多线程环境中的操作顺序。

3. 无锁操作:与使用互斥锁相比,原子操作通常更快,因为它们通常是无锁的。

在示例中,atomic<bool> 可能会在主线程中被设置,并在工作线程中被检查。为了确保这种跨线程的通信是线程安全的,使用 atomic<bool> 是合适的。如果只使用普通的 bool,可能会导致数据竞态,从而引起不可预测的行为。

总之,atomic<bool> 提供了一种线程安全的方式来读取和修改一个布尔值,特别是在无需使用互斥锁的情况下。

问题2:我在 CUDA 编程中也听过原子操作,就是 GPU 中多个线程对同一个变量进行修改时要使用原子操作,这跟你提到的原子类型是不是类似的呢?🤔

回答:是的,CUDA 中的原子操作和 C++ 中的原子类型在概念上是非常相似的。两者都是为了确保在并发环境中对数据的访问和修改是线程安全的。

1. CUDA 中的原子操作

  • CUDA 提供了一系列原子函数,如 atomicAdd, atomicSub 等,这些函数可以确保在多个线程可能同时访问同一内存位置时,这些操作是原子的。
  • 这在 GPU 编程中尤为重要,因为 GPU 具有大量的并行执行单元,这使得多个线程可能会同时访问同一个内存位置。
  • 使用原子操作可以避免数据竞态,并确保正确的结果。

2. C++ 中的原子类型

  • C++ 的 std::atomic 类模板提供了一系列原子操作,这些操作确保在多线程环境中对数据的读取和写入是线程安全的,而无需使用互斥锁。
  • 这对于 CPU 的多
    • 线程编程非常有用,因为多个线程可能会并发地运行,并尝试访问和修改同一块内存。

两者的共同点是它们都提供了一种线程安全的方式来处理并发的数据访问和修改。不同之处在于它们分别是为 GPU 和 CPU 设计的,并具有各自的特定语境和使用场景。

总的来说,无论是 CUDA 的原子操作还是 C++ 的原子类型,其核心目的都是为了提供一种在并发环境中安全访问和修改数据的机制。

总结

本次课程学习了 RAII 接口模式下的生产者消费者多 batch 实现。把前面学的知识全都串起来了,首先通过 create_infer 创建 infer 资源并对模型进行加载,这是 RAII 体现,其次该函数返回的是接口类的智能指针,因此用户只能看到 forward 这一个接口,这是接口模式的应用

在 InferImpl 具体实现类中,我们通过 forward 函数不断向队列中添加数据,而 worker 函数则会判断队列中的数据是否为空,若不为空则进行推理,若为空则继续等待,是否等待是通过条件变量 condition_variable 的 wait 和 notify_one 来实现的,另外 worker 线程将推理结果返回到 forward 中是通过 promise 和 future 来实现,值得注意的是我们在 forward 中返回的并不是 future.get() 而是直接返回的一个 future 对象,具体什么时候 get 拿结果用使用者决定

这个示例把生产者和消费者模式、RAII接口模式以及异步机制等都结合起来,有点像 tensorRT_Pro 中推理实现部分的雏形😂

相关文章:

7.5.tensorRT高级(2)-RAII接口模式下的生产者消费者多batch实现

目录 前言1. RAII接口模式封装生产者消费者2. 问答环节总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 高级-RAI…...

华为OD-最大括号深度

题目描述 一个合法的括号匹配序列有以下定义: 1、空串""是一个合法的括号匹配序列 2、如果"X"和"Y"都是合法的括号匹配序列,"XY"也是一个合法的括号匹配序列 3、如果"X"是一个合法的括号匹配序列,那么"(X)"也是一…...

【Leetcode】108. 将有序数组转换为二叉搜索树

一、题目 1、题目描述 给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 示例1: 输入:nums = [-10,-3,0,5,9] 输出:[0,-3,9,-1…...

【树莓派打怪升级】:玩转个人Web世界!

文章目录 概述使用 Raspberry Pi Imager 安装 Raspberry Pi OS设置 Apache Web 服务器测试 web 站点安装静态样例站点 将web站点发布到公网安装 Cpolarcpolar进行token认证生成cpolar随机域名网址生成cpolar二级子域名将参数保存到cpolar配置文件中测试修改后配置文件配置cpola…...

mysql5.7在centos环境下的常见故障

1. GPG Keys升级导致安装失败 错误信息&#xff1a; The GPG keys listed for the "MySQL 5.7 Community Server" repository are already installed but they are not correct for this package. Check that the correct key URLs are configured for this reposi…...

hive中get_json_object函数不支持解析json中文key

问题 今天在 Hive 中 get_json_object 函数解析 json 串的时候&#xff0c;发现函数不支持解析 json 中文 key。 例如&#xff1a; select get_json_object({ "姓名":"张三" , "年龄":"18" }, $.姓名);我们希望的结果是得到姓名对应…...

Azure VM上意外禁用NIC如何还原恢复

创建一个windows虚拟机&#xff0c;并远程连接管理员的方式打开powershell 首先查看虚拟网卡&#xff0c;netsh interface show interface 然后禁用虚拟网卡 ,netsh interface set interface Ethernet disable 去Azure虚拟机控制台&#xff0c;打开串行控制台 控制台中键入cmd,…...

神经网络简单理解:机场登机

目录 神经网络简单理解&#xff1a;机场登机 ​编辑 激活函数&#xff1a;转为非线性问题 ​编辑 激活函数ReLU 通过神经元升维&#xff08;神经元数量&#xff09;&#xff1a;提升线性转化能力 通过增加隐藏层&#xff1a;增加非线性转化能力​编辑 模型越大&#xff0c;…...

Sping源码(七)— 后置处理器

简单回顾一下上一篇文章&#xff0c;是在BeanFacroty创建完之后&#xff0c;可以通过Editor和EditorRegistrar实现对类属性的自定义扩展&#xff0c;以及忽略要自动装配的Aware接口。 本篇帖子会顺着refresh()主流程方法接着向下执行。在讲invokeBeanFactoryPostProcessors方法…...

docker导出、导入镜像、提交

导出镜像到本地&#xff0c;然后可以通过压缩包的方式传输。 导出&#xff1a;docker image save 镜像名:版本号 > /home/quxiao/javatest.tgz 导入&#xff1a;docker image load -i /home/quxiao/javatest.tgz 删除镜像就得先删除容器&#xff0c;当你每运行一次镜像&…...

shell的变量

一、什么是变量 二、变量的命名 三、查看变量的值 env显示全局变量&#xff0c;刚刚定义的root_mess是局部变量 四、变量的定义 旧版本&#xff08;7、8四个文件都加载&#xff09;和新版本&#xff08;9只加载两个etc&#xff09;不一样&#xff0c;所以su - 现在要永久生效在…...

CentOS系统环境搭建(十三)——CentOS7安装nvm

centos系统环境搭建专栏&#x1f517;点击跳转 CentOS7.9安装nvm 文章目录 CentOS7.9安装nvm1.安装2.刷新系统环境3.查看所有node4.安装Node.js版本5.查看已安装版本号6.使用指定版本7.设置默认版本8.验证 在我们的日常开发中经常会遇到这种情况&#xff1a;手上有好几个项目&…...

uniapp评论列表插件获取

从评论列表&#xff0c;回复&#xff0c;点赞&#xff0c;删除&#xff0c;留言板 - DCloud 插件市场里导入&#xff0c;并使用。 代码样式优化及接入如下&#xff1a; <template><view class"hb-comment"><!-- 阅读数-start --><view v-if&q…...

3.redis数据结构之List

List-列表类型:L&R 列表类型&#xff1a;有序、可重复 Arraylist和linkedlist的区别 Arraylist是使用数组来存储数据&#xff0c;特点&#xff1a;查询快、增删慢 Linkedlist是使用双向链表存储数据&#xff0c;特点&#xff1a;增删快、查询慢&#xff0c;但是查询链表两端…...

安装使用MySQL8遇到的问题记录

1、root密码 启动运行后 /var/log/mysqld.log 存在默认密码 2023-08-21T15:58:17.469516Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.34) initializing of server in progress as process 61233 2023-08-21T15:58:17.478009Z 1 [System] [MY-013576] [I…...

Mysql、Oracle 中锁表问题解决办法

MySQL中锁表问题的解决方法&#xff1a; 1. 确定锁定表的原因&#xff1a; 首先&#xff0c;需要确定是什么原因导致了表的锁定。可能的原因包括长时间的事务、大量的并发查询、表维护操作等。 2. 查看锁定信息&#xff1a; 使用以下命令可以查看当前MySQL数据库中的锁定信…...

AUTOSAR规范与ECU软件开发(实践篇)5.1 ETAS ISOLAR-A工具简介

前言 如前所述, 开发者可以先在系统级设计工具ISOLAR-A中设计软件组件框架, 包括端口接口、 端口等, 即创建各软件组件arxml描述性文件; 再将这些软件组件描述性文件导入到行为建模工具, 如Matlab/Simulink中完成内部行为建模。 亦可以先在行为建模工具中完成逻辑建模, 再…...

shell脚本——expect脚本免交互

目录 一.Here Document 1.1.定义 1.2.多行重定向 二.expect实现免交互 2.1.基础免交互改密码 2.2.expect定义 2.3.expect基本命令 2.4.expect实现免交互ssh主机 一.Here Document 1.1.定义 使用I/O重定向的方式将命令列表提供给交互式程序&#xff0c;是标准输 入的一…...

ubuntu18.04安装远程控制软件ToDest方法,针对官网指令报错情况

有时我们在家办公&#xff0c;需要控制实验室的笔记本&#xff0c;因此好用的远程控制软件会让我们的工作事半功倍&#xff01; 常用的远程控制软件有ToDesk&#xff0c;向日葵&#xff0c;以及TeamViewer&#xff0c;但是为感觉ToDesk更流畅一些&#xff0c;所以这里介绍一下…...

系统架构设计师之缓存技术:Redis持久化的两种方式-RDB和AOF

系统架构设计师之缓存技术&#xff1a;Redis持久化的两种方式-RDB和AOF...

Sentinel-3B OLCI 3 级全球分箱地球观测降分辨率(ERR)叶绿素(CHL)数据,版本 2022.0

Sentinel-3B OLCI Level-3 Global Binned Earth-observation Reduced Resolution (ERR) Chlorophyll (CHL) Data, version 2022.0 简介 叶绿素 a 数据集提供全球网格化的表层叶绿素 a 浓度&#xff08;浮游植物生物量的替代指标&#xff09;合成数据。CHL 支持时间序列和气候…...

用STM32CubeMX和HAL库快速上手WS2812B:告别手动计算延时,一键生成驱动框架

基于STM32CubeMX的WS2812B智能灯光控制&#xff1a;从零构建现代化驱动方案在智能硬件和物联网设备快速发展的今天&#xff0c;WS2812B可编程LED灯带因其丰富的色彩表现和简单的单线控制方式&#xff0c;成为创客和工程师们最喜爱的显示组件之一。然而&#xff0c;传统的寄存器…...

告别外部中断!用EnableInterrupt库轻松搞定Arduino Nano多通道PWM读取(附完整代码)

Arduino Nano多通道PWM读取实战&#xff1a;用EnableInterrupt突破硬件限制当你用Arduino Nano开发四轴飞行器或机器人项目时&#xff0c;是否遇到过这样的尴尬&#xff1a;遥控器的四个通道PWM信号需要同时读取&#xff0c;但Nano只有两个外部中断引脚&#xff1f;这个问题困扰…...

3分钟掌握HashCalculator:你的文件完整性守护专家

3分钟掌握HashCalculator&#xff1a;你的文件完整性守护专家 【免费下载链接】HashCalculator 哈希值计算工具&#xff0c;批量计算/批量校验/查找重复文件/改变哈希值等&#xff0c;支持集成到系统右键菜单 项目地址: https://gitcode.com/gh_mirrors/ha/HashCalculator …...

37家金融客户紧急启用的DeepSeek扫描辅助加固包(含未公开API调用密钥策略)

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;DeepSeek漏洞扫描辅助的背景与战略价值 近年来&#xff0c;大模型在安全领域的应用正从辅助问答向深度协同防御演进。DeepSeek系列模型凭借其开源、高推理精度及强代码理解能力&#xff0c;成为构建智能化漏洞…...

Windows Cleaner深度解析:5大核心模块彻底解决系统空间不足问题

Windows Cleaner深度解析&#xff1a;5大核心模块彻底解决系统空间不足问题 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服&#xff01; 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner Windows Cleaner是一款完全免费开源的…...

Postgresql基础实践教程(九)

⭐️⭐️⭐️⭐️⭐️ 完整数据详见 练习数据免费 ⭐️⭐️⭐️⭐️⭐️ 七十二、WITH查询&#xff08;公用表表达式CTE&#xff09; 1. SELECT 中的 WITH 2. 递归查询 3. 公用表表达式的物化 4. WITH中的数据修改语句 WITH提供了一种在主查询中写辅助语句的方法。这些语…...

为Claude Code配置稳定API源并解决访问限制

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 为Claude Code配置稳定API源并解决访问限制 Claude Code 作为一款强大的 AI 编程辅助工具&#xff0c;其原生服务在某些情况下可能…...

PCL 基于强度的双边滤波【2026最新版】

目录 一、算法原理 1、计算步骤 2、算法源码 3、函数解析 4、参考文献 二、代码实现 三、结果展示 四、滤波后未发生变化的原因 五、解决办法 六、结果展示 七、相关链接 本文由CSDN点云侠原创,博客长期更新,本文最近一次更新时间为:2026年5月24日。 一、算法原理 1、计算…...

CentOS 8.5最小化安装后,这5个必做的安全与效率优化设置(附一键脚本)

CentOS 8.5最小化安装后的5个必做安全与效率优化刚完成CentOS 8.5最小化安装的系统就像一张白纸——干净但缺乏生产力。作为运维老手&#xff0c;我见过太多人跳过基础优化直接部署应用&#xff0c;结果在后续使用中频繁遇到权限混乱、软件安装慢、SSH爆破等问题。本文将分享我…...