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