1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "prepared_model_service.h"
17
18 #include <hdf_base.h>
19 #include "securec.h"
20 #include "utils/hdf_log.h"
21
22 #include "shared_buffer_parser.h"
23
24 namespace OHOS {
25 namespace HDI {
26 namespace Nnrt {
27 namespace V1_0 {
PreparedModelService(std::shared_ptr<mindspore::Context> context)28 PreparedModelService::PreparedModelService(std::shared_ptr<mindspore::Context> context)
29 : m_context(context) {}
30
~PreparedModelService()31 PreparedModelService::~PreparedModelService()
32 {
33 if (m_cacheBuffer != nullptr) {
34 m_cacheBuffer->CloseAshmem();
35 }
36
37 for (auto& inputAsh : m_inputAshmems) {
38 inputAsh->UnmapAshmem();
39 inputAsh->CloseAshmem();
40 }
41
42 for (auto& outputAsh : m_outputAshmems) {
43 outputAsh->UnmapAshmem();
44 outputAsh->CloseAshmem();
45 }
46 }
47
ExportModelCache(std::vector<SharedBuffer> & modelCache)48 int32_t PreparedModelService::ExportModelCache(std::vector<SharedBuffer>& modelCache)
49 {
50 if (!modelCache.empty()) {
51 HDF_LOGE("The parameters of ExportModelCache should be an empty vector.");
52 return HDF_ERR_INVALID_PARAM;
53 }
54
55 if (m_cacheBuffer != nullptr) {
56 auto fd = m_cacheBuffer->GetAshmemFd();
57 auto size = m_cacheBuffer->GetAshmemSize();
58
59 // SharedBuffer: fd, bufferSize, offset, dataSize
60 modelCache.emplace_back(SharedBuffer{fd, size, 0, size});
61 return HDF_SUCCESS;
62 }
63
64 auto size = m_builder.GetSize();
65 auto buffer = m_builder.GetBufferPointer();
66 const char* name = m_graph != nullptr ? m_graph->name.c_str() : "CacheModel";
67 sptr<Ashmem> cache = Ashmem::CreateAshmem(name, size);
68 if (cache == nullptr) {
69 HDF_LOGE("Create shared memory failed.");
70 return HDF_ERR_MALLOC_FAIL;
71 }
72
73 bool ret = cache->MapReadAndWriteAshmem();
74 if (!ret) {
75 HDF_LOGE("Map fd to write cache failed.");
76 return HDF_FAILURE;
77 }
78
79 ret = cache->WriteToAshmem(buffer, size, 0);
80 cache->UnmapAshmem();
81 if (!ret) {
82 HDF_LOGE("Write cache failed.");
83 return HDF_FAILURE;
84 }
85
86 m_cacheBuffer = cache;
87
88 // SharedBuffer: fd, bufferSize, offset, dataSize
89 modelCache.emplace_back(SharedBuffer {cache->GetAshmemFd(), cache->GetAshmemSize(), 0, cache->GetAshmemSize()});
90 return HDF_SUCCESS;
91 }
92
Run(const std::vector<IOTensor> & inputs,const std::vector<IOTensor> & outputs,std::vector<std::vector<int32_t>> & outputsDims,std::vector<bool> & isOutputBufferEnough)93 int32_t PreparedModelService::Run(const std::vector<IOTensor>& inputs, const std::vector<IOTensor>& outputs,
94 std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough)
95 {
96 auto ret = SetInputs(inputs);
97 if (ret != HDF_SUCCESS) {
98 HDF_LOGE("Inputs tensor is invalid.");
99 return ret;
100 }
101
102 if (!m_isDynamicShape) {
103 ret = SetOutputs(outputs);
104 if (ret != HDF_SUCCESS) {
105 HDF_LOGE("Output tensor is invalid.");
106 ResetInputAndOutput();
107 return ret;
108 }
109 }
110
111 auto msRet = m_model->Predict(m_inputs, &m_outputs);
112 if (msRet != mindspore::kSuccess) {
113 HDF_LOGE("Run model failed.");
114 ResetInputAndOutput();
115 return HDF_FAILURE;
116 }
117
118 ret = UpdateOutput(outputs, outputsDims, isOutputBufferEnough);
119 if (ret != HDF_SUCCESS) {
120 HDF_LOGE("Update output dimension or data failed.");
121 ResetInputAndOutput();
122 return ret;
123 }
124
125 ResetInputAndOutput();
126
127 return HDF_SUCCESS;
128 }
129
UpdateOutput(const std::vector<IOTensor> & outputs,std::vector<std::vector<int32_t>> & outputsDims,std::vector<bool> & isOutputBufferEnough)130 int32_t PreparedModelService::UpdateOutput(const std::vector<IOTensor>& outputs,
131 std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough)
132 {
133 bool isEnough {true};
134 size_t outputSize = m_outputs.size();
135 isOutputBufferEnough.resize(outputSize, true);
136 for (size_t i = 0; i < outputSize; i++) {
137 auto& msOutput = m_outputs[i];
138 auto& output = outputs[i];
139
140 auto msShape = msOutput.Shape();
141 outputsDims.emplace_back(msShape.begin(), msShape.end());
142
143 auto dataSize = msOutput.DataSize();
144 if (dataSize > output.data.bufferSize) {
145 HDF_LOGE("Output buffer is not enough. actual size %{public}zu, buffer size %{public}u",
146 dataSize, output.data.bufferSize);
147 isOutputBufferEnough[i] = false;
148 isEnough= false;
149 }
150
151 if (isEnough && m_isDynamicShape) {
152 auto msData = msOutput.MutableData();
153 SharedBufferParser parser;
154 auto ret = parser.Init(output.data);
155 if (ret != HDF_SUCCESS) {
156 HDF_LOGE("Parse %zu th output data failed.", i);
157 return HDF_ERR_INVALID_PARAM;
158 }
159
160 auto data = parser.GetBufferPtr();
161 auto memRet = memcpy_s(data, dataSize, msData, dataSize);
162 if (memRet != EOK) {
163 HDF_LOGE("Copy output memory failed.");
164 return HDF_FAILURE;
165 }
166 }
167 }
168
169 return HDF_SUCCESS;
170 }
171
ResetInputAndOutput()172 void PreparedModelService::ResetInputAndOutput()
173 {
174 for (auto& msInput : m_inputs) {
175 msInput.SetData(nullptr);
176 }
177
178 if (!m_isDynamicShape) {
179 for (auto& msOutput : m_outputs) {
180 msOutput.SetData(nullptr);
181 }
182 }
183 }
184
Compile(std::shared_ptr<mindspore::schema::MetaGraphT> graph)185 int32_t PreparedModelService::Compile(std::shared_ptr<mindspore::schema::MetaGraphT> graph)
186 {
187 if (graph == nullptr) {
188 HDF_LOGE("Graph cannot be nullptr");
189 return HDF_ERR_INVALID_PARAM;
190 }
191 for (auto i : graph->inputIndex) {
192 auto inputShape = graph->allTensors[i]->dims;
193 auto iter = std::find(inputShape.begin(), inputShape.end(), DYNAMIC_SHAPE_FLAG);
194 if (iter != inputShape.end()) {
195 m_isDynamicShape = true;
196 break;
197 }
198 }
199 auto offset = mindspore::schema::MetaGraph::Pack(m_builder, graph.get());
200 m_builder.Finish(offset);
201 mindspore::schema::FinishMetaGraphBuffer(m_builder, offset);
202 auto modelSize = m_builder.GetSize();
203 uint8_t* modelBuffer = m_builder.GetBufferPointer();
204 if (modelBuffer == nullptr) {
205 HDF_LOGE("Model is invalid.");
206 return HDF_FAILURE;
207 }
208
209 m_model = std::make_shared<mindspore::Model>();
210 mindspore::Status msRet = m_model->Build(modelBuffer, modelSize, mindspore::kMindIR, m_context);
211 if (msRet != mindspore::kSuccess) {
212 HDF_LOGE("Prepare model failed, please make sure model is validate.");
213 return HDF_FAILURE;
214 }
215
216 auto ret = GetMSInputsAndOutputs();
217 if (ret != HDF_SUCCESS) {
218 HDF_LOGE("Model without inputs or outputs is invalid.");
219 return ret;
220 }
221 return HDF_SUCCESS;
222 }
223
Compile(const void * modelBuffer,size_t length)224 int32_t PreparedModelService::Compile(const void* modelBuffer, size_t length)
225 {
226 if (modelBuffer == nullptr || length == 0) {
227 HDF_LOGE("ModelBuffer cannot be nullptr and length cannot be zero.");
228 return HDF_ERR_INVALID_PARAM;
229 }
230
231 m_model = std::make_shared<mindspore::Model>();
232 mindspore::Status msRet = m_model->Build(modelBuffer, length, mindspore::kMindIR, m_context);
233 if (msRet != mindspore::kSuccess) {
234 HDF_LOGE("Prepare model from cache failed, please make sure model cache is valid.");
235 return HDF_FAILURE;
236 }
237
238 auto ret = GetMSInputsAndOutputs();
239 if (ret != HDF_SUCCESS) {
240 HDF_LOGE("Model without inputs or outputs is invalid.");
241 return ret;
242 }
243
244 for (auto input : m_inputs) {
245 auto shapes = input.Shape();
246 if (std::find(shapes.begin(), shapes.end(), DYNAMIC_SHAPE_FLAG) != shapes.end()) {
247 m_isDynamicShape = true;
248 break;
249 }
250 }
251 return HDF_SUCCESS;
252 }
253
SetInputs(const std::vector<IOTensor> & inputs)254 int32_t PreparedModelService::SetInputs(const std::vector<IOTensor>& inputs)
255 {
256 if (inputs.size() != m_inputs.size()) {
257 HDF_LOGE("inputs size is invalid. expect: %zu, actual: %zu", m_inputs.size(), inputs.size());
258 return HDF_ERR_INVALID_PARAM;
259 }
260 for (auto& ash : m_inputAshmems) {
261 ash->UnmapAshmem();
262 ash->CloseAshmem();
263 }
264 m_inputAshmems.clear();
265
266 int32_t ret {0};
267 size_t inputSize = m_inputs.size();
268 std::vector<std::vector<int64_t>> tmpAllDims;
269 for (size_t i = 0; i < inputSize; i++) {
270 auto& input = inputs[i];
271 auto& msInput = m_inputs[i];
272 ret = CompareTensor(input, msInput);
273 if (ret != HDF_SUCCESS) {
274 HDF_LOGE("Inputs tensor is not match that of model. Please check input tensor.");
275 return ret;
276 }
277 tmpAllDims.emplace_back(input.dimensions.begin(), input.dimensions.end());
278 }
279
280 if (m_isDynamicShape) {
281 auto msRet = m_model->Resize(m_inputs, tmpAllDims);
282 if (msRet != mindspore::kSuccess) {
283 HDF_LOGE("Resize for dynamic inputs failed.");
284 return HDF_FAILURE;
285 }
286 ret = GetMSInputsAndOutputs();
287 if (ret != HDF_SUCCESS) {
288 HDF_LOGE("Get ms inputs or outputs failed after resize.");
289 return ret;
290 }
291 }
292
293 for (size_t i = 0; i < inputSize; i++) {
294 auto& input = inputs[i];
295 auto& msInput = m_inputs[i];
296 sptr<Ashmem> ashptr = ParseBuffer(input.data);
297 if (ashptr == nullptr) {
298 HDF_LOGE("Parse %zuth input data failed.", i);
299 return HDF_ERR_INVALID_PARAM;
300 }
301
302 auto data = const_cast<void*>(ashptr->ReadFromAshmem(input.data.dataSize, 0));
303 msInput.SetData(data);
304 m_inputAshmems.emplace_back(ashptr);
305 }
306 return HDF_SUCCESS;
307 }
308
SetOutputs(const std::vector<IOTensor> & outputs)309 int32_t PreparedModelService::SetOutputs(const std::vector<IOTensor>& outputs)
310 {
311 HDF_LOGI("Start Set outputs, m_outputs size=%zu", m_outputs.size());
312 if (outputs.size() != m_outputs.size()) {
313 HDF_LOGE("outputs size is invalid. expect: %{public}zu, actual: %{public}zu", m_outputs.size(), outputs.size());
314 return HDF_ERR_INVALID_PARAM;
315 }
316 for (auto ash : m_outputAshmems) {
317 ash->UnmapAshmem();
318 ash->CloseAshmem();
319 }
320 m_outputAshmems.clear();
321
322 for (size_t i = 0; i < m_outputs.size(); i++) {
323 auto& output = outputs[i];
324 auto& msOutput = m_outputs[i];
325
326 sptr<Ashmem> ashptr = ParseBuffer(output.data);
327 if (ashptr == nullptr) {
328 HDF_LOGE("Parse %{public}zu th output data failed.", i);
329 return HDF_ERR_INVALID_PARAM;
330 }
331
332 auto data = const_cast<void*>(ashptr->ReadFromAshmem(output.data.dataSize, 0));
333 msOutput.SetAllocator(nullptr);
334 msOutput.SetData(data);
335 m_outputAshmems.emplace_back(ashptr);
336 }
337 return HDF_SUCCESS;
338 }
339
GetMSInputsAndOutputs()340 int32_t PreparedModelService::GetMSInputsAndOutputs()
341 {
342 m_inputs = m_model->GetInputs();
343 if (m_inputs.empty()) {
344 HDF_LOGE("Get inputs failed.");
345 return HDF_FAILURE;
346 }
347
348 m_outputs = m_model->GetOutputs();
349 if (m_outputs.empty()) {
350 HDF_LOGE("Get outputs failed.");
351 return HDF_FAILURE;
352 }
353 return HDF_SUCCESS;
354 }
355
CompareTensor(const IOTensor & tensor,const mindspore::MSTensor & msTensor)356 int32_t PreparedModelService::CompareTensor(const IOTensor& tensor, const mindspore::MSTensor& msTensor)
357 {
358 auto dataType = static_cast<DataType>(msTensor.DataType());
359 if (tensor.dataType != dataType) {
360 HDF_LOGE("Data type of tensor is not match that of model.");
361 return HDF_ERR_INVALID_PARAM;
362 }
363
364 auto format = static_cast<Format>(msTensor.format());
365 if (tensor.format != format) {
366 HDF_LOGE("Format of tensor is not match that of model.");
367 return HDF_ERR_INVALID_PARAM;
368 }
369
370 for (size_t i = 0; i < tensor.dimensions.size(); i++) {
371 if (msTensor.Shape()[i] != DYNAMIC_SHAPE_FLAG && tensor.dimensions[i] != msTensor.Shape()[i]) {
372 HDF_LOGE("The Shape of tensor is not match that of model.");
373 return HDF_ERR_INVALID_PARAM;
374 }
375 }
376
377 return HDF_SUCCESS;
378 }
379
ParseBuffer(const SharedBuffer & buffer)380 sptr<Ashmem> PreparedModelService::ParseBuffer(const SharedBuffer& buffer)
381 {
382 if (buffer.fd == -1) {
383 HDF_LOGE("Invalid buffer fd, it cannot be -1.");
384 return nullptr;
385 }
386
387 HDF_LOGW("NNRT buffer fd=%{public}d, length=%{public}u", buffer.fd, buffer.dataSize);
388
389 sptr<Ashmem> ashptr = new (std::nothrow) Ashmem(buffer.fd, buffer.bufferSize);
390 if (ashptr == nullptr) {
391 HDF_LOGE("Create shared memory failed.");
392 return nullptr;
393 }
394
395 if (!ashptr->MapReadAndWriteAshmem()) {
396 HDF_LOGE("Map buffer fd to address failed.");
397 return nullptr;
398 }
399
400 const void* data = ashptr->ReadFromAshmem(buffer.dataSize, buffer.offset);
401 if (data == nullptr) {
402 HDF_LOGE("Get data address failed.");
403 ashptr->UnmapAshmem();
404 ashptr->CloseAshmem();
405 return nullptr;
406 }
407 return ashptr;
408 }
409 } // V1_0
410 } // Nnrt
411 } // HDI
412 } // OHOS
413