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