• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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