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 中推理實現部分的雛形😂

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/38079.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/38079.shtml
英文地址,請注明出處:http://en.pswp.cn/news/38079.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

原生JS手寫掃雷小游戲

場景 實現一個完整的掃雷游戲需要一些復雜的邏輯和界面交互。我將為你提供一個簡化版的掃雷游戲示例&#xff0c;幫助你入門。請注意&#xff0c;這只是一個基本示例&#xff0c;你可以根據自己的需求進行擴展和改進。 思路 創建游戲板&#xff08;Grid&#xff09;&#xff1…

軟考:中級軟件設計師:文件管理,索引文件結構,樹型文件結構,位示圖,數據傳輸方式,微內核

軟考&#xff1a;中級軟件設計師: 提示&#xff1a;系列被面試官問的問題&#xff0c;我自己當時不會&#xff0c;所以下來自己復盤一下&#xff0c;認真學習和總結&#xff0c;以應對未來更多的可能性 關于互聯網大廠的筆試面試&#xff0c;都是需要細心準備的 &#xff08;1…

小森動畫回憶錄(二)-瀏覽哆啦a夢的四次元口袋

// DoraemonProps結構用于存儲單個道具信息 struct DoraemonProps{// 道具名稱string name;// 道具用途string UseOfProps; };// 從文件加載哆啦A夢道具信息到vector void LoadDoraemonProps(vector<DoraemonProps>& DoraemonProps) {// 創建文件輸入流ifstream str…

人臉識別技術應用安全管理規定(試行)

近年來&#xff0c;人臉識別技術不斷成熟&#xff0c;已大量應用于治安管理、金融支付、門禁考勤等諸多領域&#xff0c;極大便捷了公眾生活。然而&#xff0c;人臉識別技術在得到廣泛應用的同時&#xff0c;仍存在一些不規范現象。人臉識別因其技術特點&#xff0c;涉及公眾敏…

node.js 基礎高并發案例

什么是高并發 高并發是指系統在同一時間段內需要處理大量的并發請求或同時進行大量的操作。在計算機領域中&#xff0c;高并發通常指的是在短時間內有大量的用戶或客戶端同時訪問系統或進行操作&#xff0c;對系統的并發處理能力提出了較高的要求。 高并發的特點包括 大量的…

Python學習筆記第五十五天(Pandas CSV文件)

Python學習筆記第五十五天 Pandas CSV 文件read_csv()to_string()to_csv() 數據處理head()tail()fillna() info() 后記 Pandas CSV 文件 CSV&#xff08;Comma-Separated Values&#xff0c;逗號分隔值&#xff0c;有時也稱為字符分隔值&#xff0c;因為分隔字符也可以不是逗號…

【嵌入式學習筆記】嵌入式入門7——IIC總線協議

1.IIC簡介 IIC即Inter Integrated Circuit&#xff0c;集成電路總線&#xff0c;是一種同步&#xff0c;串行&#xff0c;半雙工通信總線。 IIC總線協議——總線就是傳輸數據通道&#xff0c;協議就是傳輸數據的規則&#xff0c;有以下特點&#xff1a; 由時鐘線SCL和數據線S…

ES踩坑記錄之集群間通信異常造成節點無法加入

問題描述 公司新搭了一套ES集群&#xff0c;4臺機器&#xff0c;ES版本7.5.0&#xff0c;前期搭建十分順利&#xff0c;但集群運行一段時間后會出現問題。問題具體體現為節點間通訊異常&#xff0c;集群會重新選主&#xff0c;但選主之后只能通過新的主節點進行集群操作&#…

【Linux】可重入函數 volatile關鍵字 以及SIGCHLD信號

可重入函數 volatile關鍵字 以及SIGCHLD信號 一、可重入函數1、引入2、可重入函數的判斷 二、volatile關鍵字1、引入2、關于編譯器的優化的簡單討論 三、SIGCHLD信號 一、可重入函數 1、引入 我們來先看一個例子來幫助我們理解什么是可重入函數&#xff1a; 假設我們現在要對…

EthGlobal 巴黎站 Chainlink 獲獎項目介紹

在 Web3 中&#xff0c;每一周都至關重要。項目的發布、版本的發布以及協議的更新以驚人的速度推出。開發者必須保持學習&#xff0c;隨時了解最新的工具&#xff0c;并將所有他們所學的東西&#xff08;無論是舊的還是新的&#xff09;聯系起來&#xff0c;以構建推動 Web3 技…

PLUS操作流程、應用與實踐,多源不同分辨率數據的處理、ArcGIS的應用、PLUS模型的應用、InVEST模型的應用

PLUS模型是由中國地質大學&#xff08;武漢&#xff09;地理與信息工程學院高性能空間計算智能實驗室開發&#xff0c;是一個基于柵格數據的可用于斑塊尺度土地利用/土地覆蓋(LULC)變化模擬的元胞自動機(CA)模型。PLUS模型集成了基于土地擴張分析的規則挖掘方法和基于多類型隨機…

Word轉PDF在線轉換如何操作?分享轉換技巧

現如今&#xff0c;pdf轉換器已成為大家日常辦公學習必不可少的工具&#xff0c;市場上的pdf轉換器主要有兩種類型&#xff0c;一種是需要下載安裝的&#xff0c;另一種是網頁版&#xff0c;打開就可以使用的&#xff0c;今天小編給大家推薦一個非常好用的網頁版pdf轉換器&…

基于jvm-sandbox的imock開發指南

基于jvm-sandbox的imock開發指南 團隊今年的指標是為公司提供一個方法級的mock平臺&#xff0c; 這個重要的任務落在了我的身上。 0、明確團隊的需求 支持java后端服務方法級別的mock&#xff0c;對沒有測試環境的第三方服務進行mock&#xff0c;增加團隊覆蓋率。 啟用&#x…

PDF換行的難度,誰能解決?

換行的時候確認不了長度&#xff1a; import java.awt.*;public class Test {public static void main(String[] args) {String str1 "淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘";String str2 "AAAAAAAAAAAAAAAAAAAAAAAAA…

實驗篇——亞細胞定位

實驗篇——亞細胞定位 文章目錄 前言一、亞細胞定位的在線網站1. UniProt2. WoLFPSORT3. BUSCA4. TargetP-2.0 二、代碼實現1. 基于UniProt&#xff08;不會&#xff09;2. 基于WoLFPSORT后續&#xff08;已完善&#xff0c;有關代碼放置于[python爬蟲學習&#xff08;一&#…

一零七零、Redis基礎穩固篇

Redis是什么&#xff0c;優缺點&#xff1f; Redis本質是一個K-V類型的內存數據庫 純內存操作&#xff0c;每秒可處理超過10w的讀寫操作 優點&#xff1a; 讀寫性能極高 非阻塞IO 單線程 支持持久化 支持事務 數據結構豐富 缺點&#xff1a; 容易受到物理內存的限制 主機宕機可…

【JAVA】日志

輸出語句日志輸出位置只能是控制臺可以將日志信息寫入文件或數據庫中取消日志需要修改代碼&#xff0c;靈活性差只需修改日志文件多線程性能較差性能較好 日志規范接口&#xff1a;Commons Logging(JCL)、Simple Logging Facade for Java(slf4j) 日志實現框架&#xff1a;Log…

軟件測試簡歷撰寫與優化,讓你面試邀約率暴增99%!

如何撰寫一份優秀的簡歷呢&#xff1f;&#xff1f;這是一個求職者都會遇到的問題&#xff0c;今天就來詳細帶大家寫一份軟件測試工程師職位的簡歷&#xff01;希望能給各位軟件測試求職者一個帶來幫助&#xff01; 個人簡歷是求職者給招聘單位發的一份簡要介紹。包含自己的基本…

linux系統服務學習(一)Linux高級命令擴展

文章目錄 Linux高級命令&#xff08;擴展&#xff09;一、find命令1、find命令作用2、基本語法3、*星號通配符4、根據文件修改時間搜索文件☆ 聊一下Windows中的文件時間概念&#xff1f;☆ 使用stat命令獲取文件的最后修改時間☆ 創建文件時設置修改時間以及修改文件的修改時間…

【Vue】Vue2創建移動端項目實戰教程,創建移動端項目保姆級教程,設置axios,utils工具包,vue.fonfig.js配置項 (下)

系列文章目錄 這里是創建移動端項目 【Vue】Vue2.x創建項目全程講解&#xff0c;保姆級教程&#xff0c;手把手教&#xff0c;Vue2怎么創建項目&#xff08;上&#xff09; 【Vue】Vue2創建移動端項目實戰教程&#xff0c;創建移動端項目保姆級教程&#xff0c;接上一篇創建Vue…