【tensorRT从零起步高性能部署】21-TensorRT基础-实现模型的推理过程

1. inference案例:推理全流程详解

voidinference(){// ------------------------------ 1. 准备模型并加载 ----------------------------TRTLogger logger;autoengine_data=load_file("engine.trtmodel");// 执行推理前,需要创建一个推理的runtime接口实例。与builer一样,runtime需要logger:nvinfer1::IRuntime*runtime=nvinfer1::createInferRuntime(logger);// 将模型从读取到engine_data中,则可以对其进行反序列化以获得enginenvinfer1::ICudaEngine*engine=runtime->deserializeCudaEngine(engine_data.data(),engine_data.size());if(engine==nullptr){printf("Deserialize cuda engine failed.\n");runtime->destroy();return;}nvinfer1::IExecutionContext*execution_context=engine->createExecutionContext();cudaStream_t stream=nullptr;// 创建CUDA流,以确定这个batch的推理是独立的cudaStreamCreate(&stream);/* Network definition: image | linear (fully connected) input = 3, output = 2, bias = True w=[[1.0, 2.0, 0.5], [0.1, 0.2, 0.5]], b=[0.3, 0.8] | sigmoid | prob */// ------------------------------ 2. 准备好要推理的数据并搬运到GPU ----------------------------floatinput_data_host[]={1,2,3};float*input_data_device=nullptr;floatoutput_data_host[2];float*output_data_device=nullptr;cudaMalloc(&input_data_device,sizeof(input_data_host));cudaMalloc(&output_data_device,sizeof(output_data_host));cudaMemcpyAsync(input_data_device,input_data_host,sizeof(input_data_host),cudaMemcpyHostToDevice,stream);// 用一个指针数组指定input和output在gpu中的指针。float*bindings[]={input_data_device,output_data_device};// ------------------------------ 3. 推理并将结果搬运回CPU ----------------------------boolsuccess=execution_context->enqueueV2((void**)bindings,stream,nullptr);cudaMemcpyAsync(output_data_host,output_data_device,sizeof(output_data_host),cudaMemcpyDeviceToHost,stream);cudaStreamSynchronize(stream);printf("output_data_host = %f, %f\n",output_data_host[0],output_data_host[1]);// ------------------------------ 4. 释放内存 ----------------------------printf("Clean memory\n");cudaStreamDestroy(stream);execution_context->destroy();engine->destroy();runtime->destroy();// ------------------------------ 5. 手动推理进行验证 ----------------------------constintnum_input=3;constintnum_output=2;floatlayer1_weight_values[]={1.0,2.0,0.5,0.1,0.2,0.5};floatlayer1_bias_values[]={0.3,0.8};printf("手动验证计算结果:\n");for(intio=0;io<num_output;++io){floatoutput_host=layer1_bias_values[io];for(intii=0;ii<num_input;++ii){output_host+=layer1_weight_values[io*num_input+ii]*input_data_host[ii];}// sigmoidfloatprob=1/(1+exp(-output_host));printf("output_prob[%d] = %f\n",io,prob);}}

先明确推理的核心目标:加载上一节课保存的engine.trtmodel,输入[1,2,3],让TRT算出输出,再手动计算验证结果是否一致。

// 辅助函数:读取二进制文件(比如engine.trtmodel)到内存#include<fstream>#include<vector>usingnamespacestd;vector<unsignedchar>load_file(conststring&file){ifstreamin(file,ios::in|ios::binary);if(!in.is_open())return{};in.seekg(0,ios::end);size_t length=in.tellg();vector<unsignedchar>data;if(length>0){in.seekg(0,ios::beg);data.resize(length);in.read((char*)&data[0],length);}in.close();returndata;}

推理代码逐段拆解(6大步骤)

原代码的推理流程分为6个核心步骤,我们逐段讲透,补充每个步骤的“为什么”和“怎么来的”。

步骤1:准备模型并加载(搬出造好的机器)
voidinference(){// ------------------------------ 1. 准备模型并加载 ----------------------------TRTLogger logger;// 日志对象:记录推理过程中的警告/错误(必须有)autoengine_data=load_file("engine.trtmodel");// 读取保存的模型文件到内存// 1.1 创建推理运行时(Runtime):相当于“机器的启动器”,负责加载和运行enginenvinfer1::IRuntime*runtime=nvinfer1::createInferRuntime(logger);// 1.2 反序列化engine:把二进制的模型文件还原成可运行的“机器本体”nvinfer1::ICudaEngine*engine=runtime->deserializeCudaEngine(engine_data.data(),engine_data.size());if(engine==nullptr){// 反序列化失败(比如模型文件损坏、TRT版本不匹配)printf("Deserialize cuda engine failed.\n");runtime->destroy();// 及时释放资源,避免内存泄漏return;}

通俗解读

  • IRuntime:机器的“启动器”——只有通过它,才能把硬盘里的engine.trtmodel(二进制文件)变成内存中可运行的ICudaEngine
  • deserializeCudaEngine:“组装机器”的过程——把二进制的“零件图纸”还原成能干活的“机器”;
  • 常见失败原因:模型文件损坏、编译模型的TRT版本和推理的TRT版本不一致(比如用TRT8.6编译,用TRT8.2推理)、GPU架构不匹配。
步骤2:创建执行上下文和CUDA流(给机器装操作面板+流水线)
// 2.1 创建执行上下文(ExecutionContext):相当于“机器的操作面板”nvinfer1::IExecutionContext*execution_context=engine->createExecutionContext();// 2.2 创建CUDA流(Stream):相当于“独立的流水线”,异步处理推理任务cudaStream_t stream=nullptr;cudaStreamCreate(&stream);

通俗解读

  • IExecutionContext:一个engine(机器)可以创建多个execution_context(操作面板)——比如一台机器有多个操作面板,能同时给不同的工人用(支持多批次并行推理);
  • CUDA流
    • 没有流的话,所有操作(数据拷贝、推理)都是“同步的”:等上一个做完才能做下一个;
    • 有流的话,操作是“异步的”:把任务丢到流里,CPU不用等GPU做完,能继续干别的——比如拷贝数据的同时准备下一批数据,提高效率;
    • cudaStreamCreate(&stream):创建一条空的流水线。
步骤3:准备输入数据并搬运到GPU(给机器喂原材料)
/* 回顾上一节课的网络结构: 输入(3维) → 全连接层(2维) → Sigmoid → 输出(2维) */// 3.1 主机(CPU)上准备输入数据:原材料(比如[1,2,3])floatinput_data_host[]={1,2,3};float*input_data_device=nullptr;// GPU上的输入数据指针(空)// 3.2 主机(CPU)上准备输出缓存:装成品的空盒子floatoutput_data_host[2];float*output_data_device=nullptr;// GPU上的输出数据指针(空)// 3.3 给GPU分配内存:在GPU上造“原材料仓库”和“成品仓库”cudaMalloc(&input_data_device,sizeof(input_data_host));cudaMalloc(&output_data_device,sizeof(output_data_host));// 3.4 异步拷贝数据:把CPU的原材料搬到GPU的仓库(丢到流里,异步执行)cudaMemcpyAsync(input_data_device,input_data_host,sizeof(input_data_host),cudaMemcpyHostToDevice,stream);// 3.5 定义bindings(绑定):告诉机器“原材料仓库”和“成品仓库”的地址// bindings顺序:必须和构建模型时addInput、markOutput的顺序一致!float*bindings[]={input_data_device,output_data_device};

关键细节(新手必看)

  1. bindings是推理的核心——相当于机器的“输入输出接口”:
    • 构建模型时,我们先addInput("image", ...)(第一个接口),再markOutput(...)(第二个接口);
    • 所以bindings[0]必须是输入的GPU指针,bindings[1]必须是输出的GPU指针;
    • 如果有多个输入/输出(比如2个输入、3个输出),bindings顺序要严格对应addInputmarkOutput的顺序,否则推理结果错乱或报错。
  2. cudaMemcpyAsyncvscudaMemcpy
    • cudaMemcpy:同步拷贝——CPU等GPU拷贝完才继续;
    • cudaMemcpyAsync:异步拷贝——把拷贝任务丢到流里,CPU立刻继续执行下一行代码,效率更高;
    • 最后一个参数stream:指定拷贝任务丢到哪条流水线。
步骤4:推理并将结果搬运回CPU(机器干活,取出成品)
// 4.1 启动异步推理:按下机器的“启动键”,把任务丢到流里boolsuccess=execution_context->enqueueV2((void**)bindings,stream,nullptr);// 4.2 异步拷贝结果:把GPU成品仓库的东西搬回CPU的空盒子(丢到流里)cudaMemcpyAsync(output_data_host,output_data_device,sizeof(output_data_host),cudaMemcpyDeviceToHost,stream);// 4.3 同步流:等待流里的所有任务(拷贝输入、推理、拷贝输出)都做完cudaStreamSynchronize(stream);// 4.4 打印推理结果printf("TensorRT推理结果:output_data_host = %f, %f\n",output_data_host[0],output_data_host[1]);

通俗解读

  • enqueueV2:“启动机器”的核心接口(V2对应显性batch size,上一节课用的createNetworkV2(1));
    • 参数1:(void**)bindings——输入输出的GPU指针数组(必须转成void**类型);
    • 参数2:stream——把推理任务丢到这条流水线;
    • 参数3:nullptr——暂时不用(是针对多线程的同步参数);
  • cudaStreamSynchronize(stream):“等流水线干完活”——如果不加这行,CPU可能在GPU还没算完的时候就打印结果,拿到的是随机值;
  • 为什么要异步+同步:既利用异步提高效率,又保证结果能正确拿到。
步骤5:释放内存(用完机器,清理现场)
// 5.1 打印提示printf("Clean memory\n");// 5.2 释放资源:按“创建顺序倒序”释放,避免内存泄漏cudaStreamDestroy(stream);// 销毁流水线execution_context->destroy();// 销毁操作面板engine->destroy();// 销毁机器本体runtime->destroy();// 销毁启动器

关键规则

  • TRT的资源(runtime、engine、context)必须手动destroy(),不像C++普通对象会自动析构;
  • CUDA的流必须cudaStreamDestroy,GPU内存如果是cudaMalloc分配的,也要cudaFree(原代码里漏了,补充修正:
    // 补充:释放GPU内存(原代码漏了,新手一定要加)cudaFree(input_data_device);cudaFree(output_data_device);
  • 释放顺序:后创建的先释放(流→上下文→engine→runtime)。
步骤6:手动推理验证(核对机器干活的结果是否正确)

为了确认TRT的推理结果没错,我们手动复现“全连接+Sigmoid”的计算过程:

// 6.1 定义和上一节课一致的权重、偏置constintnum_input=3;constintnum_output=2;floatlayer1_weight_values[]={1.0,2.0,0.5,0.1,0.2,0.5};// 2个输出×3个输入floatlayer1_bias_values[]={0.3,0.8};// 6.2 手动计算printf("\n手动验证计算结果:\n");for(intio=0;io<num_output;++io){// 遍历2个输出神经元// 第一步:计算全连接层输出 = 偏置 + Σ(权重×输入)floatoutput_host=layer1_bias_values[io];// 先加偏置for(intii=0;ii<num_input;++ii){// 遍历3个输入// 权重索引:io×num_input + ii → 第io个输出神经元的第ii个输入权重output_host+=layer1_weight_values[io*num_input+ii]*input_data_host[ii];}// 第二步:Sigmoid激活 = 1 / (1 + e^(-x))floatprob=1/(1+exp(-output_host));printf("output_prob[%d] = %f\n",io,prob);}}

手动计算过程(代入数值)
我们把输入[1,2,3]代入,一步步算,验证和TRT结果一致:

  • 第一个输出神经元(io=0):
    全连接:0.3 + (1.0×1) + (2.0×2) + (0.5×3) = 0.3 + 1 + 4 + 1.5 = 6.8;
    Sigmoid:1/(1+e^(-6.8)) ≈ 1/(1+0.0011) ≈ 0.9989;
  • 第二个输出神经元(io=1):
    全连接:0.8 + (0.1×1) + (0.2×2) + (0.5×3) = 0.8 + 0.1 + 0.4 + 1.5 = 2.8;
    Sigmoid:1/(1+e^(-2.8)) ≈ 1/(1+0.0608) ≈ 0.9427;
  • 最终TRT输出的两个值就是这两个结果,说明推理正确。

完整运行效果(补充)

运行代码后,控制台会输出:

TensorRT推理结果:output_data_host = 0.998897, 0.942676 手动验证计算结果: output_prob[0] = 0.998897 output_prob[1] = 0.942676

两者结果完全一致,证明TRT推理流程正确。

补充:新手必知的关键知识点

1. bindings的顺序是“命脉”

  • 构建模型时:addInput的顺序是第0个binding,markOutput的顺序是第1个binding;
  • 推理时:bindings[0]必须是输入的GPU指针,bindings[1]必须是输出的GPU指针;
  • 如何确认顺序/维度:可以用engine的接口查询(新手调试用):
    // 打印binding的名称和维度(调试用)for(inti=0;i<engine->getNbBindings();i++){printf("binding[%d] name: %s, shape: ",i,engine->getBindingName(i));autodims=engine->getBindingDimensions(i);printf("(%d, %d, %d, %d)\n",dims.d[0],dims.d[1],dims.d[2],dims.d[3]);}

2. 异步推理的核心逻辑

  • 流(stream)是“独立的任务队列”:不同流的任务互不干扰;
  • 异步操作(cudaMemcpyAsyncenqueueV2)只是“把任务加入队列”,不会等待执行;
  • 必须用cudaStreamSynchronizecudaDeviceSynchronize等待任务完成,否则会拿到错误结果。

3. 一个engine可以创建多个上下文

  • 比如:engine->createExecutionContext()可以调用多次,得到多个execution_context
  • 用途:支持多线程并行推理——每个线程用一个上下文,共用一个engine(节省内存);
  • 注意:每个上下文需要独立的bindings和流,避免冲突。

4. 常见推理报错原因

报错现象常见原因
反序列化engine失败TRT版本不匹配、模型文件损坏、GPU架构不匹配
enqueueV2返回falsebindings顺序错、GPU内存不足、维度不匹配
结果是随机值没同步流、bindings指针是CPU指针(不是GPU)
内存泄漏没调用destroy()/cudaFree()/cudaStreamDestroy

总结:推理核心步骤与关键点

核心步骤(6步走)

  1. 加载模型load_file读二进制文件 →createInferRuntimedeserializeCudaEngine
  2. 创建上下文和流createExecutionContextcudaStreamCreate
  3. 数据准备:CPU准备数据 →cudaMalloc分配GPU内存 →cudaMemcpyAsync拷贝到GPU;
  4. 推理+结果拷贝enqueueV2启动推理 →cudaMemcpyAsync拷贝结果 →cudaStreamSynchronize同步;
  5. 释放资源:按倒序destroy TRT资源、销毁流、释放GPU内存;
  6. 验证结果:手动复现计算逻辑,确认推理正确。

关键要点

  1. bindings顺序必须和构建模型时的输入输出顺序一致,且必须是GPU指针;
  2. 异步操作(Async)要配合cudaStreamSynchronize使用,否则结果错误;
  3. TRT资源(runtime/engine/context)必须手动destroy(),CUDA资源要手动释放;
  4. 推理用的TRT版本、GPU架构要和编译模型时一致,否则反序列化失败。

是TensorRT部署的“通用模板”——不管是简单的全连接模型,还是复杂的YOLO模型,推理的核心步骤都是这6步,只是输入输出的维度、网络结构不同而已。吃透这个案例,你就掌握了TensorRT推理的核心逻辑。

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

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

相关文章

AD25 —走线时如何添加过孔(加过孔不换层 / 加过孔换层)

走线模式下&#xff0c;可以直接加过孔&#xff1a; 不换层&#xff1a;按2换层&#xff1a;按小键盘上的&#xff1a;*

MySQL数据可视化实战:从查询到图表的全流程

数据可视化是将枯燥的数据库数据转化为直观图表的核心手段&#xff0c;而 MySQL 作为最常用的关系型数据库&#xff0c;并非只能做数据存储和查询 —— 结合合理的查询技巧与可视化工具&#xff0c;你可以用 MySQL 快速实现从 “数据提取” 到 “图表展示” 的全链路可视化分析…

新一代城市轨道交通云原生智能体协同架构:从智慧中枢到自主进化的未来

目录 1. 引言&#xff1a;架构演进是可持续发展的技术先导 2. 理论基础与架构设计 3. 关键协同机制与运行范式 4. 应用价值与行业变革意义 5. 实施挑战与演进路径 6. 结论 摘要&#xff1a;随着城市轨道交通迈入以“云数智”深度融合为特征的新阶段&#xff0c;传统的中心…

输入某水果店的水果名称,进价,售价,库存,计算库存预警值(库存低于10斤),输出需补货的水果。

为你完整设计一个水果店库存预警与补货分析系统&#xff0c;结合大数据与智能管理课程的思想&#xff0c;从场景到代码、从模块到文档&#xff0c;全部覆盖。1. 实际应用场景 & 痛点引入场景你是某水果店的老板或库存管理员&#xff0c;手头有水果数据&#xff08;水果名称…

AI赋能工作全攻略:从小白到高手的实用指南(建议收藏)

AI作为"智商情商双高的实习生"&#xff0c;可通过高容错、高频次方式融入日常工作。文章从数字化与AI关系入手&#xff0c;详述生成式AI应用方法&#xff0c;提出"1个秘密、2个心法、3个行动、4个资源、5个阶段"框架&#xff0c;指导读者从简单聊天框应用起…

Jetson Orin Secure Boot 完整笔记:shim、L4TLauncher、GRUB 的关系与实战落地

&#x1f4fa; B站视频讲解&#xff08;Bilibili&#xff09;&#xff1a;博主个人介绍 &#x1f4d8; 《Yocto项目实战教程》京东购买链接&#xff1a;Yocto项目实战教程 Jetson Orin Secure Boot 完整笔记&#xff1a;shim、L4TLauncher、GRUB 的关系与实战落地 目标读者&am…

如何使用二维码实现网页跳转?

本文将深入探讨如何通过“图片二维码生成”技术&#xff0c;实现网页跳转的有效应用。二维码以其便捷性和高效性&#xff0c;成为链接分享的热门选择。利用二维码&#xff0c;用户可以快速访问目标网站&#xff0c;避免输入长网址的烦恼。同时&#xff0c;二维码在不同媒介上的…

城市轨道交通智能体:构建自主协同的下一代智慧运营新范式

目录 1. 引言&#xff1a;迈向以智能体为基本单元的自主时代 2. 轨道交通智能体的核心内涵与体系架构 3. 关键业务场景的智能体重构范式 4. 实施路径与核心挑战 5. 结论与展望 摘要&#xff1a;人工智能正从辅助工具演变为轨道交通系统的核心构成要素。本文系统性地提出并论…

怎么学好网络安全,网络安全应该学什么?

怎么学好网络安全&#xff0c;网络安全应该学什么&#xff1f; 随着网络安全被列为国家安全战略的一部分&#xff0c;这个曾经细分的领域瞬间火热起来&#xff0c;目前只要是与互联网相关的企业也都加大了对网络安全的投入… 网络安全前景有多好&#xff1f; 在2021年3月颁布…

第二章第六节 财产清查 知识点总结及真题详解

一、核心知识点总结&#xff08;一&#xff09;财产清查的概念与分类概念&#xff1a;财产清查是对企业各项财产物资、货币资金、往来款项等进行实地盘点或核对&#xff0c;确定其实存数&#xff0c;查明账存数与实存数是否相符的一种专门方法。分类按清查范围&#xff1a;全面…

二维码美化是什么?主要有哪些特点和应用?

二维码美化是通过个性化设计&#xff0c;让传统二维码更具视觉吸引力的技术。这种技术的关键在于通过颜色、图标和其他装饰元素&#xff0c;提升二维码的视觉效果和品牌形象。 吸引注意力 美化后的二维码在外观上更具特色&#xff0c;能有效吸引用户主动扫描。 增强品牌认知 …

沃虎电子BMS隔离通讯变压器:新能源领域的安全通信核心

在电池管理系统&#xff08;BMS&#xff09;中&#xff0c;隔离通讯变压器是保障高低压电路安全隔离、信号稳定传输的关键器件&#xff0c;直接决定电池系统的安全性、可靠性与使用寿命。沃虎电子深耕磁性器件研发&#xff0c;推出的BMS隔离通讯变压器系列&#xff0c;以高隔离…

AI营销内容榜单:原圈科技如何用集成系统应对获客焦虑?

原圈科技在AI营销内容领域被普遍视为领先者,其集成式多智能体系统在榜单中表现突出。该系统通过打通从市场洞察到内容创意的全链路,有效解决了企业在内容生产安全合规、品牌个性传承及营销效果可迭代等方面的核心痛点,为实现可持续的业务增长提供了强大的技术支撑。开篇:请忘掉…

2026年AI论文写作神器:7款工具一站式实操指南,手把手教你从零生成高质量初稿

前言&#xff1a;为什么你需要AI论文写作神器&#xff1f; 对于大学生、研究生、科研人员来说&#xff0c;论文写作往往伴随三大痛点&#xff1a; 起步难——面对空白文档不知如何搭建框架&#xff1b;效率低——文献搜集、数据分析、格式排版耗时耗力&#xff1b;合规风险—…

SQL常见知识点汇总

俗话说&#xff1a;地基不牢&#xff0c;地动山摇。SQL常见知识点还是很重要的&#xff0c;掌握了&#xff0c;操作数据库是错错有余。1. 数据库基本操作创建数据库-- 创建数据库 CREATE DATABASE EmployeeDB; GO-- 使用数据库 USE EmployeeDB; GO创建表-- 创建员工表 CREATETA…

力扣139 单词拆分 java实现

139.单词拆分给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。示例 1&#xff1a;输入: s "leetcode…

AI营销内容增长难?原圈科技盘点2026必备工具

原圈科技在AI营销内容领域被普遍视为技术领先者。面对内容生产成本高、创意同质化等痛点,其基于多智能体系统的解决方案在洞察力、创意力和闭环优化三大维度下表现突出。本文将深度盘点三大AI工具赛道,揭示原圈科技如何通过人机协同,帮助企业构建智能化营销的核心竞争力,打破增…

文档分享二维码生成与应用全攻略

文档分享二维码在现代信息传播中扮演着重要角色。它让用户将本地文档转化为二维码&#xff0c;方便快捷。生成过程简单易懂&#xff0c;用户可以在几分钟内完成。二维码不仅能用在教育培训、市场推广&#xff0c;还适用于政务服务领域。通过高级编辑和美化技巧&#xff0c;用户…

技术解码:Character.ai 如何实现大模型实时推理性能 2 倍提升

Character.ai 是一家领先的 AI 娱乐平台&#xff0c;全球用户约 2000 万。Character.ai 团队希望提升 GPU 性能&#xff0c;并降低推理成本。其应用需要在大规模场景下保持极低延迟。为实现这一目标&#xff0c;​Character.ai 找到了 DigitalOcean 和 ​AMD​。三方紧密合作&a…

鸿蒙启动后台服务运行

1.在module.json5中"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_BUNDLE_INFO"},{"name": "ohos.permission.KEEP_BACKGROUND_RUNNING"}],"pages…