Repository navigation
Expand file tree
/
Copy pathort_session.cpp
More file actions
339 lines (316 loc) · 13.7 KB
/
Copy pathort_session.cpp
File metadata and controls
339 lines (316 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/************************************************
* Copyright MaybeShewill-CV. All Rights Reserved.
* Author: MaybeShewill-CV
* File: ort_session.cpp
* Date: 26-8-20
************************************************/
#include "models/backend/ort_session.h"
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <string>
#include <unordered_map>
#include "glog/logging.h"
#include "common/file_path_util.h"
#include "models/backend/session_io.h"
namespace mortred {
namespace models {
namespace backend {
using mortred::common::StatusCode;
namespace {
ONNXTensorElementDataType to_ort_dtype(const DType& dtype) {
switch (dtype) {
case DType::F32:
return ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT;
case DType::I32:
return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32;
case DType::I64:
return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64;
case DType::U8:
return ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8;
default:
return ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED;
}
}
bool dtype_from_ort(const ONNXTensorElementDataType& dtype, DType* out, std::string* err) {
switch (dtype) {
case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT:
*out = DType::F32;
return true;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32:
*out = DType::I32;
return true;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64:
*out = DType::I64;
return true;
case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8:
*out = DType::U8;
return true;
default:
if (err != nullptr) {
*err = "unsupported onnxruntime tensor element type: " + std::to_string(dtype);
}
return false;
}
}
const void* ort_tensor_data(const Ort::Value& value, const DType& dtype) {
switch (dtype) {
case DType::F32:
return value.GetTensorData<float>();
case DType::I32:
return value.GetTensorData<int32_t>();
case DType::I64:
return value.GetTensorData<int64_t>();
case DType::U8:
return value.GetTensorData<uint8_t>();
default:
return nullptr;
}
}
} // namespace
StatusCode OrtSession::init(const BackendConfig& config, std::string* err) {
if (err != nullptr) {
err->clear();
}
if (!mortred::common::FilePathUtil::is_file_exist(config.model_file_path)) {
if (err != nullptr) {
*err = "onnx model file not exist: " + config.model_file_path;
}
return StatusCode::MODEL_INIT_FAILED;
}
try {
_m_env = Ort::Env(ORT_LOGGING_LEVEL_ERROR, "mortred");
_m_session_options = Ort::SessionOptions();
_m_session_options.SetIntraOpNumThreads(config.threads);
_m_session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
_m_session_options.SetExecutionMode(ExecutionMode::ORT_SEQUENTIAL);
if (config.use_gpu()) {
int limit_mb = config.gpu_mem_limit_mb;
if (const char* env = std::getenv("MORTRED_ORT_GPU_MEM_LIMIT_MB");
env != nullptr && *env != '\0') {
try {
limit_mb = std::stoi(env);
} catch (...) {
if (err != nullptr) {
*err = "invalid MORTRED_ORT_GPU_MEM_LIMIT_MB";
}
return StatusCode::MODEL_INIT_FAILED;
}
}
if (limit_mb < 0) {
if (err != nullptr) {
*err = "onnxruntime gpu_mem_limit_mb must be >= 0 (0 = unlimited)";
}
return StatusCode::MODEL_INIT_FAILED;
}
size_t limit_bytes = 0;
if (limit_mb > 0) {
const uint64_t bytes =
static_cast<uint64_t>(limit_mb) * 1024ull * 1024ull;
if (bytes > std::numeric_limits<size_t>::max()) {
if (err != nullptr) {
*err = "onnxruntime gpu_mem_limit_mb overflows size_t";
}
return StatusCode::MODEL_INIT_FAILED;
}
limit_bytes = static_cast<size_t>(bytes);
}
Ort::CUDAProviderOptions cuda_opts;
const std::string mem_limit =
(limit_mb == 0)
? std::to_string(std::numeric_limits<size_t>::max())
: std::to_string(limit_bytes);
cuda_opts.Update({
{"device_id", std::to_string(config.device_id)},
{"gpu_mem_limit", mem_limit},
{"arena_extend_strategy", "kSameAsRequested"},
{"cudnn_conv_algo_search", "DEFAULT"},
{"do_copy_in_default_stream", "1"},
});
_m_session_options.AppendExecutionProvider_CUDA_V2(*cuda_opts);
LOG(INFO) << "onnxruntime cuda gpu_mem_limit_mb=" << limit_mb
<< (limit_mb == 0 ? " (unlimited)" : "")
<< " device_id=" << config.device_id;
// fuse/transform passes of ALL are not guaranteed on the CUDA EP
_m_session_options.SetGraphOptimizationLevel(
GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
}
_m_session = std::make_unique<Ort::Session>(
_m_env, config.model_file_path.c_str(), _m_session_options);
Ort::AllocatorWithDefaultOptions allocator;
const auto fill_infos = [&allocator](size_t count, auto get_name, auto get_type_info,
std::vector<TensorInfo>* infos,
std::string* parse_err) -> bool {
for (size_t idx = 0; idx < count; ++idx) {
TensorInfo info;
info.name = get_name(idx, allocator);
// ConstTensorTypeAndShapeInfo borrows from the TypeInfo: keep
// the owner alive while reading shape/element type
const auto type_info = get_type_info(idx);
const auto shape_and_type = type_info.GetTensorTypeAndShapeInfo();
if (!dtype_from_ort(shape_and_type.GetElementType(), &info.dtype, parse_err)) {
return false;
}
info.shape = shape_and_type.GetShape();
info.dynamic = shape_is_dynamic(info.shape);
info.layout = host_output_layout(info.shape);
infos->push_back(std::move(info));
}
return true;
};
const bool inputs_ok = fill_infos(
_m_session->GetInputCount(),
[this](size_t idx, Ort::AllocatorWithDefaultOptions& alloc) {
return std::string(_m_session->GetInputNameAllocated(idx, alloc).get());
},
[this](size_t idx) { return _m_session->GetInputTypeInfo(idx); }, &_m_input_infos, err);
if (!inputs_ok) {
return StatusCode::MODEL_INIT_FAILED;
}
const bool outputs_ok = fill_infos(
_m_session->GetOutputCount(),
[this](size_t idx, Ort::AllocatorWithDefaultOptions& alloc) {
return std::string(_m_session->GetOutputNameAllocated(idx, alloc).get());
},
[this](size_t idx) { return _m_session->GetOutputTypeInfo(idx); }, &_m_output_infos,
err);
if (!outputs_ok) {
return StatusCode::MODEL_INIT_FAILED;
}
} catch (const Ort::Exception& exception) {
if (err != nullptr) {
*err = "onnxruntime init failed: " + std::string(exception.what());
}
_m_session.reset();
return StatusCode::MODEL_INIT_FAILED;
}
if (_m_input_infos.empty() || _m_output_infos.empty()) {
if (err != nullptr) {
*err = "onnx model exposes no io tensors: " + config.model_file_path;
}
return StatusCode::MODEL_INIT_FAILED;
}
if (apply_configured_io_names(config.input_names, &_m_input_infos, "input", err) !=
StatusCode::OK) {
return StatusCode::MODEL_INIT_FAILED;
}
if (apply_configured_io_names(config.output_names, &_m_output_infos, "output", err) !=
StatusCode::OK) {
return StatusCode::MODEL_INIT_FAILED;
}
const auto rebuild_name_ptrs = [](const std::vector<TensorInfo>& infos,
std::vector<std::string>* names,
std::vector<const char*>* name_ptrs) {
names->clear();
names->reserve(infos.size());
for (const auto& info : infos) {
names->push_back(info.name);
}
name_ptrs->clear();
name_ptrs->reserve(names->size());
for (const auto& name : *names) {
name_ptrs->push_back(name.c_str());
}
};
rebuild_name_ptrs(_m_input_infos, &_m_input_names, &_m_input_name_ptrs);
rebuild_name_ptrs(_m_output_infos, &_m_output_names, &_m_output_name_ptrs);
_m_model_file_path = config.model_file_path;
for (const auto& info : _m_input_infos) {
LOG(INFO) << "ort session input: " << info.to_string();
}
for (const auto& info : _m_output_infos) {
LOG(INFO) << "ort session output: " << info.to_string();
}
return StatusCode::OK;
}
StatusCode OrtSession::run(const std::vector<NamedTensor>& inputs,
std::vector<NamedTensor>& outputs) {
if (_m_session == nullptr) {
LOG(ERROR) << "onnxruntime session is not initialized";
return StatusCode::MODEL_INIT_FAILED;
}
std::vector<const NamedTensor*> ordered_inputs;
std::string match_err;
if (match_required_inputs(_m_input_infos, inputs, &ordered_inputs, &match_err) !=
StatusCode::OK) {
LOG(ERROR) << "onnxruntime " << match_err;
return StatusCode::MODEL_RUN_SESSION_FAILED;
}
try {
const auto memory_info = Ort::MemoryInfo::CreateCpu(
OrtAllocatorType::OrtDeviceAllocator, OrtMemType::OrtMemTypeDefault);
std::vector<Ort::Value> ort_inputs;
ort_inputs.reserve(_m_input_infos.size());
// Bind in session input order so name ptrs and Ort::Value slots match,
// independent of the caller's NamedTensor vector order.
for (size_t idx = 0; idx < _m_input_infos.size(); ++idx) {
const auto& info = _m_input_infos[idx];
const auto& named = *ordered_inputs[idx];
if (info.dtype != named.tensor.dtype) {
LOG(ERROR) << "onnxruntime input '" << named.name << "' dtype mismatch, expected "
<< info.to_string() << ", got "
<< dtype_to_string(named.tensor.dtype);
return StatusCode::MODEL_RUN_SESSION_FAILED;
}
if (!named.tensor.shape_is_concrete()) {
LOG(ERROR) << "onnxruntime input '" << named.name << "' has non concrete shape "
<< shape_to_string(named.tensor.shape);
return StatusCode::MODEL_RUN_SESSION_FAILED;
}
ort_inputs.push_back(Ort::Value::CreateTensor(
memory_info, const_cast<uint8_t*>(named.tensor.buffer.data()),
named.tensor.byte_size(), named.tensor.shape.data(), named.tensor.shape.size(),
to_ort_dtype(named.tensor.dtype)));
}
auto ort_outputs = _m_session->Run(
Ort::RunOptions{nullptr}, _m_input_name_ptrs.data(), ort_inputs.data(),
ort_inputs.size(), _m_output_name_ptrs.data(), _m_output_name_ptrs.size());
std::string count_err;
if (require_output_count(_m_output_infos.size(), ort_outputs.size(), "onnxruntime",
&count_err) != StatusCode::OK) {
LOG(ERROR) << count_err;
return StatusCode::MODEL_RUN_SESSION_FAILED;
}
outputs.clear();
outputs.reserve(ort_outputs.size());
for (size_t idx = 0; idx < ort_outputs.size(); ++idx) {
const auto& value = ort_outputs[idx];
NamedTensor named;
named.name = _m_output_infos[idx].name;
const auto type_and_shape = value.GetTensorTypeAndShapeInfo();
std::string dtype_err;
if (!dtype_from_ort(type_and_shape.GetElementType(), &named.tensor.dtype,
&dtype_err)) {
LOG(ERROR) << "onnxruntime output '" << named.name << "': " << dtype_err;
return StatusCode::MODEL_RUN_SESSION_FAILED;
}
named.tensor.shape = type_and_shape.GetShape();
size_t bytes = 0;
if (!checked_shape_nbytes(named.tensor.shape, named.tensor.dtype, &bytes) ||
bytes == 0) {
LOG(ERROR) << "onnxruntime output '" << named.name
<< "' is empty or shape byte size overflows: "
<< shape_to_string(named.tensor.shape);
return StatusCode::MODEL_EMPTY_OUTPUT;
}
named.tensor.buffer.resize(bytes);
const void* src = ort_tensor_data(value, named.tensor.dtype);
if (src == nullptr) {
LOG(ERROR) << "onnxruntime output '" << named.name << "' data is null";
return StatusCode::MODEL_RUN_SESSION_FAILED;
}
std::memcpy(named.tensor.buffer.data(), src, bytes);
named.tensor.layout = host_output_layout(named.tensor.shape);
outputs.push_back(std::move(named));
}
} catch (const Ort::Exception& exception) {
LOG(ERROR) << "onnxruntime run failed: " << exception.what();
return StatusCode::MODEL_RUN_SESSION_FAILED;
}
return StatusCode::OK;
}
} // namespace backend
} // namespace models
} // namespace mortred