问题
在c++程序中要引入onnxruntime进行onnx的模型推理,出现了一个问题。
在程序开始加载模型建立会话时,会先用假数据进行一次session->Run,这时候是能正常运行的
Ort::Session* m_session; //类的私有成员
void xxxx::loadDeblurOnnx()
{
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "default");
const ORTCHAR_T* model_path = L"onnx_modules/xxxxx.onnx";
// set options
Ort::SessionOptions session_option;
session_option.SetIntraOpNumThreads(1);
session_option.SetGraphOptimizationLevel(ORT_ENABLE_ALL);
// use CUDA
//OrtCUDAProviderOptions cuda_options;
//session_option.AppendExecutionProvider_CUDA(cuda_options);
// use tensorRT
OrtTensorRTProviderOptions trt_options{};
trt_options.device_id = 0;
trt_options.trt_engine_cache_enable = 1;
trt_options.trt_engine_cache_path = "onnx_modules/cache";
trt_options.trt_max_workspace_size = 4294967296;
trt_options.trt_min_subgraph_size = 1;
trt_options.trt_max_partition_iterations = 1;
trt_options.trt_fp16_enable = 1;
session_option.AppendExecutionProvider_TensorRT(trt_options);
m_session = new Ort::Session(env, model_path, session_option);
Ort::AllocatorWithDefaultOptions allocator;
auto input_info = m_session->GetInputTypeInfo(0);
auto input_type_info = input_info.GetTensorTypeAndShapeInfo();
input_dims = input_type_info.GetShape();
// Create input tensor
std::vector<float> input_data(input_dims[0] * input_dims[1] * input_dims[2] * input_dims[3], 1.0f); // initialize input data with dummy values
Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memoryInfo, input_data.data(), input_data.size(), input_dims.data(), input_dims.size());
if (!inputTensor.IsTensor()) {
std::cerr << "Failed to create input tensor." << std::endl;
assert(false && "Failed to create input tensor."); // Assertion failure
}
const char* input_names[] = { "input" };
const char* output_names[] = { "output" };
m_session->Run(Ort::RunOptions{nullptr}, input_names, &inputTensor, 1, output_names, 1);
}
但是在程序运行中,调用session->Run时,则会报错
float* xxxx::runDeblurOnnx(std::vector<float>& inputData)
{
if(inputData.empty()){
return nullptr;
}
Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memoryInfo, inputData.data(), inputData.size(), input_dims.data(), input_dims.size());
if (!inputTensor.IsTensor()) {
std::cerr << "Failed to create input tensor." << std::endl;
assert(false && "Failed to create input tensor."); // Assertion failure
}
const char* input_names[] = { "input" };
const char* output_names[] = { "output" };
std::vector<Ort::Value> outputs;
outputs = m_session->Run(Ort::RunOptions{nullptr}, input_names, &inputTensor, 1, output_names, 1);
float* output = outputs.back().GetTensorMutableData<float>();
return output;
}
报错问题为:异常: 0xC0000005: 读取位置 0x00007FFD6EB65258 时发生访问冲突。
解决
在排查后发现,如果
- 当前函数内执行过
m_session = new Ort::Session(env, model_path, session_option);
那么session->Run就能正常运行。一旦跨函数了就不可以。
- 或者,session_option不使用cuda或者tensorrt的配置,只使用默认的配置,也能正常运行。
在搜索后,看到一个也是session->Run出问题的案例。虽然具体情况和我的不太一样,但姑且试用了对应的方法:尝试将 Ort::Env 变量设为静态
修改为:
static Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "default");
问题解决了,但还是没有完全了解这个问题的原因。
一些猜想:
- Ort::Env 对象负责设置整个推理过程中的日志记录级别和其他环境配置,它需要在程序的整个生命周期内都保持活跃状态。
- Ort::Session 对象依赖于 Ort::Env 对象的状态,在session->Run时还会读取调用建立session时的env的信息。
- 静态变量在C++中是线程安全的,这可能避免了多线程环境中产生的问题。