Apollo 11.0
自动驾驶开放平台
onnx_multi_batch_infer.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2020 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16#ifdef NV_TENSORRT_MAJOR
17#if NV_TENSORRT_MAJOR == 8
19#endif
20#endif
21
22#include <sys/stat.h>
23
24#include <fstream>
25
26#include <cuda_runtime_api.h>
27
28#include "absl/strings/str_cat.h"
29
30#include "cyber/common/log.h"
35
36// Logger for TensorRT info/warning/errors
37class OnnxLogger : public nvinfer1::ILogger {
38public:
39 OnnxLogger(Severity severity = Severity::kWARNING) : mReportableSeverity(severity) {}
40 nvinfer1::ILogger& getTRTLogger() {
41 return *this;
42 }
43 Severity getReportableSeverity() const {
45 }
46
48
49 void log(Severity severity, const char* msg) noexcept override {
50 switch (severity) {
51 case Severity::kINTERNAL_ERROR:
52 case Severity::kERROR:
53 AERROR << msg;
54 break;
55 case Severity::kWARNING:
56 AWARN << msg;
57 break;
58 case Severity::kINFO:
59 case Severity::kVERBOSE:
60 ADEBUG << msg;
61 break;
62 default:
63 break;
64 }
65 }
67
68namespace apollo {
69namespace perception {
70namespace inference {
71
73
74#define GPU_CHECK(ans) \
75 { GPUAssert((ans), __FILE__, __LINE__); }
76inline void GPUAssert(cudaError_t code, const char* file, int line, bool abort = true) {
77 if (code != cudaSuccess) {
78 fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
79 if (abort)
80 exit(code);
81 }
82}
83
85 auto iter = blobs_.find(name);
86 if (iter == blobs_.end()) {
87 return nullptr;
88 }
89 return iter->second;
90}
91
93 if (network_) {
94 network_->destroy();
95 }
96 if (builder_) {
97 builder_->destroy();
98 }
99 if (context_) {
100 context_->destroy();
101 }
102 if (engine_) {
103 engine_->destroy();
104 }
105}
106
107bool MultiBatchInference::LoadCache(const std::string& path) {
108 struct stat buffer;
109 if (stat(path.c_str(), &buffer) != 0) {
110 AERROR << "[INFO] cannot find model cache : " << path << ", it will take minutes to generate...";
111 return false;
112 }
113 return true;
114}
115
117 model_file_ = proto_file_;
118 output_names_ = net_output_names_;
119 input_names_ = net_input_names_;
120}
121
122bool MultiBatchInference::Init(const std::map<std::string, std::vector<int>>& shapes) {
123 BASE_GPU_CHECK(cudaSetDevice(gpu_id_));
124 BASE_GPU_CHECK(cudaStreamCreate(&stream_));
125
126 FileInit();
127
128 // create a TensorRT model from the onnx model and load it into an engine
129 OnnxToTRTModel(model_file_);
130
131 if (engine_ == nullptr) {
132 AERROR << "Fail to load obstacle ONNX model";
133 return false;
134 }
135
136 // create execution context from the engine
137 context_ = engine_->createExecutionContext();
138 if (context_ == nullptr) {
139 AERROR << "Fail to create Exceution Context";
140 return false;
141 }
142 for (const auto& name : output_names_) {
143 auto iter = shapes.find(name);
144 if (iter != shapes.end()) {
145 auto blob = std::make_shared<Blob<float>>(iter->second);
146 blobs_.emplace(name, blob);
147 }
148 }
149
150 for (const auto& name : input_names_) {
151 auto iter = shapes.find(name);
152 if (iter != shapes.end()) {
153 auto blob = std::make_shared<Blob<float>>(iter->second);
154 blobs_.emplace(name, blob);
155 }
156 }
157 buffers_.resize(input_names_.size() + output_names_.size());
158
159 // init_blob(&input_names_);
160 // init_blob(&output_names_);
161
162 return true;
163}
164
166 nvinfer1::IOptimizationProfile* profile,
167 const BlobMap& input_shapes) {
168 // Optimizat every input
169 // TODO(huanglei14) Distinguish inputs that do not require optimization
170 for (int i = 0; i < network_->getNbInputs(); ++i) {
171 nvinfer1::ITensor* nv_input = network_->getInput(i);
172 const char* nv_input_name = nv_input->getName();
173 nvinfer1::Dims nv_input_dims = nv_input->getDimensions();
174 auto it = input_shapes.find(nv_input_name);
175 if (it != input_shapes.end()) {
176 CHECK_EQ(nv_input_dims.nbDims, input_shapes.at(nv_input_name)->num_axes());
177 }
178 AINFO << "nv_input_name: " << nv_input_name;
179 AINFO << "nv_input_dims: ";
180 AINFO << "\t" << nv_input_dims.d[0];
181 if (nv_input_dims.d[0] > 0) {
182 continue;
183 }
184
185 for (int j = 1; j < nv_input_dims.nbDims; ++j) {
186 AINFO << "\t" << nv_input_dims.d[j];
187 if (it != input_shapes.end()) {
188 // input dims should be dynamic, or equal to those specified by init
189 // options
190 CHECK(nv_input_dims.d[j] < 0 || nv_input_dims.d[j] == it->second->shape(j));
191 nv_input_dims.d[j] = it->second->shape(j);
192 } else {
193 // input should have fixed dims if not specfied by init options
194 CHECK(nv_input_dims.d[j] > 0);
195 }
196 }
197
198 nv_input_dims.d[0] = 1;
199 profile->setDimensions(nv_input_name, nvinfer1::OptProfileSelector::kMIN, nv_input_dims);
200 nv_input_dims.d[0] = std::max(1, static_cast<int>(max_batch_size_ / 2));
201 profile->setDimensions(nv_input_name, nvinfer1::OptProfileSelector::kOPT, nv_input_dims);
202 nv_input_dims.d[0] = max_batch_size_;
203 profile->setDimensions(nv_input_name, nvinfer1::OptProfileSelector::kMAX, nv_input_dims);
204 }
205}
206
207bool MultiBatchInference::OnnxToTRTModel(const std::string& model_file) {
208 if (gpu_id_ < 0) {
209 AERROR << "must use gpu mode";
210 return false;
211 }
212 // create the builder
213 builder_ = nvinfer1::createInferBuilder(onnx_gLogger);
214 network_ = builder_->createNetworkV2(
215 1U << static_cast<int>(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH));
216
217 // parse onnx model
218 nvonnxparser::IParser* parser = nvonnxparser::createParser(*network_, onnx_gLogger.getTRTLogger());
219
220 const char* onnx_filename = model_file.c_str();
221 int verbosity = static_cast<int>(onnx_gLogger.getReportableSeverity());
222 if (!parser->parseFromFile(onnx_filename, verbosity)) {
223 AERROR << "Failure while parsing ONNX file: " << onnx_filename;
224 return false;
225 }
226
227 // Build the engine
228 builder_->setMaxBatchSize(max_batch_size_);
229 nvinfer1::IBuilderConfig* config = builder_->createBuilderConfig();
230 config->setMaxWorkspaceSize(1 << 30);
231 bool use_fp16 = builder_->platformHasFastFp16();
232 if (use_fp16) {
233 config->setFlag(nvinfer1::BuilderFlag::kFP16);
234 }
235 config->setFlag(nvinfer1::BuilderFlag::kDEBUG);
236
237 auto trt_cache_path = model_file + ".trt.engine";
238
239 if (!LoadCache(trt_cache_path)) {
240 nvinfer1::IOptimizationProfile* profile = builder_->createOptimizationProfile();
241 SetOnnxOptimizationProfile(profile, blobs_);
242 config->addOptimizationProfile(profile);
243
244 engine_ = builder_->buildEngineWithConfig(*network_, *config);
245 nvinfer1::IHostMemory* ser_mem = engine_->serialize();
246
247 if (ser_mem) {
248 nvinfer1::IHostMemory* serialized_model(ser_mem);
249 std::ofstream f(trt_cache_path, std::ios::binary);
250
251 f.write(reinterpret_cast<char*>(serialized_model->data()), serialized_model->size());
252 serialized_model->destroy();
253 }
254 } else {
255 AINFO << "Loading TensorRT engine from serialized model file...";
256 std::ifstream planFile(trt_cache_path);
257
258 if (!planFile.is_open()) {
259 AERROR << "Could not open serialized model";
260 return false;
261 }
262 initLibNvInferPlugins(&onnx_gLogger, "");
263
264 AINFO << "success open serialized model";
265 std::stringstream planBuffer;
266 planBuffer << planFile.rdbuf();
267 std::string plan = planBuffer.str();
268 nvinfer1::IRuntime* runtime = nvinfer1::createInferRuntime(onnx_gLogger);
269
270 engine_ = runtime->deserializeCudaEngine(static_cast<const void*>(plan.data()), plan.size(), nullptr);
271 runtime->destroy();
272 }
273 config->destroy();
274 parser->destroy();
275 return true;
276}
277
279 AINFO << "Do Inference";
280}
281
282void MultiBatchInference::init_blob(std::vector<std::string>* names) {
283 for (auto name : *names) {
284 auto blob = get_blob(name);
285
286 std::vector<int> shape(blob->shape().begin(), blob->shape().end());
287 blob.reset(new apollo::perception::base::Blob<float>(shape));
288
289 buffers_[bindingIndex_] = blob->mutable_gpu_data();
290
291 bindingIndex_ += 1;
292 }
293}
294
296 BASE_GPU_CHECK(cudaSetDevice(gpu_id_));
297 BASE_GPU_CHECK(cudaStreamSynchronize(stream_));
298 CHECK(input_names_.size() > 0 && blobs_.size() > 0);
299 auto input_shape = blobs_[input_names_[0]]->shape();
300 kBatchSize = input_shape[0];
301 if (kBatchSize == 0) {
302 AINFO << "batch size is zero.";
303 return;
304 }
305
306 int batch_size = max_batch_size_;
307
308 // binding input blob
309 for (auto name : input_names_) {
310 auto blob = get_blob(name);
311 if (blob != nullptr) {
312 int32_t index = engine_->getBindingIndex(name.c_str());
313 // careful about this line
314 // batch_size = std::min<int>(batch_size, blob->shape(0));
315 batch_size = blob->shape(0);
316 // set buffer
317 buffers_[index] = const_cast<float*>(blob->gpu_data());
318
319 // set Dim
320 nvinfer1::Dims dims = network_->getInput(index)->getDimensions();
321 dims.d[0] = batch_size;
322 context_->setOptimizationProfile(0);
323 context_->setBindingDimensions(index, dims);
324 }
325 }
326
327 // bingding output blob
328 for (auto name : output_names_) {
329 auto blob = get_blob(name);
330 if (blob != nullptr) {
331 blob->mutable_gpu_data();
332 int32_t index = engine_->getBindingIndex(name.c_str());
333 buffers_[index] = blob->mutable_gpu_data();
334 }
335 }
336
337 {
338 std::lock_guard<std::mutex> lock(mutex_);
339 context_->enqueueV2(&buffers_[0], stream_, nullptr);
340 }
341
342 BASE_GPU_CHECK(cudaStreamSynchronize(stream_));
343 for (auto name : output_names_) {
344 auto blob = get_blob(name);
345 if (blob != nullptr) {
346 blob->mutable_gpu_data();
347 }
348 }
349}
350
351} // namespace inference
352} // namespace perception
353} // namespace apollo
double f
nvinfer1::ILogger & getTRTLogger()
OnnxLogger(Severity severity=Severity::kWARNING)
Severity getReportableSeverity() const
void log(Severity severity, const char *msg) noexcept override
A wrapper around SyncedMemory holders serving as the basic computational unit for images,...
Definition blob.h:88
std::vector< std::string > net_input_names_
Definition inference.h:68
std::vector< std::string > net_output_names_
Definition inference.h:67
bool OnnxToTRTModel(const std::string &model_file)
Convert ONNX to TensorRT model
void init_blob(std::vector< std::string > *names)
void SetOnnxOptimizationProfile(nvinfer1::IOptimizationProfile *profile, const BlobMap &input_shapes)
bool Init(const std::map< std::string, std::vector< int > > &shapes) override
base::BlobPtr< float > get_blob(const std::string &name) override
#define ADEBUG
Definition log.h:41
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
#define AWARN
Definition log.h:43
std::shared_ptr< Blob< Dtype > > BlobPtr
Definition blob.h:313
std::map< std::string, base::BlobPtr< float > > BlobMap
Definition inference.h:39
class register implement
Definition arena_queue.h:37
OnnxLogger onnx_gLogger
OnnxLogger onnx_gLogger