【TensorRT】c++使用面向对象来封装tensorRT推理代码的指针释放问题

使用类来封装智能指针创建的tensorRT推理engine,runtime,context

  • 一、🍎代码框架🍎
  • 二、💡问题以及分析💡

一、🍎代码框架🍎

初始化模型

std::shared_ptr<nvinfer1::IExecutionContext> Instance::Init_Instance(const char* model_path, const string class_name_path)
{//注册防止反序列化报错nvinfer1::ILogger* gLogger = NULL;initLibNvInferPlugins(gLogger, "");TRTLogger logger;ifstream fs(model_path, std::ios::binary);std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(fs), {});fs.close();std::shared_ptr<nvinfer1::IRuntime> runtime = make_nvshared(nvinfer1::createInferRuntime(logger));//nvinfer1::IRuntime *runtime = nvinfer1::createInferRuntime(logger);_engine = make_nvshared(runtime->deserializeCudaEngine((void*)buffer.data(), buffer.size()));//_engine = runtime->deserializeCudaEngine((void*)buffer.data(), buffer.size());Malloc_data();class_names = get_name(class_name_path);//初始化cuda流,不需要了,在类中已经作为成员变量初始化了//cudaStream_t stream = nullptr;checkRuntime(cudaStreamCreate(&stream));//创建执行的上下文auto execution_context = make_nvshared(_engine->createExecutionContext());return execution_context;
}

这里可以看到我用了智能指针来分别定义_engine, _runtime, _context。并且我在头文件中定义了一个类来封装我的推理代码,包括初始化模型的步骤:


类封装

class Instance {
public:Instance(float scale_):scale(scale_){cout << "调用了构造函数" << endl;}~Instance() {cout << "调用了析构函数" << endl;};float scale;std::shared_ptr < nvinfer1::IExecutionContext > _context = nullptr;//nvinfer1::IExecutionContext* _context = nullptr;vector<string> class_names;string total_output;std::shared_ptr<nvinfer1::IExecutionContext> Init_Instance(const char* model_path, const string class_name_path);//nvinfer1::IExecutionContext* Init_Instance(const char* model_path, const string class_name_path);void Run_Instance(cv::Mat input);void check_ptr();void Free_Memory();vector<string> get_name(string class_name_path);void destory_engine();private:void Malloc_data();cv::cuda::GpuMat ProcessImage(cv::cuda::GpuMat pre_input);void PostcessImage(cv::Mat nums, cv::Mat boxes, cv::Mat scores, cv::Mat classes, cv::Mat masks);//void Init_parameters();int INPUT_SIZE = 1344;int MIN_SIZE = 800;int MAX_SIZE = 1333;int MASK_SIZE = 28;float CONFIDENCE = 0;float MASK_THRESHOLD = 0.5;int input_numel = 1;int output_nums_numel = 1;int output_boxes_numel = 1;int output_scores_numel = 1;int output_classes_numel = 1;int output_masks_numel = 1;cv::Mat INPUT;cudaStream_t stream = nullptr;std::shared_ptr<nvinfer1::ICudaEngine> _engine = nullptr;std::shared_ptr<nvinfer1::IRuntime> _runtime = nullptr;//nvinfer1::ICudaEngine* _engine = nullptr;//nvinfer1::IRuntime* _runtime = nullptr;//初始化输入和输出指针//static float* input_host_data = nullptr;float* input_device_data = nullptr;Output_ptr output_host = { nullptr, nullptr, nullptr, nullptr, nullptr };Output_ptr output_device = { nullptr, nullptr, nullptr, nullptr, nullptr };
};

在这里可以看到,我已经将_engine, _runtime, _context都定义在了类的成员变量当中,并且都用的智能指针shared_ptr的方式。(并且我也在类中封装了Malloc_data()和Free_malloc()函数,作用分别是为tensorRT推理时在host和device上为输入输出的指针分配存储空间,和在执行推理完毕后,将host和device上分配的指针指向的内存空间手动释放掉)。


主函数
第一次封装是将我的推理代码封装在类中,为了方便c#软件调用部署导出的dll,我们将进行二次封装,因此主函数的接口使用的是二次封装的:

#include "port.h"int main()
{cv::String folder = ;std::vector<cv::String> paths;cv::glob(folder, paths);const char* model_path = ;const string class_path = ;Init_model(model_path, class_path);for (int i = 0; i < paths.size(); i++){char* y_output = NULL;cv::Mat input = cv::imread(paths[i]);cv::Mat bgr[3];cv::split(input, bgr);uchar* b_ptr = bgr[0].data;uchar* g_ptr = bgr[1].data;uchar* r_ptr = bgr[2].data;auto time_start = std::chrono::system_clock::now();Run_model(b_ptr, g_ptr, r_ptr, 800, 800, 3, y_output);auto time_end = std::chrono::system_clock::now();std::chrono::duration<double> diff = time_end - time_start;cout << y_output;cout << "deep learning cost time : " << diff.count() * 1000 << "ms" << endl << endl;}Free_memory();check_Ptr();destory_trt();system("pause");return 0;
}

可以看到在上述代码经过一个for循环遍历完所有要检测的图像之后,在循环外部,程序即将结束之前,我们调用了Free_memory()来手动释放host和device指针指向的内存以及释放推理时创建的cuda流,并且通过Check_ptr()方法来判断指针是否被释放完成。最后我们通过destory_trt()接口来释放推理时创建的_engine, _runtime, _context。最后跑出的推理结果十分正确,代码跑的也很流畅没有bug,并且通过一个写了一个while死循环来跑也没有发现内存泄露。到这里我以为我已经大功告成了。


二、💡问题以及分析💡

在这里插入图片描述
这是终端跑出来的结果,可以看到执行完了Free_memory()并且通过check_ptr()方法,我们可以判断指针已经释放完毕了。但是当我们经过system(“pause”)继续往下执行推出程序时,问题就来了:
在这里插入图片描述
是NvInferRuntime.h所报出的问题,显示我们可管理的指针将被执行两次释放,说人话就是我们的指针已经释放完了,但是现在又要执行那个指针的释放,电脑也不知道咋办了,只能抛出错误。于是乎,我便开始从头开始查询我的代码,看看到底哪里多释放了一次,后来突然发现,由于我使用了智能指针,智能指针在其完成调用之后,根据引用计数可以自动释放,而我的这些_engine, _context, _runtime,也都已经在类的成员变量中定义过了,在类中如果不是new出来的内存,其他的都会随着程序的执行结束而自动销毁(new出来的内存需要手动delete)。到这里我才恍然大悟,既然我用类来封装代码了,因此也不需要通过智能指针的方式来定义推理的变量了,因此我将这三个智能指针换成了普通指针,最后运行,没有问题!
在这里插入图片描述

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

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

相关文章

SD-WAN企业组网场景深度解析

在当前快速发展的企业网络环境中&#xff0c;SD-WAN技术不仅仅是实现企业站点之间网络互通的关键&#xff0c;更是满足不同站点对因特网、SaaS云应用、公有云等多种企业应用和业务访问的理想选择。从企业的WAN业务需求出发&#xff0c;我们可以对SD-WAN的组网场景进行深度解析&…

DAY03_Spring—自动装配注解模式优化XML文件

目录 1 Spring注解模式1.1 自动装配1.1.1 说明1.1.2 配置规则 1.2 注解模式1.2.1 关于注解的说明1.2.2 注解使用原理1.2.3 编辑配置文件1.2.4 属性注解 1.3 实现MVC结构的纯注解开发1.3.1 编写java代码1.3.2 编辑xml配置文件1.3.3 编写测试类1.3.4 关于注解说明1.3.5 关于Sprin…

C语言之【函数】篇章以及例题分析

文章目录 前言一、函数是什么&#xff1f;二、C语言中函数的分类1、库函数2、自定义函数 三、函数的参数1、实际参数&#xff08;实参&#xff09;2、形式参数&#xff08;形参&#xff09; 四、函数的调用1、传值调用2、传址调用3、专项练习3.1 素数判断3.2 闰年判断3.3 二分查…

【Spring源码分析】从源码角度去熟悉依赖注入(一)

从源码角度去熟悉依赖注入 一、全局出发引出各种依赖注入策略二、Autowired依赖注入源码分析属性注入源码分析&#xff08;AutowiredFieldElement.inject&#xff09;方法注入源码分析&#xff08;AutowiredMethodElement.inject&#xff09;流程图 其实在上篇阐述非懒加载单例…

手拉手Vue组件由浅入深

组件 (Component) 是 Vue.js 最强大的功能之一&#xff0c;它是html、css、js等的一个聚合体&#xff0c;封装性和隔离性非常强。 组件化开发&#xff1a; 1、将一个具备完整功能的项目的一部分分割多处使用 2、加快项目的进度 3、可以进行项目的复用 组件注册分…

android studio使用总结

gradle是项目构建的工具&#xff0c;在gradle-wrapper.properties这个文件中设置&#xff0c; 然后就会下载相应版本的安装包到这个路径C:\Users\ly.gradle\wrapper\dists&#xff0c;例如这里是7.0.2&#xff0c; gradle和studio中的jdk版本需要对应&#xff0c;否则无法构建项…

khbc靶场小记(upload 666靶场)

尝试上传正常的png jpg gif php的格式的文件发现老是提示烦人的消息&#xff08;上传不成功&#xff09;&#xff1b; 通过抓包对MIME进行爆破没爆出来&#xff0c;当时可能用成小字典了&#xff1b; 猜测可能是把后缀名和MIME绑定检测了&#xff1b; 反正也没思路&#xff0c;…

lombok引发的血案

一直以为lombok是一个无比牛叉的省代码工具&#xff0c;感觉再也不用&#xff0c;好爽&#xff1b; 然而lombok再给我们带来便利的同时&#xff0c;却有可能悄无声息的带来各种代码问题&#xff1b; 今天笔主就遇到了一个引发生产事故的bug&#xff1b; 我们在开发后端项目时…

C++核心编程之通过类和对象的思想对文件进行操作

目录 ​​​​​​​一、文件操作 1. 文件类型分类&#xff1a; 2. 操作文件的三大类 二、文本文件 1.写文件 2.读文件 三、二进制文件 1.写二进制文件 2.读二进制文件 一、文件操作 程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放 通过文件可以将…

qt.qpa.plugin: Could not find the Qt platform plugin “windows“ in ““

系统环境&#xff1a;Win10家庭中文版 Qt : 5.12.9 链接了一些64位的第三方库&#xff0c;程序编译完运行后出现 qt.qpa.plugin: Could not find the Qt platform plugin "windows" in "" 弹窗如下&#xff1a; 网上搜了一些都是关于pyQt的&#xff0c…

.Net Core 使用 AspNetCoreRateLimit 实现限流

上一篇文章介绍过ASP.NET Core 的 Web Api 实现限流 中间件-CSDN博客 使用.NET 7 自带的中间件 Microsoft.AspNetCore.RateLimiting 可以实现简单的Api限流&#xff0c;但是这个.NET 7以后才集成的中间件&#xff0c;如果你使用的是早期版本的.NET&#xff0c;可以使用第三方库…

10个用于Android开发的有用的Kotlin库及示例

10个用于Android开发的有用的Kotlin库及示例 在Android开发领域&#xff0c;Kotlin已成为一门领先的语言&#xff0c;带来了现代语法和功能的浪潮。随着Kotlin的崛起&#xff0c;涌现出了许多专为其定制的库&#xff0c;进一步增强了开发体验。本文将深入介绍其中的10个库&…