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::__anonb0e221d20110::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::__anonb0e221d20110::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 size_t cacheNumber = caches.size();
535 if (cacheNumber == 0 || cacheNumber > NN_CACHE_FILE_NUMBER_MAX) {
536 LOGE("[NNCompiler] Caches size is equal 0 or greater than 100.");
537 return OH_NN_FAILED;
538 }
539
540 NNCompiledCache compiledCache;
541 ret = compiledCache.SetBackend(m_backendID);
542 if (ret != OH_NN_SUCCESS) {
543 LOGE("[NNCompiler] SaveToCacheFile failed, fail to set backend.");
544 return ret;
545 }
546
547 Buffer inputTensorDescBuffer;
548 ret = SerializeTensorsToBuffer(m_inputTensorDescs, inputTensorDescBuffer);
549 if (ret != OH_NN_SUCCESS) {
550 LOGE("[NNCompiler] SaveToCacheFile failed, error happened when serializing input tensor desc.");
551 return ret;
552 }
553 caches.emplace_back(inputTensorDescBuffer);
554 tensorBuffers.emplace_back(inputTensorDescBuffer);
555
556 Buffer outputTensorDescBuffer;
557 ret = SerializeTensorsToBuffer(m_outputTensorDescs, outputTensorDescBuffer);
558 if (ret != OH_NN_SUCCESS) {
559 LOGE("[NNCompiler] SaveToCacheFile failed, error happened when serializing output tensor desc.");
560 ReleaseBuffer(tensorBuffers);
561 return ret;
562 }
563 caches.emplace_back(outputTensorDescBuffer);
564 tensorBuffers.emplace_back(outputTensorDescBuffer);
565
566 compiledCache.SetModelName(m_extensionConfig.modelName);
567 ret = compiledCache.Save(caches, m_cachePath, m_cacheVersion);
568 if (ret != OH_NN_SUCCESS) {
569 LOGE("[NNCompiler] SaveToCacheFile failed, error happened when saving model cache.");
570 ReleaseBuffer(tensorBuffers);
571 return ret;
572 }
573
574 ReleaseBuffer(tensorBuffers);
575 LOGI("[NNCompiler] Export model cache successfully.");
576 return OH_NN_SUCCESS;
577 }
578
RestoreFromCacheFile()579 OH_NN_ReturnCode NNCompiler::RestoreFromCacheFile()
580 {
581 if (m_cachePath.empty()) {
582 LOGE("[NNCompiler] RestoreFromCacheFile failed, path is empty.");
583 return OH_NN_INVALID_PARAMETER;
584 }
585
586 if (m_cacheVersion == INVALID_CAHCE_VERSION) {
587 LOGE("[NNCompiler] RestoreFromCacheFile failed, cache version is invalid. Please set a valid cache version.");
588 return OH_NN_INVALID_PARAMETER;
589 }
590
591 if (m_preparedModel != nullptr) {
592 LOGE("[NNCompiler] RestoreFromCacheFile failed, m_preparedModel is not nullptr.");
593 return OH_NN_FAILED;
594 }
595
596 NNCompiledCache compiledCache;
597 OH_NN_ReturnCode ret = compiledCache.SetBackend(m_backendID);
598 if (ret != OH_NN_SUCCESS) {
599 LOGE("[NNCompiler] RestoreFromCacheFile failed, fail to set backend.");
600 return ret;
601 }
602
603 std::vector<Buffer> caches;
604 compiledCache.SetModelName(m_extensionConfig.modelName);
605 ret = compiledCache.Restore(m_cachePath, m_cacheVersion, caches);
606 if (ret != OH_NN_SUCCESS) {
607 LOGE("[NNCompiler] RestoreFromCacheFile failed, error happened when restoring model cache.");
608 ReleaseBufferByDevice(caches);
609 return ret;
610 }
611
612 size_t cacheNum = caches.size();
613 std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>> inputTensorDescs;
614 ret = DeserializedTensorsFromBuffer(caches[cacheNum - CACHE_INPUT_TENSORDESC_OFFSET], inputTensorDescs);
615 if (ret != OH_NN_SUCCESS) {
616 LOGE("[NNCompiler] RestoreFromCacheFile failed, error happened when deserializing input tensor desc.");
617 ReleaseBufferByDevice(caches);
618 return ret;
619 }
620
621 std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>> outputTensorDescs;
622 ret = DeserializedTensorsFromBuffer(caches[cacheNum - CACHE_OUTPUT_TENSORDESC_OFFSET], outputTensorDescs);
623 if (ret != OH_NN_SUCCESS) {
624 LOGE("[NNCompiler] RestoreFromCacheFile failed, error happened when deserializing output tensor desc.");
625 ReleaseBufferByDevice(caches);
626 return ret;
627 }
628
629 ModelConfig config;
630 config.enableFloat16 = m_enableFp16;
631 config.mode = m_performance;
632 config.priority = m_priority;
633 config.extensionConfig.isNpuFmShared = m_extensionConfig.isNpuFmShared;
634 std::vector<Buffer> modelOnlyCaches(caches.begin(), caches.end() - CACHE_INPUT_TENSORDESC_OFFSET);
635 bool isUpdatable = false;
636 ret = m_device->PrepareModelFromModelCache(modelOnlyCaches, config, m_preparedModel, isUpdatable);
637 if (ret != OH_NN_SUCCESS) {
638 LOGE("[NNCompiler] RestoreFromCacheFile failed, error happened when preparing model from cache.");
639 ReleaseBufferByDevice(caches);
640 return ret;
641 }
642
643 if (isUpdatable) {
644 LOGI("isUpdatable is true");
645
646 std::string currentVersion = CURRENT_VERSION;
647 char versionPath[PATH_MAX];
648 if (realpath(HIAI_VERSION_PATH.c_str(), versionPath) != nullptr) {
649 std::ifstream inf(versionPath);
650 if (inf.is_open()) {
651 getline(inf, currentVersion);
652 }
653 inf.close();
654 }
655 int currentOpVersion = std::stoi(currentVersion.substr(OPVERSION_SUBSTR_NUM));
656
657 NNCompiledCacheInfo modelCacheInfo;
658 std::string cacheInfoPath = m_cachePath + "/" + m_extensionConfig.modelName + "cache_info.nncache";
659 ret = compiledCache.CheckCacheInfo(modelCacheInfo, cacheInfoPath);
660 if (ret != OH_NN_SUCCESS) {
661 LOGE("[NNCompiledCache] isUpdatable is true to check cache info failed.");
662 return ret;
663 }
664
665 LOGI("isUpdatable currentOpVersion is: %{public}d", currentOpVersion);
666 LOGI("isUpdatable modelCacheInfo opVersion is %{public}d", static_cast<int>(modelCacheInfo.opVersion));
667
668 if (currentOpVersion > modelCacheInfo.opVersion) {
669 const size_t cacheNumber = caches.size();
670 uint32_t cacheSize = NUMBER_CACHE_INFO_MEMBERS + cacheNumber + 1;
671 uint32_t infoCharNumber = cacheSize * sizeof(int64_t);
672
673 std::unique_ptr<int64_t[]> cacheInfo = CreateUniquePtr<int64_t[]>(cacheSize);
674 if (cacheInfo == nullptr) {
675 LOGE("[NNCompiledCache] isUpdatable is true to create unique failed.");
676 return OH_NN_MEMORY_ERROR;
677 }
678
679 auto cacheInfoPtr = cacheInfo.get();
680 *cacheInfoPtr++ = modelCacheInfo.fileNumber;
681 *cacheInfoPtr++ = modelCacheInfo.version - 1;
682 *cacheInfoPtr++ = modelCacheInfo.deviceId;
683
684 for (size_t i = 0; i < modelCacheInfo.modelCheckSum.size(); ++i) {
685 *cacheInfoPtr++ = static_cast<int64_t>(modelCacheInfo.modelCheckSum[i]);
686 }
687
688 *cacheInfoPtr++ = currentOpVersion;
689
690 ret = compiledCache.WriteCacheInfo(infoCharNumber, cacheInfo, m_cachePath);
691 if (ret != OH_NN_SUCCESS) {
692 LOGE("[NNCompiledCache] isUpdatable is true to write cache info failed.");
693 return ret;
694 }
695 }
696 }
697
698 ReleaseBufferByDevice(caches);
699
700 m_inputTensorDescs = inputTensorDescs;
701 m_outputTensorDescs = outputTensorDescs;
702 LOGI("[NNCompiler] Restore model cache successfully.");
703 return OH_NN_SUCCESS;
704 }
705
SaveToCacheBuffer(const void * buffer,size_t length,size_t * modelSize) const706 OH_NN_ReturnCode NNCompiler::SaveToCacheBuffer(const void* buffer, size_t length, size_t* modelSize) const
707 {
708 LOGE("[NNCompiler] SaveToCacheBuffer is not supported currently.");
709 return OH_NN_UNSUPPORTED;
710 }
711
RestoreFromCacheBuffer(const void * buffer,size_t length)712 OH_NN_ReturnCode NNCompiler::RestoreFromCacheBuffer(const void* buffer, size_t length)
713 {
714 LOGE("[NNCompiler] RestoreFromCacheBuffer is not supported currently.");
715 return OH_NN_UNSUPPORTED;
716 }
717
SetExtensionConfig(const std::unordered_map<std::string,std::vector<char>> & configs)718 OH_NN_ReturnCode NNCompiler::SetExtensionConfig(const std::unordered_map<std::string, std::vector<char>>& configs)
719 {
720 if (configs.find(EXTENSION_KEY_MODEL_NAME) != configs.end()) {
721 std::vector<char> value = configs.at(EXTENSION_KEY_MODEL_NAME);
722 if (value.empty()) {
723 LOGE("[NNCompiler] SetExtensionConfig get empty model name from configs");
724 return OH_NN_INVALID_PARAMETER;
725 }
726 m_extensionConfig.modelName.assign(value.data(), value.data() + value.size());
727 LOGI("[NNCompiler] SetExtensionConfig get model name:%{public}s.", m_extensionConfig.modelName.c_str());
728 }
729 if (configs.find(EXTENSION_KEY_FM_SHARED) != configs.end()) {
730 m_extensionConfig.isNpuFmShared = true;
731 LOGI("[NNCompiler] SetExtensionConfig NpuFmShared enabled.");
732 }
733 return OH_NN_SUCCESS;
734 }
735
SetOptions(const std::vector<std::shared_ptr<void>> & options)736 OH_NN_ReturnCode NNCompiler::SetOptions(const std::vector<std::shared_ptr<void>>& options)
737 {
738 LOGE("[NNCompiler] SetOptions is not supported for NN compiler currently.");
739 return OH_NN_UNSUPPORTED;
740 }
741
GetModelName(std::string & modelName)742 OH_NN_ReturnCode NNCompiler::GetModelName(std::string& modelName)
743 {
744 modelName = m_extensionConfig.modelName;
745 return OH_NN_SUCCESS;
746 }
747
CreateExecutor()748 NNExecutor* NNCompiler::CreateExecutor()
749 {
750 if (m_device == nullptr) {
751 LOGE("[NNCompiler] CreateExecutor failed, m_device is nullptr");
752 return nullptr;
753 }
754
755 if (m_preparedModel == nullptr) {
756 LOGE("[NNCompiler] CreateExecutor failed, m_preparedModel is nullptr");
757 return nullptr;
758 }
759
760 if (m_inputTensorDescs.empty()) {
761 LOGE("[NNCompiler] CreateExecutor failed, m_inputTensorDescs is empty");
762 return nullptr;
763 }
764
765 if (m_outputTensorDescs.empty()) {
766 LOGE("[NNCompiler] CreateExecutor failed, m_outputTensorDescs is empty");
767 return nullptr;
768 }
769
770 NNExecutor* nnExecutor = new (std::nothrow) NNExecutor(
771 m_backendID, m_device, m_preparedModel, m_inputTensorDescs, m_outputTensorDescs);
772 if (nnExecutor == nullptr) {
773 LOGE("[NNCompiler] CreateExecutor failed, error happend when allocating NN Executor.");
774 return nullptr;
775 }
776
777 return nnExecutor;
778 }
779
SerializeTensorsToBuffer(const std::vector<std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType>> & tensorDescs,Buffer & buffer) const780 OH_NN_ReturnCode NNCompiler::SerializeTensorsToBuffer(
781 const std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>>& tensorDescs, Buffer& buffer) const
782 {
783 std::vector<SerializedTensorDesc> immediateTensorDescs;
784 OH_NN_ReturnCode ret = OH_NN_SUCCESS;
785 for (const auto& tensorDesc : tensorDescs) {
786 SerializedTensorDesc immediateTensorDesc;
787 ret = immediateTensorDesc.CopyFromTensorDesc(tensorDesc);
788 if (ret != OH_NN_SUCCESS) {
789 LOGE("[NNCompiler] SerializeInputsToBuffer failed, error happened when copying tensorDesc to "
790 "SerializedTensorDesc.");
791 immediateTensorDescs.clear();
792 return ret;
793 }
794 immediateTensorDescs.emplace_back(immediateTensorDesc);
795 }
796
797 size_t totalSize = 0;
798 for (const auto& tensorDesc : immediateTensorDescs) {
799 totalSize += SIZE_OF_DATATYPE;
800 totalSize += SIZE_OF_FORMAT;
801 totalSize += SIZE_OF_TENSOR_TYPE;
802 totalSize += SIZE_OF_SHAPE_NUM;
803 totalSize += tensorDesc.m_shapeNum * sizeof(int32_t);
804 totalSize += strlen(tensorDesc.m_name) + 1;
805 }
806
807 // Allocate memory for the serialized data
808 char* serializedData = new (std::nothrow) char[totalSize];
809 if (serializedData == nullptr) {
810 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to create serialized data.");
811 return OH_NN_NULL_PTR;
812 }
813 char* currentPos = serializedData;
814
815 // Serialize each tensor description
816 for (const auto& tensorDesc : immediateTensorDescs) {
817 auto memRet = memcpy_s(currentPos, SIZE_OF_DATATYPE, &tensorDesc.m_dataType, SIZE_OF_DATATYPE);
818 if (memRet != EOK) {
819 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s data type.");
820 delete[] serializedData;
821 return OH_NN_MEMORY_ERROR;
822 }
823 currentPos += SIZE_OF_DATATYPE;
824
825 memRet = memcpy_s(currentPos, SIZE_OF_FORMAT, &tensorDesc.m_format, SIZE_OF_FORMAT);
826 if (memRet != EOK) {
827 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s format.");
828 delete[] serializedData;
829 return OH_NN_MEMORY_ERROR;
830 }
831 currentPos += SIZE_OF_FORMAT;
832
833 memRet = memcpy_s(currentPos, SIZE_OF_TENSOR_TYPE, &tensorDesc.m_tensorType, SIZE_OF_TENSOR_TYPE);
834 if (memRet != EOK) {
835 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s tensor type.");
836 delete[] serializedData;
837 return OH_NN_MEMORY_ERROR;
838 }
839 currentPos += SIZE_OF_TENSOR_TYPE;
840
841 memRet = memcpy_s(currentPos, SIZE_OF_SHAPE_NUM, &tensorDesc.m_shapeNum, SIZE_OF_SHAPE_NUM);
842 if (memRet != EOK) {
843 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s shape num.");
844 delete[] serializedData;
845 return OH_NN_MEMORY_ERROR;
846 }
847 currentPos += SIZE_OF_SHAPE_NUM;
848
849 size_t sizeOfShape = tensorDesc.m_shapeNum * sizeof(int32_t);
850 memRet = memcpy_s(currentPos, sizeOfShape, tensorDesc.m_shape, sizeOfShape);
851 if (memRet != EOK) {
852 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s shape.");
853 delete[] serializedData;
854 return OH_NN_MEMORY_ERROR;
855 }
856 currentPos += sizeOfShape;
857
858 memRet = strcpy_s(currentPos, strlen(tensorDesc.m_name) + 1, tensorDesc.m_name);
859 if (memRet != EOK) {
860 LOGE("[NNCompiler] SerializeInputsToBuffer failed, failed to memcpy_s name.");
861 delete[] serializedData;
862 return OH_NN_MEMORY_ERROR;
863 }
864 currentPos += strlen(tensorDesc.m_name) + 1;
865 }
866
867 buffer.data = serializedData;
868 buffer.length = totalSize;
869
870 return OH_NN_SUCCESS;
871 }
872
ReleaseDescShape(std::vector<SerializedTensorDesc> & immediateTensorDescs)873 void ReleaseDescShape(std::vector<SerializedTensorDesc>& immediateTensorDescs)
874 {
875 for (auto desc : immediateTensorDescs) {
876 delete[] desc.m_shape;
877 }
878 immediateTensorDescs.clear();
879 }
880
DeserializedTensorsFromBuffer(const Buffer & buffer,std::vector<std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType>> & tensorDescs)881 OH_NN_ReturnCode NNCompiler::DeserializedTensorsFromBuffer(
882 const Buffer& buffer, std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>>& tensorDescs)
883 {
884 std::vector<SerializedTensorDesc> immediateTensorDescs;
885 const char* ptr = static_cast<const char*>(buffer.data);
886 const char* end = ptr + buffer.length;
887 while (ptr < end) {
888 SerializedTensorDesc desc;
889
890 auto memRet = memcpy_s(&desc.m_dataType, SIZE_OF_DATATYPE, ptr, sizeof(desc.m_dataType));
891 if (memRet != EOK) {
892 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s data type.");
893 ReleaseDescShape(immediateTensorDescs);
894 return OH_NN_MEMORY_ERROR;
895 }
896 ptr += sizeof(desc.m_dataType);
897
898 memRet = memcpy_s(&desc.m_format, SIZE_OF_FORMAT, ptr, sizeof(desc.m_format));
899 if (memRet != EOK) {
900 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s format.");
901 ReleaseDescShape(immediateTensorDescs);
902 return OH_NN_MEMORY_ERROR;
903 }
904 ptr += sizeof(desc.m_format);
905
906 memRet = memcpy_s(&desc.m_tensorType, SIZE_OF_TENSOR_TYPE, ptr, sizeof(desc.m_tensorType));
907 if (memRet != EOK) {
908 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s tensor type.");
909 ReleaseDescShape(immediateTensorDescs);
910 return OH_NN_MEMORY_ERROR;
911 }
912 ptr += sizeof(desc.m_tensorType);
913
914 memRet = memcpy_s(&desc.m_shapeNum, SIZE_OF_SHAPE_NUM, ptr, sizeof(desc.m_shapeNum));
915 if (memRet != EOK) {
916 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s shape num.");
917 ReleaseDescShape(immediateTensorDescs);
918 return OH_NN_MEMORY_ERROR;
919 }
920 ptr += sizeof(desc.m_shapeNum);
921
922 desc.m_shape = new (std::nothrow) int32_t[desc.m_shapeNum];
923 if (desc.m_shape == nullptr) {
924 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to create shape buffer.");
925 ReleaseDescShape(immediateTensorDescs);
926 return OH_NN_NULL_PTR;
927 }
928 memRet = memcpy_s(desc.m_shape, desc.m_shapeNum * sizeof(int32_t), ptr, desc.m_shapeNum * sizeof(int32_t));
929 if (memRet != EOK) {
930 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to memcpy_s shape.");
931 ReleaseDescShape(immediateTensorDescs);
932 return OH_NN_MEMORY_ERROR;
933 }
934 ptr += desc.m_shapeNum * sizeof(int32_t);
935
936 desc.m_name = ptr;
937 ptr += std::strlen(desc.m_name) + 1; // +1 for null terminator
938
939 immediateTensorDescs.push_back(desc);
940 }
941
942 OH_NN_ReturnCode ret {OH_NN_SUCCESS};
943 for (const auto& immediateTensorDesc : immediateTensorDescs) {
944 std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType> tensorDescPair;
945 tensorDescPair.first = CreateSharedPtr<TensorDesc>();
946 if (tensorDescPair.first == nullptr) {
947 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, failed to create tensor desc.");
948 tensorDescs.clear();
949 ReleaseDescShape(immediateTensorDescs);
950 return OH_NN_NULL_PTR;
951 }
952 ret = immediateTensorDesc.CopyToTensorDesc(*(tensorDescPair.first.get()));
953 if (ret != OH_NN_SUCCESS) {
954 LOGE("[NNCompiler] DeserializedTensorsFromBuffer failed, error happened when copying "
955 "SerializedTensorDesc to TensorDesc.");
956 tensorDescs.clear();
957 ReleaseDescShape(immediateTensorDescs);
958 return ret;
959 }
960 tensorDescPair.second = immediateTensorDesc.m_tensorType;
961
962 tensorDescs.emplace_back(tensorDescPair);
963 }
964
965 ReleaseDescShape(immediateTensorDescs);
966 return ret;
967 }
968
969 } // NeuralNetworkRuntime
970 } // OHOS
971