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 "nnrt_device_service.h"
17
18 #include <hdf_base.h>
19 #include "utils/hdf_log.h"
20 #include "ashmem.h"
21 #include "securec.h"
22
23 #include "node_registry.h"
24 #include "prepared_model_service.h"
25 #include "shared_buffer_parser.h"
26 #include "validation.h"
27
28 namespace OHOS {
29 namespace HDI {
30 namespace Nnrt {
31 namespace V1_0 {
NnrtDeviceImplGetInstance(void)32 extern "C" INnrtDevice *NnrtDeviceImplGetInstance(void)
33 {
34 return new (std::nothrow) NnrtDeviceService();
35 }
36
~NnrtDeviceService()37 NnrtDeviceService::~NnrtDeviceService()
38 {
39 for (auto ash : m_ashmems) {
40 ash.second->UnmapAshmem();
41 ash.second->CloseAshmem();
42 }
43 }
44
GetDeviceName(std::string & name)45 int32_t NnrtDeviceService::GetDeviceName(std::string& name)
46 {
47 name = "RK3568-CPU";
48 return HDF_SUCCESS;
49 }
50
GetVendorName(std::string & name)51 int32_t NnrtDeviceService::GetVendorName(std::string& name)
52 {
53 name = "Rockchip";
54 return HDF_SUCCESS;
55 }
56
GetDeviceType(DeviceType & deviceType)57 int32_t NnrtDeviceService::GetDeviceType(DeviceType& deviceType)
58 {
59 deviceType = DeviceType::CPU;
60 return HDF_SUCCESS;
61 }
62
GetDeviceStatus(DeviceStatus & status)63 int32_t NnrtDeviceService::GetDeviceStatus(DeviceStatus& status)
64 {
65 status = DeviceStatus::AVAILABLE;
66 return HDF_SUCCESS;
67 }
68
GetSupportedOperation(const Model & model,std::vector<bool> & ops)69 int32_t NnrtDeviceService::GetSupportedOperation(const Model& model, std::vector<bool>& ops)
70 {
71 size_t nodeSize = model.nodes.size();
72 auto nodes = model.nodes;
73 ops.resize(nodeSize, false);
74 auto& regInstance = NodeRegistry::GetSingleton();
75 for (size_t i = 0; i < nodeSize; i++) {
76 ops[i] = regInstance.IsNodeTypeExist(nodes[i].nodeType);
77 }
78 return HDF_SUCCESS;
79 }
80
IsFloat16PrecisionSupported(bool & isSupported)81 int32_t NnrtDeviceService::IsFloat16PrecisionSupported(bool& isSupported)
82 {
83 isSupported = true;
84 return HDF_SUCCESS;
85 }
86
IsPerformanceModeSupported(bool & isSupported)87 int32_t NnrtDeviceService::IsPerformanceModeSupported(bool& isSupported)
88 {
89 isSupported = true;
90 return HDF_SUCCESS;
91 }
92
IsPrioritySupported(bool & isSupported)93 int32_t NnrtDeviceService::IsPrioritySupported(bool& isSupported)
94 {
95 isSupported = false;
96 return HDF_SUCCESS;
97 }
98
IsDynamicInputSupported(bool & isSupported)99 int32_t NnrtDeviceService::IsDynamicInputSupported(bool& isSupported)
100 {
101 isSupported = true;
102 return HDF_SUCCESS;
103 }
104
PrepareModel(const Model & model,const ModelConfig & config,sptr<IPreparedModel> & preparedModel)105 int32_t NnrtDeviceService::PrepareModel(const Model& model, const ModelConfig& config,
106 sptr<IPreparedModel>& preparedModel)
107 {
108 auto ret = ValidateModel(model);
109 if (ret != HDF_SUCCESS) {
110 HDF_LOGE("Model is invalid.");
111 return ret;
112 }
113
114 auto graph = TransModelToGraph(model);
115 if (graph == nullptr) {
116 HDF_LOGE("Transfrom model to graph failed.");
117 return HDF_ERR_INVALID_PARAM;
118 }
119
120 ret = ValidateModelConfig(config);
121 if (ret != HDF_SUCCESS) {
122 HDF_LOGE("ModelConfig is invalid.");
123 return ret;
124 }
125
126 auto context = TransModelConfig(config);
127 sptr<PreparedModelService> service = new (std::nothrow) PreparedModelService(context);
128 if (service == nullptr) {
129 HDF_LOGE("Create new PreparedModelService instance failed.");
130 return HDF_ERR_MALLOC_FAIL;
131 }
132
133 ret = service->Compile(graph);
134 if (ret != HDF_SUCCESS) {
135 HDF_LOGE("Prepared model failed.");
136 return ret;
137 }
138
139 preparedModel = service;
140 return HDF_SUCCESS;
141 }
142
IsModelCacheSupported(bool & isSupported)143 int32_t NnrtDeviceService::IsModelCacheSupported(bool& isSupported)
144 {
145 isSupported = true;
146 return HDF_SUCCESS;
147 }
148
PrepareModelFromModelCache(const std::vector<SharedBuffer> & modelCache,const ModelConfig & config,sptr<IPreparedModel> & preparedModel)149 int32_t NnrtDeviceService::PrepareModelFromModelCache(const std::vector<SharedBuffer>& modelCache,
150 const ModelConfig& config, sptr<IPreparedModel>& preparedModel)
151 {
152 HDF_LOGD("Using cache to prepare model.");
153
154 // modelCache must be 1, because PreparedModel only export one cache file.
155 if (modelCache.size() != 1) {
156 HDF_LOGE("The size of modelCache vector is not valid, it should be one elememt in that vector.");
157 return HDF_ERR_INVALID_PARAM;
158 }
159
160 SharedBufferParser parser;
161 auto ret = parser.Init(modelCache[0]);
162 if (ret != HDF_SUCCESS) {
163 HDF_LOGE("Parse modle buffer failed.");
164 return HDF_ERR_INVALID_PARAM;
165 }
166
167 void* modelBuffer = parser.GetBufferPtr();
168 auto context = TransModelConfig(config);
169 sptr<PreparedModelService> service = new (std::nothrow) PreparedModelService(context);
170 if (service == nullptr) {
171 HDF_LOGE("Create new instance PreparedModelService failed.");
172 return HDF_ERR_MALLOC_FAIL;
173 }
174
175 ret = service->Compile(modelBuffer, modelCache[0].dataSize);
176 if (ret != HDF_SUCCESS) {
177 HDF_LOGE("Prepared model failed.");
178 return ret;
179 }
180
181 preparedModel = service;
182 return HDF_SUCCESS;
183 }
184
AllocateBuffer(uint32_t length,SharedBuffer & buffer)185 int32_t NnrtDeviceService::AllocateBuffer(uint32_t length, SharedBuffer& buffer)
186 {
187 sptr<Ashmem> ashptr = Ashmem::CreateAshmem("allocateBuffer", length);
188 if (ashptr == nullptr) {
189 HDF_LOGE("Create shared memory failed.");
190 return HDF_FAILURE;
191 }
192
193 if (!ashptr->MapReadAndWriteAshmem()) {
194 HDF_LOGE("Map allocate buffer failed.");
195 return HDF_FAILURE;
196 }
197
198 buffer.fd = ashptr->GetAshmemFd();
199 buffer.bufferSize = ashptr->GetAshmemSize();
200 buffer.offset = 0;
201 buffer.dataSize = length;
202
203 m_ashmems[buffer.fd] = ashptr;
204 return HDF_SUCCESS;
205 }
206
ReleaseBuffer(const SharedBuffer & buffer)207 int32_t NnrtDeviceService::ReleaseBuffer(const SharedBuffer& buffer)
208 {
209 // parser will close current fd.
210 SharedBufferParser parser;
211 auto ret = parser.Init(buffer);
212 if (ret != HDF_SUCCESS) {
213 HDF_LOGE("Parse buffer failed.");
214 return HDF_ERR_INVALID_PARAM;
215 }
216
217 for (auto& ash : m_ashmems) {
218 ash.second->UnmapAshmem();
219 ash.second->CloseAshmem();
220 }
221 m_ashmems.clear();
222 return HDF_SUCCESS;
223 }
224
ValidateModelConfig(const ModelConfig & config) const225 int32_t NnrtDeviceService::ValidateModelConfig(const ModelConfig& config) const
226 {
227 if (!ValidatePerformanceMode(config.mode)) {
228 HDF_LOGE("PerformanceMode is invalid. mode=%d", config.mode);
229 return HDF_ERR_INVALID_PARAM;
230 }
231
232 if (!ValidatePriority(config.priority)) {
233 HDF_LOGE("Priority is invalid. priority=%d", config.priority);
234 return HDF_ERR_INVALID_PARAM;
235 }
236
237 return HDF_SUCCESS;
238 }
239
ValidateModel(const Model & model) const240 int32_t NnrtDeviceService::ValidateModel(const Model& model) const
241 {
242 if (model.allTensors.empty()) {
243 HDF_LOGE("Model has no tensors.");
244 return HDF_ERR_INVALID_PARAM;
245 }
246
247 if (model.subGraph.empty()) {
248 HDF_LOGE("Model has no subGraphs.");
249 return HDF_ERR_INVALID_PARAM;
250 }
251
252 if (model.nodes.empty()) {
253 HDF_LOGE("Model has no nodes.");
254 return HDF_ERR_INVALID_PARAM;
255 }
256
257 if (model.inputIndex.empty()) {
258 HDF_LOGE("Model has no input.");
259 return HDF_ERR_INVALID_PARAM;
260 }
261
262 if (model.outputIndex.empty()) {
263 HDF_LOGE("Model has no output.");
264 return HDF_ERR_INVALID_PARAM;
265 }
266
267 size_t tensorSize = model.allTensors.size();
268 for (auto index : model.inputIndex) {
269 if (index > tensorSize) {
270 HDF_LOGE("Input index is invalid, index=%u", index);
271 return HDF_ERR_INVALID_PARAM;
272 }
273 }
274
275 for (auto index : model.outputIndex) {
276 if (index > tensorSize) {
277 HDF_LOGE("Output index is invalid, index=%u", index);
278 return HDF_ERR_INVALID_PARAM;
279 }
280 }
281
282 return HDF_SUCCESS;
283 }
284
TransModelToGraph(const Model & model) const285 std::shared_ptr<mindspore::schema::MetaGraphT> NnrtDeviceService::TransModelToGraph(const Model& model) const
286 {
287 auto metaGraph = std::make_shared<mindspore::schema::MetaGraphT>();
288 metaGraph->name = model.name;
289 metaGraph->version = mindspore::Version();
290
291 std::unique_ptr<mindspore::schema::TensorT> transTensor{nullptr};
292 for (auto tensor : model.allTensors) {
293 transTensor = TransTensor(tensor);
294 if (transTensor == nullptr) {
295 HDF_LOGE("Transform tensor failed.");
296 return nullptr;
297 }
298 metaGraph->allTensors.emplace_back(std::move(transTensor));
299 }
300 metaGraph->inputIndex = model.inputIndex;
301 metaGraph->outputIndex = model.outputIndex;
302
303 // Transform node
304 std::unique_ptr<mindspore::schema::CNodeT> transNode {nullptr};
305 for (auto& node : model.nodes) {
306 transNode = TransNode(node);
307 if (transNode == nullptr) {
308 HDF_LOGE("Transform node failed, node name=%{public}s", node.name.c_str());
309 return nullptr;
310 }
311 metaGraph->nodes.emplace_back(std::move(transNode));
312 }
313
314 // Transform subgraph
315 const size_t numTensor = model.allTensors.size();
316 for (auto graph : model.subGraph) {
317 metaGraph->subGraph.emplace_back(TransSubGraph(graph, numTensor));
318 }
319 return metaGraph;
320 }
321
TransTensor(const Tensor & tensor) const322 std::unique_ptr<mindspore::schema::TensorT> NnrtDeviceService::TransTensor(const Tensor& tensor) const
323 {
324 if (!ValidateDataType(tensor.dataType)) {
325 HDF_LOGE("DataType of tensor is invalid. dataType=%d", tensor.dataType);
326 return nullptr;
327 }
328
329 if (!ValidateFormat(tensor.format)) {
330 HDF_LOGE("Format of tensor is invalid. format=%d", tensor.format);
331 return nullptr;
332 }
333
334 auto schemaTensor = std::make_unique<mindspore::schema::TensorT>();
335 schemaTensor->name = tensor.name;
336 schemaTensor->dataType = static_cast<int32_t>(tensor.dataType);
337 schemaTensor->format = static_cast<mindspore::schema::Format>(tensor.format);
338 schemaTensor->dims = tensor.dims;
339 for (auto param : tensor.quantParams) {
340 auto quantParam = std::make_unique<mindspore::schema::QuantParamT>();
341 quantParam->scale = param.scale;
342 quantParam->zeroPoint = param.zeroPoint;
343 quantParam->numBits = param.numBits;
344 quantParam->inited = true;
345 schemaTensor->quantParams.emplace_back(std::move(quantParam));
346 }
347
348 if (tensor.data.fd != INVALID_FD) {
349 SharedBufferParser parser;
350 auto ret = parser.Init(tensor.data);
351 if (ret != HDF_SUCCESS) {
352 HDF_LOGE("Parse tensor data failed.");
353 return nullptr;
354 }
355
356 auto data = parser.GetBufferPtr();
357 schemaTensor->data.resize(tensor.data.dataSize);
358 auto memRet = memcpy_s(const_cast<uint8_t*>(schemaTensor->data.data()),
359 tensor.data.dataSize, data, tensor.data.dataSize);
360 if (memRet != EOK) {
361 HDF_LOGW("Copy tensor data failed.");
362 return nullptr;
363 }
364 }
365 return schemaTensor;
366 }
367
TransNode(const Node & node) const368 std::unique_ptr<mindspore::schema::CNodeT> NnrtDeviceService::TransNode(const Node& node) const
369 {
370 auto cnode = std::make_unique<mindspore::schema::CNodeT>();
371 cnode->name = node.name;
372 cnode->inputIndex = node.inputIndex;
373 cnode->outputIndex = node.outputIndex;
374 cnode->quantType = static_cast<mindspore::schema::QuantType>(node.quantType);
375
376 auto& regInstance = NodeRegistry::GetSingleton();
377 auto parseFunc = regInstance.GetNodeFunc(node.nodeType);
378 auto primitive = parseFunc(node.nodeAttr);
379 if (primitive == nullptr) {
380 HDF_LOGE("Parse primitve data failed. node name=%{public}s", node.name.c_str());
381 return nullptr;
382 }
383
384 cnode->primitive = std::move(primitive);
385 return cnode;
386 }
387
TransSubGraph(const SubGraph & graph,const size_t numTensor) const388 std::unique_ptr<mindspore::schema::SubGraphT> NnrtDeviceService::TransSubGraph(const SubGraph& graph,
389 const size_t numTensor) const
390 {
391 auto subGraph = std::make_unique<mindspore::schema::SubGraphT>();
392 subGraph->name = graph.name;
393 subGraph->inputIndices = graph.inputIndices;
394 subGraph->outputIndices = graph.outputIndices;
395 subGraph->nodeIndices = graph.nodeIndices;
396 subGraph->tensorIndices.reserve(numTensor);
397 for (auto i = 0; i < numTensor; i++) {
398 subGraph->tensorIndices.emplace_back(static_cast<uint32_t>(i));
399 }
400 return subGraph;
401 }
402
TransModelConfig(const ModelConfig & config) const403 std::shared_ptr<mindspore::Context> NnrtDeviceService::TransModelConfig(const ModelConfig& config) const
404 {
405 auto context = std::make_shared<mindspore::Context>();
406 const int cpuThreadNum = 2;
407 const int cpuNoAffinities = 0;
408 const int cpuBigCore = 1;
409 const int cpuLittleCore = 2;
410 context->SetThreadNum(cpuThreadNum);
411
412 int mode = cpuNoAffinities;
413 switch (config.mode) {
414 case PerformanceMode::PERFORMANCE_LOW:
415 case PerformanceMode::PERFORMANCE_MEDIUM:
416 mode = cpuLittleCore;
417 break;
418 case PerformanceMode::PERFORMANCE_HIGH:
419 case PerformanceMode::PERFORMANCE_EXTREME:
420 mode = cpuBigCore;
421 break;
422 default:
423 mode = cpuNoAffinities;
424 }
425 context->SetThreadAffinity(mode);
426
427 auto cpuInfo = std::make_shared<mindspore::CPUDeviceInfo>();
428 cpuInfo->SetEnableFP16(config.enableFloat16);
429 auto& deviceInfos = context->MutableDeviceInfo();
430 deviceInfos.emplace_back(cpuInfo);
431 return context;
432 }
433 } // V1_0
434 } // Nnrt
435 } // HDI
436 } // OHOS
437