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