一、??代碼框架??
初始化模型
std::shared_ptr<nvinfer1::IExecutionContext> Instance::Init_Instance(const char* model_path, const string class_name_path)
{
//注冊(cè)防止反序列化報(bào)錯(cuò)
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流,不需要了,在類中已經(jīng)作為成員變量初始化了
//cudaStream_t stream = nullptr;
checkRuntime(cudaStreamCreate(&stream));
//創(chuàng)建執(zhí)行的上下文
auto execution_context = make_nvshared(_engine->createExecutionContext());
return execution_context;
}
這里可以看到我用了智能指針來(lái)分別定義_engine, _runtime, _context。并且我在頭文件中定義了一個(gè)類來(lái)封裝我的推理代碼,包括初始化模型的步驟:
類封裝
class Instance {
public:
Instance(float scale_):scale(scale_)
{
cout << "調(diào)用了構(gòu)造函數(shù)" << endl;
}
~Instance() {
cout << "調(diào)用了析構(gòu)函數(shù)" << 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 };
};
在這里可以看到,我已經(jīng)將_engine, _runtime, _context都定義在了類的成員變量當(dāng)中,并且都用的智能指針shared_ptr的方式。(并且我也在類中封裝了Malloc_data()和Free_malloc()函數(shù),作用分別是為tensorRT推理時(shí)在host和device上為輸入輸出的指針?lè)峙浯鎯?chǔ)空間,和在執(zhí)行推理完畢后,將host和device上分配的指針指向的內(nèi)存空間手動(dòng)釋放掉)。
主函數(shù)
第一次封裝是將我的推理代碼封裝在類中,為了方便c#軟件調(diào)用部署導(dǎo)出的dll,我們將進(jìn)行二次封裝,因此主函數(shù)的接口使用的是二次封裝的:
#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;
}
可以看到在上述代碼經(jīng)過(guò)一個(gè)for循環(huán)遍歷完所有要檢測(cè)的圖像之后,在循環(huán)外部,程序即將結(jié)束之前,我們調(diào)用了Free_memory()來(lái)手動(dòng)釋放host和device指針指向的內(nèi)存以及釋放推理時(shí)創(chuàng)建的cuda流,并且通過(guò)Check_ptr()方法來(lái)判斷指針是否被釋放完成。最后我們通過(guò)destory_trt()接口來(lái)釋放推理時(shí)創(chuàng)建的_engine, _runtime, _context。最后跑出的推理結(jié)果十分正確,代碼跑的也很流暢沒(méi)有bug,并且通過(guò)一個(gè)寫(xiě)了一個(gè)while死循環(huán)來(lái)跑也沒(méi)有發(fā)現(xiàn)內(nèi)存泄露。到這里我以為我已經(jīng)大功告成了。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-802182.html
二、??問(wèn)題以及分析??
這是終端跑出來(lái)的結(jié)果,可以看到執(zhí)行完了Free_memory()并且通過(guò)check_ptr()方法,我們可以判斷指針已經(jīng)釋放完畢了。但是當(dāng)我們經(jīng)過(guò)system(“pause”)繼續(xù)往下執(zhí)行推出程序時(shí),問(wèn)題就來(lái)了:
是NvInferRuntime.h所報(bào)出的問(wèn)題,顯示我們可管理的指針將被執(zhí)行兩次釋放,說(shuō)人話就是我們的指針已經(jīng)釋放完了,但是現(xiàn)在又要執(zhí)行那個(gè)指針的釋放,電腦也不知道咋辦了,只能拋出錯(cuò)誤。于是乎,我便開(kāi)始從頭開(kāi)始查詢我的代碼,看看到底哪里多釋放了一次,后來(lái)突然發(fā)現(xiàn),由于我使用了智能指針,智能指針在其完成調(diào)用之后,根據(jù)引用計(jì)數(shù)可以自動(dòng)釋放,而我的這些_engine, _context, _runtime,也都已經(jīng)在類的成員變量中定義過(guò)了,在類中如果不是new出來(lái)的內(nèi)存,其他的都會(huì)隨著程序的執(zhí)行結(jié)束而自動(dòng)銷毀(new出來(lái)的內(nèi)存需要手動(dòng)delete)。到這里我才恍然大悟,既然我用類來(lái)封裝代碼了,因此也不需要通過(guò)智能指針的方式來(lái)定義推理的變量了,因此我將這三個(gè)智能指針換成了普通指針,最后運(yùn)行,沒(méi)有問(wèn)題!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-802182.html
到了這里,關(guān)于【TensorRT】c++使用面向?qū)ο髞?lái)封裝tensorRT推理代碼的指針釋放問(wèn)題的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!