完整报错:[E] [TRT] 3: [runtime.cpp::nvinfer1::Runtime::~Runtime::346] Error Code 3: API Usage Error (Parameter check failed at: runtime.cpp::nvinfer1::Runtime::~Runtime::346, condition: mEngineCounter.use_count() == 1. Destroying a runtime before destroying deserialized engines created by the runtime leads to undefined behavior.
)
我在使用TensorRT推理的时候,出现了这个错误。
首先,tensorRT推理的构建顺序是:
-
创建推理运行时runtime
-
反序列化生成engine
-
创建执行上下文context
这个顺序是固定的,后序受前序影响。
若这三个函数在一个函数里面那么不会出现上述错误,例如下面的代码:
// 1
auto runtime = std::unique_ptr<nvinfer1::IRuntime>(nvinfer1::createInferRuntime(sample::gLogger.getTRTLogger()));
if (!runtime)
{
std::cout << "runtime create failed" << std::endl;
return -1;
}
//2
// 加载模型文件
auto plan = load_engine_file(engine_file);
// 反序列化生成engine
auto mEngine = std::shared_ptr<nvinfer1::ICudaEngine>(runtime->deserializeCudaEngine(plan.data(), plan.size()));
if (!mEngine)
{
return -1;
}
// 3
auto context = std::unique_ptr<nvinfer1::IExecutionContext>(mEngine->createExecutionContext());
if (!context)
{
std::cout << "context create failed" << std::endl;
return -1;
}
但如果,把这个函数式的流程修改为类的形式就会报错:
class Tensor {
public:
std::shared_ptr<nvinfer1::ICudaEngine> mEngine;
std::unique_ptr<nvinfer1::IRuntime> runtime;
std::unique_ptr<nvinfer1::IExecutionContext> context;
samplesCommon::BufferManager* buffers;
Tensor();
~Tensor();
public:
bool init();
};
在init中执行初始化。
这样看是没有什么问题的,但在程序结束时就会上面的错误,翻译:在销毁运行时创建的反序列化引擎之前销毁运行时会导致未定义的行为。
就是说:runtime应该在mEngine后销毁。
这里就存在一个成员变量之间的构造与析构顺序的问题了,可以查看下面的代码:
#include <iostream>
// 成员类
class A{
public:
A(){
std::cout<< "A" <<std::endl;
}
~A(){
std::cout<< "~A" <<std::endl;
}
};
class B{
public:
B(){
std::cout<< "B" <<std::endl;
}
~B(){
std::cout<< "~B" <<std::endl;
}
};
// 父类
class C{
public:
C(){
std::cout<< "C" <<std::endl;
}
~C(){
std::cout<< "~C" <<std::endl;
}
};
// 子类
class D : public C{
public:
D(){
std::cout<< "D" <<std::endl;
}
~D(){
std::cout<< "~D" <<std::endl;
}
public:
A a;
B b;
};
int main(int argc, char **argv){
D d;
}
结果:
/*
C 父
A 先声明的成员
B 后声明的成员
D 子
~D 子
~B 后声明的成员
~A 先声明的成员
~C 父
*/
就是说我们的runtime要先声明,而mEngine要后声明,才能达到后声明的先析构,先声明的后析构的目的,所以runtime要放在mEngine前面。
class Tensor {
public:
std::unique_ptr<nvinfer1::IRuntime> runtime;
std::shared_ptr<nvinfer1::ICudaEngine> mEngine;
std::unique_ptr<nvinfer1::IExecutionContext> context;
samplesCommon::BufferManager* buffers;
Tensor();
~Tensor();
public:
bool init();
};
当然context也必须在mEngine后面,否则会报类似的顺序报错。
这里你无法通过析构函数通过手动的方式去释放智能指针代理的对象(有没有这样的接口,我还暂时没有了解过,因为智能指针存在的目的就是去代理释放,而非手动),所以只能通过修改声明顺序的方式来修改,以解决这个问题。
问题来源:推理报错