1 /*
2 * Copyright (c) 2023 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 "nncompiler.h"
17
18 #include <sys/stat.h>
19 #include <fstream>
20 #include <climits>
21 #include <securec.h>
22
23 #include "validation.h"
24 #include "nncompiled_cache.h"
25 #include "common/utils.h"
26
27 namespace OHOS {
28 namespace NeuralNetworkRuntime {
29 namespace {
30 const int CACHE_INPUT_TENSORDESC_OFFSET = 2;
31 const int CACHE_OUTPUT_TENSORDESC_OFFSET = 1;
32 constexpr int32_t NUMBER_CACHE_INFO_MEMBERS = 3;
33 const std::string EXTENSION_KEY_MODEL_NAME = "ModelName";
34 const std::string EXTENSION_KEY_FM_SHARED = "NPU_FM_SHARED";
35 const int OPVERSION_SUBSTR_NUM = 2;
36 const std::string CURRENT_VERSION = "0x00000000";
37 const std::string HIAI_VERSION_PATH = "/data/data/hiai/version";
38
39 struct SerializedTensorDesc {
40 public:
41 SerializedTensorDesc() = default;
42 ~SerializedTensorDesc() = default;
43
CopyFromTensorDescOHOS::NeuralNetworkRuntime::__anone3af5d9a0111::SerializedTensorDesc44 OH_NN_ReturnCode CopyFromTensorDesc(const std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>& tensorDesc)
45 {
46 if (tensorDesc.first == nullptr) {
47 LOGE("CopyFromTensorDesc failed, tensor desc is nullptr.");
48 return OH_NN_NULL_PTR;
49 }
50 OH_NN_ReturnCode ret = tensorDesc.first->GetDataType(&m_dataType);
51 if (ret != OH_NN_SUCCESS) {
52 LOGE("CopyFromTensorDesc failed, error happened when getting data type from tensor desc.");
53 return ret;
54 }
55
56 ret = tensorDesc.first->GetFormat(&m_format);
57 if (ret != OH_NN_SUCCESS) {
58 LOGE("CopyFromTensorDesc failed, error happened when getting format from tensor desc.");
59 return ret;
60 }
61
62 ret = tensorDesc.first->GetShape(&m_shape, &m_shapeNum);
63 if (ret != OH_NN_SUCCESS) {
64 LOGE("CopyFromTensorDesc failed, error happened when getting shape from tensor desc.");
65 return ret;
66 }
67
68 ret = tensorDesc.first->GetName(&m_name);
69 if (ret != OH_NN_SUCCESS) {
70 LOGE("CopyFromTensorDesc failed, error happened when getting name from tensor desc.");
71 return ret;
72 }
73
74 m_tensorType = tensorDesc.second;
75
76 return ret;
77 }
78
CopyToTensorDescOHOS::NeuralNetworkRuntime::__anone3af5d9a0111::SerializedTensorDesc79 OH_NN_ReturnCode CopyToTensorDesc(TensorDesc& tensorDesc) const
80 {
81 OH_NN_ReturnCode ret = tensorDesc.SetDataType(m_dataType);
82 if (ret != OH_NN_SUCCESS) {
83 LOGE("CopyToTensorDesc failed, error happened when setting data type to tensor desc.");
84 return ret;
85 }
86
87 ret = tensorDesc.SetFormat(m_format);
88 if (ret != OH_NN_SUCCESS) {
89 LOGE("CopyToTensorDesc failed, error happened when setting format to tensor desc.");
90 return ret;
91 }
92
93 ret = tensorDesc.SetShape(m_shape, m_shapeNum);
94 if (ret != OH_NN_SUCCESS) {
95 LOGE("CopyToTensorDesc failed, error happened when setting shape to tensor desc.");
96 return ret;
97 }
98
99 ret = tensorDesc.SetName(m_name);
100 if (ret != OH_NN_SUCCESS) {
101 LOGE("CopyToTensorDesc failed, error happened when setting name to tensor desc.");
102 }
103
104 return ret;
105 }
106
107 public:
108 OH_NN_DataType m_dataType{OH_NN_UNKNOWN};
109 OH_NN_Format m_format{OH_NN_FORMAT_NONE};
110 OH_NN_TensorType m_tensorType{OH_NN_TENSOR};
111 size_t m_shapeNum{0};
112 int32_t* m_shape{nullptr};
113 const char* m_name{nullptr}; // null-terminated
114 };
115
116 const size_t SIZE_OF_DATATYPE = sizeof(SerializedTensorDesc::m_dataType);
117 const size_t SIZE_OF_FORMAT = sizeof(SerializedTensorDesc::m_format);
118 const size_t SIZE_OF_TENSOR_TYPE = sizeof(SerializedTensorDesc::m_tensorType);
119 const size_t SIZE_OF_SHAPE_NUM = sizeof(SerializedTensorDesc::m_shapeNum);
120 } // namespace
121
NNCompiler(std::shared_ptr<Device> device,size_t backendID)122 NNCompiler::NNCompiler(std::shared_ptr<Device> device, size_t backendID)
123 : m_device(device),
124 m_backendID(backendID) {}
125
NNCompiler(const void * model,std::shared_ptr<Device> device,size_t backendID)126 NNCompiler::NNCompiler(const void* model, std::shared_ptr<Device> device, size_t backendID)
127 : m_device(device),
128 m_backendID(backendID)
129 {
130 m_innerModel = const_cast<InnerModel*>(reinterpret_cast<const InnerModel*>(model));
131 m_liteGraph = m_innerModel->GetLiteGraphs();
132 m_inputTensorDescs = m_innerModel->GetInputTensorDescs();
133 m_outputTensorDescs = m_innerModel->GetOutputTensorDescs();
134 m_metaGraph = m_innerModel->GetMetaGraph();
135 m_extensionConfig = m_innerModel->GetExtensionConfig();
136 }
137
~NNCompiler()138 NNCompiler::~NNCompiler()
139 {
140 if (m_preparedModel != nullptr) {
141 m_preparedModel.reset();
142 }
143 m_inputTensorDescs.clear();
144 m_outputTensorDescs.clear();
145 }
146
GetBackendID() const147 size_t NNCompiler::GetBackendID() const
148 {
149 return m_backendID;
150 }
151
SetCacheDir(const std::string & cacheModelPath,uint32_t version)152 OH_NN_ReturnCode NNCompiler::SetCacheDir(const std::string& cacheModelPath, uint32_t version)
153 {
154 if (m_device == nullptr) {
155 LOGE("[NNCompiler] SetCacheDir failed, m_device is nullptr");
156 return OH_NN_OPERATION_FORBIDDEN;
157 }
158
159 bool isSupportedCache {false};
160 OH_NN_ReturnCode ret = m_device->IsModelCacheSupported(isSupportedCache);
161 if (ret != OH_NN_SUCCESS) {
162 LOGE("[NNCompiler] SetCacheDir failed, fail to call device.");
163 return ret;
164 }
165
166 if (!isSupportedCache && !cacheModelPath.empty()) {
167 LOGE("[NNCompiler] SetCacheDir failed, this device is not support cache setting.");
168 return OH_NN_OPERATION_FORBIDDEN;
169 }
170
171 m_cachePath = cacheModelPath;
172 m_cacheVersion = version;
173
174 return OH_NN_SUCCESS;
175 }
176
SetPerformance(OH_NN_PerformanceMode performance)177 OH_NN_ReturnCode NNCompiler::SetPerformance(OH_NN_PerformanceMode performance)
178 {
179 if (m_device == nullptr) {
180 LOGE("[NNCompiler] SetPerformance failed, m_device is nullptr");
181 return OH_NN_OPERATION_FORBIDDEN;
182 }
183
184 bool isSupportedPerformance {false};
185 OH_NN_ReturnCode ret = m_device->IsPerformanceModeSupported(isSupportedPerformance);
186 if (ret != OH_NN_SUCCESS) {
187 LOGE("[NNCompiler] SetPerformance failed, fail to call device.");
188 return OH_NN_FAILED;
189 }
190
191 if (!isSupportedPerformance && (performance != OH_NN_PERFORMANCE_NONE)) {
192 LOGE("[NNCompiler] SetPerformance failed, this device is not support performance setting.");
193 return OH_NN_OPERATION_FORBIDDEN;
194 }
195
196 if (!Validation::ValidatePerformanceMode(performance)) {
197 LOGE("[NNCompiler] SetPerformance failed, performance=%{public}d is invalid", performance);
198 return OH_NN_INVALID_PARAMETER;
199 }
200
201 m_performance = performance;
202 return OH_NN_SUCCESS;
203 }
204
SetPriority(OH_NN_Priority priority)205 OH_NN_ReturnCode NNCompiler::SetPriority(OH_NN_Priority priority)
206 {
207 if (m_device == nullptr) {
208 LOGE("[NNCompiler] SetPriority failed, m_device is nullptr");
209 return OH_NN_OPERATION_FORBIDDEN;
210 }
211
212 bool isSupportedPriority {false};
213 OH_NN_ReturnCode ret = m_device->IsPrioritySupported(isSupportedPriority);
214 if (ret != OH_NN_SUCCESS) {
215 LOGE("[NNCompiler] SetPriority failed, fail to call device.");
216 return ret;
217 }
218
219 if (!isSupportedPriority && (priority != OH_NN_PRIORITY_NONE)) {
220 LOGE("[NNCompiler] SetPriority failed, this device is not support priority setting.");
221 return OH_NN_OPERATION_FORBIDDEN;
222 }
223
224 if (!Validation::ValidatePriority(priority)) {
225 LOGE("[NNCompiler] SetPriority failed, priority=%{public}d is invalid.", priority);
226 return OH_NN_INVALID_PARAMETER;
227 }
228
229 m_priority = priority;
230 return OH_NN_SUCCESS;
231 }
232
SetEnableFp16(bool isFp16)233 OH_NN_ReturnCode NNCompiler::SetEnableFp16(bool isFp16)
234 {
235 if (m_device == nullptr) {
236 LOGE("[NNCompiler] SetEnableFp16 failed, m_device is nullptr");
237 return OH_NN_OPERATION_FORBIDDEN;
238 }
239
240 bool isSupportedFp16 {false};
241 OH_NN_ReturnCode ret = m_device->IsFloat16PrecisionSupported(isSupportedFp16);
242 if (ret != OH_NN_SUCCESS) {
243 LOGE("[NNCompiler] SetEnableFp16 failed, fail to call device.");
244 return ret;
245 }
246
247 if (!isSupportedFp16 && isFp16) {
248 LOGE("[NNCompiler] SetEnableFp16 failed, this device is not support float16 precision setting.");
249 return OH_NN_OPERATION_FORBIDDEN;
250 }
251
252 m_enableFp16 = isFp16;
253 return OH_NN_SUCCESS;
254 }
255
IsBuild() const256 bool NNCompiler::IsBuild() const
257 {
258 return m_isBuild;
259 }
260
IsSupportedModel(const std::shared_ptr<mindspore::lite::LiteGraph> & liteGraph,bool & isSupportedModel) const261 OH_NN_ReturnCode NNCompiler::IsSupportedModel(const std::shared_ptr<mindspore::lite::LiteGraph>& liteGraph,
262 bool& isSupportedModel) const
263 {
264 std::vector<bool> supportedList;
265 OH_NN_ReturnCode ret = m_device->GetSupportedOperation(liteGraph, supportedList);
266 if (ret != OH_NN_SUCCESS) {
267 LOGE("[NNCompiler] Build failed, error happened when getting supported operation.");
268 return ret;
269 }
270
271 bool isNotSupport = std::any_of(supportedList.begin(), supportedList.end(), [](bool isSupport) {
272 return !isSupport;
273 });
274 if (isNotSupport) {
275 LOGE("[NNCompiler] Build failed, current device not support the model, device id: %{public}zu.", m_backendID);
276 isSupportedModel = false;
277 return OH_NN_FAILED;
278 }
279
280 isSupportedModel = true;
281 return OH_NN_SUCCESS;
282 }
283
CheckModelParameter() const284 OH_NN_ReturnCode NNCompiler::CheckModelParameter() const
285 {
286 // If m_innerModel is not passed, the compiler must be construct from cache, jump check m_innerModel.
287 if (m_innerModel == nullptr) {
288 LOGW("[NNCompiler] Restoring from cache not need to check model.");
289 return OH_NN_SUCCESS;
290 }
291
292 // m_innerModel is not constructed completely.
293 if ((m_liteGraph == nullptr) && (m_metaGraph == nullptr)) {
294 LOGE("[NNCompiler] LiteGraph and metaGraph are empty, m_innerModel is not constructed completely.");
295 return OH_NN_INVALID_PARAMETER;
296 }
297
298 if ((m_liteGraph != nullptr) && (m_metaGraph != nullptr)) {
299 LOGE("[NNCompiler] Both LiteGraph and metaGraph are not empty.");
300 return OH_NN_INVALID_PARAMETER;
301 }
302
303 return OH_NN_SUCCESS;
304 }
305
IsOfflineModel(bool & isOfflineModel) const306 OH_NN_ReturnCode NNCompiler::IsOfflineModel(bool& isOfflineModel) const
307 {
308 // If m_innerModel is not passed, the compiler must be construct from cache, jump check m_innerModel.
309 if (m_innerModel == nullptr) {
310 LOGW("[NNCompiler] Restoring from cache not need to judge offline model.");
311 return OH_NN_SUCCESS;
312 }
313
314 isOfflineModel = false; // Initialize the returned value
315 if (m_metaGraph != nullptr) {
316 isOfflineModel = false;
317 return OH_NN_SUCCESS;
318 }
319
320 if (m_liteGraph->all_nodes_.size() == 0) {
321 LOGE("[NNCompiler] Find empty node in the model.");
322 return OH_NN_INVALID_PARAMETER;
323 }
324
325 // If the model consists of more than 1 node, it will not be considered as offline model.
326 if (m_liteGraph->all_nodes_.size() > 1) {
327 isOfflineModel = false;
328 return OH_NN_SUCCESS;
329 }
330
331 const mindspore::lite::LiteGraph::Node* pNode = m_liteGraph->all_nodes_[0];
332 if (pNode == nullptr) {
333 LOGE("[NNCompiler] Find invalid node in the model.");
334 return OH_NN_NULL_PTR;
335 }
336
337 const mindspore::lite::NodeType& nodeType = mindspore::lite::MindIR_Primitive_GetType(pNode->primitive_);
338 if (nodeType == mindspore::lite::NodeType::NODE_TYPE_CUSTOM) {
339 isOfflineModel = true;
340 }
341
342 return OH_NN_SUCCESS;
343 }
344
BuildOfflineModel()345 OH_NN_ReturnCode NNCompiler::BuildOfflineModel()
346 {
347 ModelConfig config {m_enableFp16, m_performance, m_priority};
348 OH_NN_ReturnCode ret = m_device->PrepareOfflineModel(m_liteGraph, config, m_preparedModel);
349 if (ret != OH_NN_SUCCESS) {
350 LOGE("[NNCompiler] Preparing model failed when building from offline model.");
351 return ret;
352 }
353
354 return OH_NN_SUCCESS;
355 }
356
NormalBuild()357 OH_NN_ReturnCode NNCompiler::NormalBuild()
358 {
359 if ((m_liteGraph == nullptr) && (m_metaGraph == nullptr)) {
360 LOGE("[NNCompiler] Build failed, both liteGraph and metaGraph are nullptr.");
361 return OH_NN_INVALID_PARAMETER;
362 }
363
364 if ((m_liteGraph != nullptr) && (m_metaGraph != nullptr)) {
365 LOGE("[NNCompiler] Build failed, neither liteGraph nor metaGraph are nullptr.");
366 return OH_NN_INVALID_PARAMETER;
367 }
368
369 // 判断是否支持模型
370 bool isSupportedModel = true;
371 OH_NN_ReturnCode ret = IsSupportedModel(m_liteGraph, isSupportedModel);
372 if (ret != OH_NN_SUCCESS) {
373 LOGE("[NNCompiler] Build failed, error happened when judge if support the model.");
374 return ret;
375 } else if (!isSupportedModel) {
376 LOGE("[NNCompiler] Build failed, current device not support the model.");
377 return OH_NN_FAILED;
378 }
379
380 ModelConfig config {m_enableFp16, static_cast<OH_NN_PerformanceMode>(m_performance),
381 static_cast<OH_NN_Priority>(m_priority), m_cachePath, m_extensionConfig};
382 if (m_liteGraph != nullptr) {
383 ret = m_device->PrepareModel(m_liteGraph, config, m_preparedModel);
384 }
385 if (m_metaGraph != nullptr) {
386 ret = m_device->PrepareModel(m_metaGraph, config, m_preparedModel);
387 }
388 if (ret != OH_NN_SUCCESS) {
389 LOGE("[NNCompiler] Build failed, fail to prepare model when normally building.");
390 return ret;
391 }
392 m_isBuild = true;
393
394 // 保存cache
395 if (!m_cachePath.empty()) {
396 ret = SaveToCacheFile();
397 if (ret != OH_NN_SUCCESS) {
398 LOGE("[NNCompiler] Build success, but fail to save cache to file.");
399 return ret;
400 }
401 }
402
403 return OH_NN_SUCCESS;
404 }
405
Build()406 OH_NN_ReturnCode NNCompiler::Build()
407 {
408 if (m_isBuild) {
409 LOGE("[NNCompiler] Build failed, cannot build again.");
410 return OH_NN_OPERATION_FORBIDDEN;
411 }
412
413 if (m_device == nullptr) {
414 LOGE("[NNCompiler] Build failed, the m_device is nullptr.");
415 return OH_NN_OPERATION_FORBIDDEN;
416 }
417
418 OH_NN_ReturnCode ret = CheckModelParameter();
419 if (ret != OH_NN_SUCCESS) {
420 LOGE("[NNCompiler] CheckModelParameter failed, some error happened when checking model parameter.");
421 return ret;
422 }
423
424 // Prepare from offline model.
425 bool isOfflineModel {false};
426 ret = IsOfflineModel(isOfflineModel);
427 if (ret != OH_NN_SUCCESS) {
428 LOGE("[NNCompiler] Build failed, fail to identify the offline model.");
429 return ret;
430 }
431
432 if (isOfflineModel) {
433 ret = BuildOfflineModel();
434 if (ret != OH_NN_SUCCESS) {
435 LOGE("[NNCompiler] Build failed, Failed to build offline model.");
436 return ret;
437 }
438
439 m_isBuild = true;
440 return OH_NN_SUCCESS;
441 }
442
443 ret = OnlineBuild();
444 if (ret != OH_NN_SUCCESS) {
445 LOGE("[NNCompiler] OnlineBuild failed, Failed to build model online.");
446 return ret;
447 }
448
449 return OH_NN_SUCCESS;
450 }
451
OnlineBuild()452 OH_NN_ReturnCode NNCompiler::OnlineBuild()
453 {
454 // cache存在,从cache直接复原prepareModel、input/output TensorDesc
455 OH_NN_ReturnCode ret = RestoreFromCacheFile();
456 if (ret != OH_NN_SUCCESS) {
457 LOGW("[NNCompiler] cache file is failed, to delete cache file.");
458 char path[PATH_MAX];
459 if (realpath(m_cachePath.c_str(), path) == nullptr) {
460 LOGW("[NNCompiledCache] WriteCacheInfo failed, fail to get the real path of cacheDir.");
461 }
462
463 std::string cachePath = path;
464 std::string cacheInfo = cachePath + "/" + m_extensionConfig.modelName + "cache_info.nncache";
465 if (std::filesystem::exists(cacheInfo)) {
466 std::filesystem::remove_all(cacheInfo);
467 }
468 }
469
470 if (ret == OH_NN_OPERATION_FORBIDDEN) {
471 LOGE("[NNCompiler] Build failed, operation is forbidden.");
472 return ret;
473 }
474 if (ret == OH_NN_SUCCESS) {
475 LOGI("[NNCompiler] Build success, restore from cache file.");
476 m_isBuild = true;
477 }
478
479 // cache不存在或cache restore失败,走在线构图
480 if (!m_isBuild) {
481 ret = NormalBuild();
482 if (ret != OH_NN_SUCCESS) {
483 LOGE("[NNCompiler] Build failed, fail to build model online.");
484 return ret;
485 }
486 }
487
488 return OH_NN_SUCCESS;
489 }
490
ReleaseBuffer(std::vector<Buffer> & buffers) const491 void NNCompiler::ReleaseBuffer(std::vector<Buffer>& buffers) const
492 {
493 for (size_t i = 0; i < buffers.size(); ++i) {
494 // release tensor buffer which is allocated by new method.
495 delete[] reinterpret_cast<char*>(buffers[i].data);
496 }
497 buffers.clear();
498 }
499
ReleaseBufferByDevice(std::vector<Buffer> & buffers) const500 void NNCompiler::ReleaseBufferByDevice(std::vector<Buffer>& buffers) const
501 {
502 for (size_t i = 0; i < buffers.size(); ++i) {
503 // release cache buffer which is allocated by idevice.
504 m_device->ReleaseBuffer(buffers[i].data);
505 }
506 buffers.clear();
507 }
508
SaveToCacheFile() const509 OH_NN_ReturnCode NNCompiler::SaveToCacheFile() const
510 {
511 if (m_cachePath.empty()) {
512 LOGE("[NNCompiler] SaveToCacheFile failed, m_cachePath is empty.");
513 return OH_NN_INVALID_PARAMETER;
514 }
515
516 if (m_cacheVersion == INVALID_CAHCE_VERSION) {
517 LOGE("[NNCompiler] SaveToCacheFile failed, cache version is invalid. Please set a valid cache version.");
518 return OH_NN_INVALID_PARAMETER;
519 }
520
521 if (m_preparedModel == nullptr) {
522 LOGE("[NNCompiler] SaveToCacheFile failed, m_preparedModel is nullptr. Please construct prepareModel first.");
523 return OH_NN_FAILED;
524 }
525
526 std::vector<Buffer> caches;
527 std::vector<Buffer> tensorBuffers;
528 OH_NN_ReturnCode ret = m_preparedModel->ExportModelCache(caches);
529 if (ret != OH_NN_SUCCESS) {
530 LOGE("[NNCompiler] SaveToCacheFile failed, error happened when exporting model cache.");
531 return ret;
532 }
533
534 NNCompiledCache compiledCache;
535 ret = compiledCache.SetBackend(m_backendID);
536 if (ret != OH_NN_SUCCESS) {
537 LOGE("[NNCompiler] SaveToCacheFile failed, fail to set backend.");
538 return ret;
539 }
540
541 Buffer inputTensorDescBuffer;
542 ret = SerializeTensorsToBuffer(m_inputTensorDescs, inputTensorDescBuffer);
543 if (ret != OH_NN_SUCCESS) {
544 LOGE("[NNCompiler] SaveToCacheFile failed, error happened when serializing input tensor desc.");
545 return ret;
546 }
547 caches.emplace_back(inputTensorDescBuffer);
548 tensorBuffers.emplace_back(inputTensorDescBuffer);
549
550 Buffer outputTensorDescBuffer;
551 ret = SerializeTensorsToBuffer(m_outputTensorDescs, outputTensorDescBuffer);
552 if (ret != OH_NN_SUCCESS) {
553 LOGE("[NNCompiler] SaveToCacheFile failed, error happened when serializing output tensor desc.");
554 ReleaseBuffer(tensorBuffers);
555 return ret;
556 }
557 caches.emplace_back(outputTensorDescBuffer);
558 tensorBuffers.emplace_back(outputTensorDescBuffer);
559
560 compiledCache.SetModelName(m_extensionConfig.modelName);
561 ret = compiledCache.Save(caches, m_cachePath, m_cacheVersion);
562 if (ret != OH_NN_SUCCESS) {
563 LOGE("[NNCompiler] SaveToCacheFile failed, error happened when saving model cache.");
564 ReleaseBuffer(tensorBuffers);
565 return ret;
566 }
567
568 ReleaseBuffer(tensorBuffers);
569 LOGI("[NNCompiler] Export model cache successfully.");
570 return OH_NN_SUCCESS;
571 }
572
RestoreFromCacheFile()573 OH_NN_ReturnCode NNCompiler::RestoreFromCacheFile()
574 {
575 if (m_cachePath.empty()) {
576 LOGE("[NNCompiler] RestoreFromCacheFile failed, path is empty.");
577 return OH_NN_INVALID_PARAMETER;
578 }
579
580 if (m_cacheVersion == INVALID_CAHCE_VERSION) {
581 LOGE("[NNCompiler] RestoreFromCacheFile failed, cache version is invalid. Please set a valid cache version.");
582 return OH_NN_INVALID_PARAMETER;
583 }
584
585 if (m_preparedModel != nullptr) {
586 LOGE("[NNCompiler] RestoreFromCacheFile failed, m_preparedModel is not nullptr.");
587 return OH_NN_FAILED;
588 }
589
590 NNCompiledCache compiledCache;
591 OH_NN_ReturnCode ret = compiledCache.SetBackend(m_backendID);
592 if (ret != OH_NN_SUCCESS) {
593 LOGE("[NNCompiler] RestoreFromCacheFile failed, fail to set backend.");
594 return ret;
595 }
596
597 std::vector<Buffer> caches;
598 compiledCache.SetModelName(m_extensionConfig.modelName);
599 ret = compiledCache.Restore(m_cachePath, m_cacheVersion, caches);
600 if (ret != OH_NN_SUCCESS) {
601 LOGE("[NNCompiler] RestoreFromCacheFile failed, error happened when restoring model cache.");
602 ReleaseBufferByDevice(caches);
603 return ret;
604 }
605
606 size_t cacheNum = caches.size();
607 std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>> inputTensorDescs;
608 ret = DeserializedTensorsFromBuffer(caches[cacheNum - CACHE_INPUT_TENSORDESC_OFFSET], inputTensorDescs);
609 if (ret != OH_NN_SUCCESS) {
610 LOGE("[NNCompiler] RestoreFromCacheFile failed, error happened when deserializing input tensor desc.");
611 ReleaseBufferByDevice(caches);
612 return ret;
613 }
614
615 std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>> outputTensorDescs;
616 ret = DeserializedTensorsFromBuffer(caches[cacheNum - CACHE_OUTPUT_TENSORDESC_OFFSET], outputTensorDescs);
617 if (ret != OH_NN_SUCCESS) {
618 LOGE("[NNCompiler] RestoreFromCacheFile failed, error happened when deserializing output tensor desc.");
619 ReleaseBufferByDevice(caches);
620 return ret;
621 }
622
623 ModelConfig config;
624 config.enableFloat16 = m_enableFp16;
625 config.mode = m_performance;
626 config.priority = m_priority;
627 config.extensionConfig.isNpuFmShared = m_extensionConfig.isNpuFmShared;
628 std::vector<Buffer> modelOnlyCaches(caches.begin(), caches.end() - CACHE_INPUT_TENSORDESC_OFFSET);
629 bool isUpdatable = false;
630 ret = m_device->PrepareModelFromModelCache(modelOnlyCaches, config, m_preparedModel, isUpdatable);
631 if (ret != OH_NN_SUCCESS) {
632 LOGE("[NNCompiler] RestoreFromCacheFile failed, error happened when preparing model from cache.");
633 ReleaseBufferByDevice(caches);
634 return ret;
635 }
636
637 if (isUpdatable) {
638 LOGI("isUpdatable is true");
639
640 std::string currentVersion = CURRENT_VERSION;
641 char versionPath[PATH_MAX];
642 if (realpath(HIAI_VERSION_PATH.c_str(), versionPath) != nullptr) {
643 std::ifstream inf(versionPath);
644 if (inf.is_open()) {
645 getline(inf, currentVersion);
646 }
647 inf.close();
648 }
649 int currentOpVersion = std::stoi(currentVersion.substr(OPVERSION_SUBSTR_NUM));
650
651 NNCompiledCacheInfo modelCacheInfo;
652 std::string cacheInfoPath = m_cachePath + "/" + m_extensionConfig.modelName + "cache_info.nncache";
653 ret = compiledCache.CheckCacheInfo(modelCacheInfo, cacheInfoPath);
654 if (ret != OH_NN_SUCCESS) {
655 LOGE("[NNCompiledCache] isUpdatable is true to check cache info failed.");
656 return ret;
657 }
658
659 LOGI("isUpdatable currentOpVersion is: %{public}d", currentOpVersion);
660 LOGI("isUpdatable modelCacheInfo opVersion is %{public}d", static_cast<int>(modelCacheInfo.opVersion));
661
662 if (currentOpVersion > modelCacheInfo.opVersion) {
663 const size_t cacheNumber = caches.size();
664 uint32_t cacheSize = NUMBER_CACHE_INFO_MEMBERS + cacheNumber + 1;
665 uint32_t infoCharNumber = cacheSize * sizeof(int64_t);
666
667 std::unique_ptr<int64_t[]> cacheInfo = CreateUniquePtr<int64_t[]>(cacheSize);
668 if (cacheInfo == nullptr) {
669 LOGE("[NNCompiledCache] isUpdatable is true to create unique failed.");
670 return OH_NN_MEMORY_ERROR;
671 }
672
673 auto cacheInfoPtr = cacheInfo.get();
674 *cacheInfoPtr++ = modelCacheInfo.fileNumber;
675 *cacheInfoPtr++ = modelCacheInfo.version - 1;
676 *cacheInfoPtr++ = modelCacheInfo.deviceId;
677
678 for (size_t i = 0; i < modelCacheInfo.modelCheckSum.size(); ++i) {
679 *cacheInfoPtr++ = static_cast<int64_t>(modelCacheInfo.modelCheckSum[i]);
680 }
681
682 *cacheInfoPtr++ = currentOpVersion;
683
684 ret = compiledCache.WriteCacheInfo(infoCharNumber, cacheInfo, m_cachePath);
685 if (ret != OH_NN_SUCCESS) {
686 LOGE("[NNCompiledCache] isUpdatable is true to write cache info failed.");
687 return ret;
688 }
689 }
690 }
691
692 ReleaseBufferByDevice(caches);
693
694 m_inputTensorDescs = inputTensorDescs;
695 m_outputTensorDescs = outputTensorDescs;
696 LOGI("[NNCompiler] Restore model cache successfully.");
697 return OH_NN_SUCCESS;
698 }
699
SaveToCacheBuffer(const void * buffer,size_t length,size_t * modelSize) const700 OH_NN_ReturnCode NNCompiler::SaveToCacheBuffer(const void* buffer, size_t length, size_t* modelSize) const
701 {
702 LOGE("[NNCompiler] SaveToCacheBuffer is not supported currently.");
703 return OH_NN_UNSUPPORTED;
704 }
705
RestoreFromCacheBuffer(const void * buffer,size_t length)706 OH_NN_ReturnCode NNCompiler::RestoreFromCacheBuffer(const void* buffer, size_t length)
707 {
708 LOGE("[NNCompiler] RestoreFromCacheBuffer is not supported currently.");
709 return OH_NN_UNSUPPORTED;
710 }
711
SetExtensionConfig(const std::unordered_map<std::string,std::vector<char>> & configs)712 OH_NN_ReturnCode NNCompiler::SetExtensionConfig(const std::unordered_map<std::string, std::vector<char>>& configs)
713 {
714 if (configs.find(EXTENSION_KEY_MODEL_NAME) != configs.end()) {
715 std::vector<char> value = configs.at(EXTENSION_KEY_MODEL_NAME);
716 if (value.empty()) {
717 LOGE("[NNCompiler] SetExtensionConfig get empty model name from configs");
718 return OH_NN_INVALID_PARAMETER;
719 }
720 m_extensionConfig.modelName.assign(value.data(), value.data() + value.size());
721 LOGI("[NNCompiler] SetExtensionConfig get model name:%{public}s.", m_extensionConfig.modelName.c_str());
722 }
723 if (configs.find(EXTENSION_KEY_FM_SHARED) != configs.end()) {
724 m_extensionConfig.isNpuFmShared = true;
725 LOGI("[NNCompiler] SetExtensionConfig NpuFmShared enabled.");
726 }
727 return OH_NN_SUCCESS;
728 }
729
SetOptions(const std::vector<std::shared_ptr<void>> & options)730 OH_NN_ReturnCode NNCompiler::SetOptions(const std::vector<std::shared_ptr<void>>& options)
731 {
732 LOGE("[NNCompiler] SetOptions is not supported for NN compiler currently.");
733 return OH_NN_UNSUPPORTED;
734 }
735
GetModelName(std::string & modelName)736 OH_NN_ReturnCode NNCompiler::GetModelName(std::string& modelName)
737 {
738 modelName = m_extensionConfig.modelName;
739 return OH_NN_SUCCESS;
740 }
741
CreateExecutor()742 NNExecutor* NNCompiler::CreateExecutor()
743 {
744 if (m_device == nullptr) {
745 LOGE("[NNCompiler] CreateExecutor failed, m_device is nullptr");
746 return nullptr;
747 }
748
749 if (m_preparedModel == nullptr) {
750 LOGE("[NNCompiler] CreateExecutor failed, m_preparedModel is nullptr");
751 return nullptr;
752 }
753
754 if (m_inputTensorDescs.empty()) {
755 LOGE("[NNCompiler] CreateExecutor failed, m_inputTensorDescs is empty");
756 return nullptr;
757 }
758
759 if (m_outputTensorDescs.empty()) {
760 LOGE("[NNCompiler] CreateExecutor failed, m_outputTensorDescs is empty");
761 return nullptr;
762 }
763
764 NNExecutor* nnExecutor = new (std::nothrow) NNExecutor(
765 m_backendID, m_device, m_preparedModel, m_inputTensorDescs, m_outputTensorDescs);
766 if (nnExecutor == nullptr) {
767 LOGE("[NNCompiler] CreateExecutor failed, error happend when allocating NN Executor.");
768 return nullptr;
769 }
770
771 return nnExecutor;
772 }
773
SerializeTensorsToBuffer(const std::vector<std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType>> & tensorDescs,Buffer & buffer) const774 OH_NN_ReturnCode NNCompiler::SerializeTensorsToBuffer(
775 const std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>>& tensorDescs, Buffer& buffer) const
776 {
777 std::vector<SerializedTensorDesc> immediateTensorDescs;
778 OH_NN_ReturnCode ret = OH_NN_SUCCESS;
779 for (const auto& tensorDesc : tensorDescs) {
780 SerializedTensorDesc immediateTensorDesc;
781 ret = immediateTensorDesc.CopyFromTensorDesc(tensorDesc);
782 if (ret != OH_NN_SUCCESS) {
783 LOGE("[NNCompiler] SerializeInputsToBuffer failed, error happened when copying tensorDesc to "
784 "SerializedTensorDesc.");
785 immediateTensorDescs.clear();
786 return ret;
787 }
788 immediateTensorDescs.emplace_back(immediateTensorDesc);
789 }
790
791 size_t totalSize = 0;
792 for (const auto& tensorDesc : immediateTensorDescs) {
793 totalSize += SIZE_OF_DATATYPE;
794 totalSize += SIZE_OF_FORMAT;
795 totalSize += SIZE_OF_TENSOR_TYPE;
796 totalSize += SIZE_OF_SHAPE_NUM;
797 totalSize += tensorDesc.m_shapeNum * sizeof(int32_t);
798 totalSize += strlen(tensorDesc.m_name) + 1;
799 }
800
801 // Allocate memory for the serialized data
802 char* serializedData = new (std::nothrow) char[totalSize];
803 if (serializedData == nullptr) {
804 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to create serialized data.");
805 return OH_NN_NULL_PTR;
806 }
807 char* currentPos = serializedData;
808
809 // Serialize each tensor description
810 for (const auto& tensorDesc : immediateTensorDescs) {
811 auto memRet = memcpy_s(currentPos, SIZE_OF_DATATYPE, &tensorDesc.m_dataType, SIZE_OF_DATATYPE);
812 if (memRet != EOK) {
813 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s data type.");
814 delete[] serializedData;
815 return OH_NN_MEMORY_ERROR;
816 }
817 currentPos += SIZE_OF_DATATYPE;
818
819 memRet = memcpy_s(currentPos, SIZE_OF_FORMAT, &tensorDesc.m_format, SIZE_OF_FORMAT);
820 if (memRet != EOK) {
821 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s format.");
822 delete[] serializedData;
823 return OH_NN_MEMORY_ERROR;
824 }
825 currentPos += SIZE_OF_FORMAT;
826
827 memRet = memcpy_s(currentPos, SIZE_OF_TENSOR_TYPE, &tensorDesc.m_tensorType, SIZE_OF_TENSOR_TYPE);
828 if (memRet != EOK) {
829 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s tensor type.");
830 delete[] serializedData;
831 return OH_NN_MEMORY_ERROR;
832 }
833 currentPos += SIZE_OF_TENSOR_TYPE;
834
835 memRet = memcpy_s(currentPos, SIZE_OF_SHAPE_NUM, &tensorDesc.m_shapeNum, SIZE_OF_SHAPE_NUM);
836 if (memRet != EOK) {
837 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s shape num.");
838 delete[] serializedData;
839 return OH_NN_MEMORY_ERROR;
840 }
841 currentPos += SIZE_OF_SHAPE_NUM;
842
843 size_t sizeOfShape = tensorDesc.m_shapeNum * sizeof(int32_t);
844 memRet = memcpy_s(currentPos, sizeOfShape, tensorDesc.m_shape, sizeOfShape);
845 if (memRet != EOK) {
846 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s shape.");
847 delete[] serializedData;
848 return OH_NN_MEMORY_ERROR;
849 }
850 currentPos += sizeOfShape;
851
852 memRet = strcpy_s(currentPos, strlen(tensorDesc.m_name) + 1, tensorDesc.m_name);
853 if (memRet != EOK) {
854 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s name.");
855 delete[] serializedData;
856 return OH_NN_MEMORY_ERROR;
857 }
858 currentPos += strlen(tensorDesc.m_name) + 1;
859 }
860
861 buffer.data = serializedData;
862 buffer.length = totalSize;
863
864 return OH_NN_SUCCESS;
865 }
866
ReleaseDescShape(std::vector<SerializedTensorDesc> & immediateTensorDescs)867 void ReleaseDescShape(std::vector<SerializedTensorDesc>& immediateTensorDescs)
868 {
869 for (auto desc : immediateTensorDescs) {
870 delete[] desc.m_shape;
871 }
872 immediateTensorDescs.clear();
873 }
874
DeserializedTensorsFromBuffer(const Buffer & buffer,std::vector<std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType>> & tensorDescs)875 OH_NN_ReturnCode NNCompiler::DeserializedTensorsFromBuffer(
876 const Buffer& buffer, std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>>& tensorDescs)
877 {
878 std::vector<SerializedTensorDesc> immediateTensorDescs;
879 const char* ptr = static_cast<const char*>(buffer.data);
880 const char* end = ptr + buffer.length;
881 while (ptr < end) {
882 SerializedTensorDesc desc;
883
884 auto memRet = memcpy_s(&desc.m_dataType, SIZE_OF_DATATYPE, ptr, sizeof(desc.m_dataType));
885 if (memRet != EOK) {
886 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s data type.");
887 ReleaseDescShape(immediateTensorDescs);
888 return OH_NN_MEMORY_ERROR;
889 }
890 ptr += sizeof(desc.m_dataType);
891
892 memRet = memcpy_s(&desc.m_format, SIZE_OF_FORMAT, ptr, sizeof(desc.m_format));
893 if (memRet != EOK) {
894 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s format.");
895 ReleaseDescShape(immediateTensorDescs);
896 return OH_NN_MEMORY_ERROR;
897 }
898 ptr += sizeof(desc.m_format);
899
900 memRet = memcpy_s(&desc.m_tensorType, SIZE_OF_TENSOR_TYPE, ptr, sizeof(desc.m_tensorType));
901 if (memRet != EOK) {
902 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s tensor type.");
903 ReleaseDescShape(immediateTensorDescs);
904 return OH_NN_MEMORY_ERROR;
905 }
906 ptr += sizeof(desc.m_tensorType);
907
908 memRet = memcpy_s(&desc.m_shapeNum, SIZE_OF_SHAPE_NUM, ptr, sizeof(desc.m_shapeNum));
909 if (memRet != EOK) {
910 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s shape num.");
911 ReleaseDescShape(immediateTensorDescs);
912 return OH_NN_MEMORY_ERROR;
913 }
914 ptr += sizeof(desc.m_shapeNum);
915
916 desc.m_shape = new (std::nothrow) int32_t[desc.m_shapeNum];
917 if (desc.m_shape == nullptr) {
918 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to create shape buffer.");
919 ReleaseDescShape(immediateTensorDescs);
920 return OH_NN_NULL_PTR;
921 }
922 memRet = memcpy_s(desc.m_shape, desc.m_shapeNum * sizeof(int32_t), ptr, desc.m_shapeNum * sizeof(int32_t));
923 if (memRet != EOK) {
924 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s shape.");
925 ReleaseDescShape(immediateTensorDescs);
926 return OH_NN_MEMORY_ERROR;
927 }
928 ptr += desc.m_shapeNum * sizeof(int32_t);
929
930 desc.m_name = ptr;
931 ptr += std::strlen(desc.m_name) + 1; // +1 for null terminator
932
933 immediateTensorDescs.push_back(desc);
934 }
935
936 OH_NN_ReturnCode ret {OH_NN_SUCCESS};
937 for (const auto& immediateTensorDesc : immediateTensorDescs) {
938 std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType> tensorDescPair;
939 tensorDescPair.first = CreateSharedPtr<TensorDesc>();
940 if (tensorDescPair.first == nullptr) {
941 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to create tensor desc.");
942 tensorDescs.clear();
943 ReleaseDescShape(immediateTensorDescs);
944 return OH_NN_NULL_PTR;
945 }
946 ret = immediateTensorDesc.CopyToTensorDesc(*(tensorDescPair.first.get()));
947 if (ret != OH_NN_SUCCESS) {
948 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, error happened when copying "
949 "SerializedTensorDesc to TensorDesc.");
950 tensorDescs.clear();
951 ReleaseDescShape(immediateTensorDescs);
952 return ret;
953 }
954 tensorDescPair.second = immediateTensorDesc.m_tensorType;
955
956 tensorDescs.emplace_back(tensorDescPair);
957 }
958
959 ReleaseDescShape(immediateTensorDescs);
960 return ret;
961 }
962
963 } // NeuralNetworkRuntime
964 } // OHOS
965