!1 Add Neural Network Runtime code

* add neural network runtime
This commit is contained in:
yangyongjie
2022-10-28 02:32:29 +00:00
parent b63446ba2e
commit 7f4a0afc68
315 changed files with 56883 additions and 101 deletions
+20
View File
@@ -0,0 +1,20 @@
# ignore .a .o
*.[ao]
*.so
*.ms
# ignore build
build/
libs/
obj/
_ignore/
# ignore schema
jni/include/schema/
# ignore vscode
.vscode
# ignore runtime file
deploy.bat
log.txt
+28
View File
@@ -0,0 +1,28 @@
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/ohos.gni")
group("nnrt_target") {
deps = [
"frameworks:libneural_network_runtime"
]
}
group("nnrt_test_target") {
testonly = true
deps = [
"test/unittest:unittest",
"test/system_test:system_test"
]
}
+1 -25
View File
@@ -1,3 +1,4 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -174,28 +175,3 @@
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+34
View File
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2022 Huawei Device Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This is the configuration file template for OpenHarmony OSS Audit Tool, please copy it to your project root dir and modify it refer to OpenHarmony/tools_oat/README.
-->
<configuration>
<oatconfig>
<filefilterlist>
<filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies">
<filteritem type="filename" name="neural_network_runtime_intro.png" desc="自研图片"/>
<filteritem type="filename" name="neural_network_runtime_add_op_model.png" desc="自研图片"/>
<filteritem type="filename" name="arch_diagram.png" desc="自研图片"/>
<filteritem type="filename" name="dev_flow.png" desc="自研图片"/>
<filteritem type="filename" name="Flowchart.png" desc="自研图片"/>
<filteritem type="filename" name="Principle.png" desc="自研图片"/>
</filefilter>
</filefilterlist>
</oatconfig>
</configuration>
-36
View File
@@ -1,36 +0,0 @@
# neural_network_runtime
#### Description
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
-39
View File
@@ -1,39 +0,0 @@
# neural_network_runtime
#### 介绍
{**以下是 Gitee 平台说明,您可以替换此简介**
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
#### 软件架构
软件架构说明
#### 安装教程
1. xxxx
2. xxxx
3. xxxx
#### 使用说明
1. xxxx
2. xxxx
3. xxxx
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+26
View File
@@ -0,0 +1,26 @@
# Neural Network Runtime
Neural Network Runtime(神经网络运行时)是一套面向AI领域的运行时部件,适配上层AI推理引擎和底层加速芯片,为端侧AI推理引擎提供硬件加速的计算能力。
## 基本概念
在开发前,需要先了解以下概念,以便更好地理解全文内容:
- Native APIOpenharmony 面向应用开发者的C语言接口。
- HDIHardware Device Interface,硬件设备接口,是OpenHarmony中系统组件与芯片组件通信的接口。关于更多HDI的细节,请浏览[驱动子系统](https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/%E9%A9%B1%E5%8A%A8%E5%AD%90%E7%B3%BB%E7%BB%9F.md)。
## 运作机制
**图1** Neural Network Runtime架构图
!["Neural Network Runtime架构图"](neural_network_runtime_intro.png)
如图1所示,在OpenHarmony系统上,AI应用通常要经过AI推理引擎和Neural Network Runtime才能对接底层芯片驱动,进而加速推理计算。Neural Network Runtime和芯片驱动直接通过HDI接口交互,Neural Network Runtime将模型和数据传递给芯片驱动,通过HDI接口在加速芯片上执行推理计算,计算结果通过Neural Network Runtime、AI推理引擎逐层返回至AI应用。
通常,AI应用、AI推理引擎、Neural Network Runtime处在同一个进程下,芯片驱动运行在另一个进程下,两者之间需要借助进程间通信(IPC)传递模型和计算数据。Neural Network Runtime根据HDI接口实现了HDI客户端,相应的,芯片厂商需要根据HDI接口实现并开放HDI服务。
架构图中每层功能简单阐述如下:
- AI应用:借助AI模型,提供丰富的应用能力,如:图像分类、人脸识别、文字识别等。
- AI推理引擎:为AI应用提供模型搭建、模型优化、推理计算的能力。
- Neural Network Runtime:作为AI推理引擎和底层加速芯片的桥梁,它开放了标准统一的HDI接口,不同的芯片都可以通过HDI接口接入Neural Network Runtime。
- HDI服务端:HDI服务端接收Neural Network Runtime传入的模型,将模型转换为加速芯片驱动所使用模型格式,并调用芯片驱动的接口执行计算。
- 加速芯片:加速芯片通常能够加速AI模型或者模型中部分算子的计算,提供优于CPU的性能。
+50
View File
@@ -0,0 +1,50 @@
{
"name": "@ohos/neural_network_runtime",
"description": "The Neural Network Runtime that bridges the inference framework and the device accelerator.",
"version": "3.2",
"license": "MIT",
"publishAs": "code-segment",
"segment": {
"destPath": "foundation/ai/neural_network_runtime"
},
"dirs": {},
"scripts": {},
"licensePath": "COPYING",
"readmePath": {
"en": "README.rst"
},
"component": {
"name": "neural_network_runtime",
"subsystem": "ai",
"syscap": [],
"features": [],
"adapted_system_type": ["standard"],
"rom": "1024KB",
"ram": "2048KB",
"deps": {
"components": [
"hilog"
],
"third_party": []
},
"build": {
"sub_component": [
"//foundation/ai/neural_network_runtime:nnrt_target"
],
"inner_kits": [
{"type": "so",
"name": "//foundation/ai/neural_network_runtime:nnrt_target",
"header": {
"header_files": [
"neural_network_runtime_inner.h"
],
"header_base":"//foundation/ai/neural_network_runtime/interfaces/innerkits/c"
}
}
],
"test": [
"//foundation/ai/neural_network_runtime:nnrt_test_target"
]
}
}
}
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_LOG_H
#define NEURAL_NETWORK_RUNTIME_LOG_H
#include <stdarg.h>
#include <stdbool.h>
#include "hilog/log_c.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef NNR_LOG_DOMAIN
#define NNR_LOG_DOMAIN 0xD002101
#endif
#define LOGD(...) HiLogPrint(LOG_CORE, LOG_DEBUG, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
#define LOGI(...) HiLogPrint(LOG_CORE, LOG_INFO, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
#define LOGW(...) HiLogPrint(LOG_CORE, LOG_WARN, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
#define LOGE(...) HiLogPrint(LOG_CORE, LOG_ERROR, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
#define LOGF(...) HiLogPrint(LOG_CORE, LOG_FATAL, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif // NEURAL_NETWORK_RUNTIME_LOG_H
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_SCOPED_TRACE_H
#define NEURAL_NETWORK_RUNTIME_SCOPED_TRACE_H
#include <string>
#include "hitrace/trace.h"
#define NNRT_TRACE_NAME(name) ScopedTrace ___tracer(name)
namespace OHOS {
namespace NeuralNetworkRuntime {
class ScopedTrace {
public:
inline ScopedTrace(const std::string& name)
{
m_name = name;
HiviewDFX::HiTraceId traceId = HiviewDFX::HiTraceChain::GetId();
if (traceId.IsValid()) {
HiviewDFX::HiTraceChain::Tracepoint(HITRACE_TP_GENERAL, traceId, "NNRt Trace start: %s", name.c_str());
}
}
inline ~ScopedTrace()
{
HiviewDFX::HiTraceId traceId = HiviewDFX::HiTraceChain::GetId();
if (traceId.IsValid()) {
HiviewDFX::HiTraceChain::Tracepoint(HITRACE_TP_GENERAL, traceId, "NNRt Trace end: %s", m_name.c_str());
}
}
private:
std::string m_name {};
};
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_SCOPED_TRACE_H
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_UTILS_H
#define NEURAL_NETWORK_RUNTIME_UTILS_H
#include <memory>
#include "log.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
template<typename T, typename... Args>
std::shared_ptr<T> CreateSharedPtr(Args&&... args)
{
std::shared_ptr<T> tPtr = nullptr;
try {
tPtr = std::make_shared<T>(args...);
} catch (const std::bad_alloc& except) {
LOGW("Create a new shared pointer failed. Error: %s", except.what());
return nullptr;
}
return tPtr;
}
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_UTILS_H
@@ -0,0 +1,29 @@
#
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# CMake lowest version requirement
cmake_minimum_required(VERSION 3.16.5)
# project information
project(label_classification)
set(CMAKE_C_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2")
# If the CPU architecture is 32-bit, -march=armv7-a
set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv7-a")
set(LOCAL_DIRECTORY_PATH ${PROJECT_SOURCE_DIR})
add_subdirectory(${LOCAL_DIRECTORY_PATH}/tflite)
Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -0,0 +1,218 @@
# Tensorflow Lite 接入NNRt Delegate Demo开发指南
## 概述
### 功能简介
- 神经网络运行时部件(NNRt)是跨设备的AI运行时框架,作为端侧推理框架和专用加速芯片的中间桥梁,为端侧推理框架提供了统一的Native接口;
- 本demo旨在介绍上层AI业务如何利用NNRt在专有芯片上加速推理,使能OpenHarmony社区生态;
- 本demo根据用户输入参数(模型、标签、模型输入shape、循环浮点推理次数、是否允许动态尺寸推理、以及是否打印结果等)完成标签分类模型推理,用户可通过打印信息观察在不同条件下的模型推理性能、精度等KIP。
### 基本概念
在开发前,开发者需要先了解以下概念,以便更好地理解全文内容:
- NNRt: Neural Network Runtime,神经网络运行时,是本指导主要介绍的部件。
- OHOSOpenHarmony Operating System,开源鸿蒙操作系统。
### 约束与限制
- 系统版本:OpenHarmonyOS 3.2及以上
- 开发环境:Ubuntu 18.04及以上
- 接入设备:OpenHarmony定义的标准设备
- 其他开发依赖:
- tensorflow-lite.so及其依赖库,目前完成在tensorflow lite 2.6版本上的测试;
- NNRt库libneural_network_runtime.z.so;
- TensorFlow Lite头文件:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite;
- mobilenetv2.tflite模型(https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz);
- 标签文件labels.txt;
- 测试图片grace_hopper.bmp;
### 运作机制
<div align=center><img src="./Flowchart.png"></div>
- 用户调用TFLite的BuildFromFile接口完成初始构图;
- 用户设置自定义的参数options,应用于创建NnrtDelegate
- 用户创建DelegateProviders,并调用DelegateProviders的CreateAllRankedDelegates接口创建NnrtDelegate,创建NnrtDelegate过程中dlopen打开NNRt的动态库,并加载API,返回delegate
- 用户调用ModifyGraphWithDelegate接口完成Node替换,其中该步分四个步骤;
- Initalize初始化NnrtDelegate;
- 判断图中各node是否支持NnrtDelegate,返回支持的node集合;
- 调用TFLiteRegistration注册NnrtDelegate,并初始化init, prepare, invoke成员函数指针,指向delegateKernel的Init, Prepare和run函数方法;
- 替换TensorFlow Delegate的node为已注册的NNrt delegate kernel, 并调用Init完成构图步骤;
- 用户调用AllocateTensors,完成内存分配和图编译,其中支持delegate的node会转到delegateKernel的prepare完成编译,不支持delegate的会调用原有tflite node的prepare编译;
- 用户调用Invoke完成图执行;
### 开发流程
<div align=center><img src="./Principle.png"></div>
### 开发步骤
本节主要描述NNRt接入TFLite的TFLite-delegate代理机制,重点对TFLite调用delegate的流程和delegate对接NNRt的方式进行了介绍。
TensorFlow Lite Delegate有两个基类DelegateProvider、TfLiteDelegate, 本节主要描述继承这两个基类得到子类NnrtDelegate和NnrtDelegateProvider。
本demo主要文件目录结构如下图:
```text
.
├── CMakeLists.txt
├── delegates
│   └── nnrt_delegate
│   ├── CMakeLists.txt
│   ├── nnrt_delegate.cpp
│   ├── nnrt_delegate.h
│   ├── nnrt_delegate_kernel.cpp
│   ├── nnrt_delegate_kernel.h
│   ├── nnrt_delegate_provider.cpp
│   ├── nnrt_op_builder.cpp
│   ├── nnrt_op_builder.h
│   ├── nnrt_utils.cpp
│   ├── nnrt_utils.h
│   └── tensor_mapping.h
├── label_classify
│   ├── CMakeLists.txt
│   ├── label_classify.cpp
│   └── label_classify.h
├── nnrt
│   ├── CMakeLists.txt
│   ├── nnrt_implementation.cpp
│   └── nnrt_implementation.h
└── tools
├── bitmap_helpers.cpp
├── bitmap_helpers.h
├── get_topn.h
├── log.h
├── utils.cpp
└── utils.h
```
1. 创建Tensorflow Lite NnrtDelegate类
- Tensorflow Lite NNRt Delegate 使TensorFlow Lite模型能够运行在NNRt框架(https://gitee.com/openharmony/neural_network_runtime)上,这导致了在OHOS设备上更快的模型推理
- nnrt_delegate依赖nnrt_delegate_kernel, nnrt_delegate_kernel(将支持替换的TensorFlow Lite模型中的operators替换成Nnrt中的operators)依赖nnrt_op_builder(给每个operators设置输入输出tensor和operation属性),完成nnrt_delegate的自定义。
2. 创建NnrtDelegateProvider
- NnrtDelegateProvider依赖nnrt_implementation(用于加载libneural_network_runtime.z.so中的Api)和nnrt_delegate(用于创建子类NnrtDelegate对象),完成与TFLite的对接;
- 注册NnrtDelegateProvider
```cpp
REGISTER_DELEGATE_PROVIDER(NnrtDelegateProvider);
```
- 创建CreateTfLiteDelegate主要有以下几步
```cpp
NnrtDelegate::Options options;
const auto* nnrtImpl = NnrtImplementation();
if (!nnrtImpl->nnrtExists) {
TFLITE_LOG(WARN) << "NNRT acceleration is unsupported on this platform.";
return delegate;
}
Interpreter::TfLiteDelegatePtr TfLiteDelegatePtr(new (std::nothrow) NnrtDelegate(nnrtImpl, options),
[](TfLiteDelegate* delegate) { delete reinterpret_cast<NnrtDelegate*>(delegate); });
```
3. label_classify.cpp中加载Nnrt_Delegate
```cpp
interpreter->ModifyGraphWithDelegate(std::move(delegate.delegate))
```
### 调测命令
1. 编译生成Tensorflow Lite库及其依赖库
请参考Tensorflow Lite交叉编译指南(https://www.tensorflow.org/lite/guide/build_cmake_arm, 同时在```tensorflow/lite/CMakeLists.txt```中增加以下内容:
```text
# TODO: TFLite External Delegate
list(APPEND TFLITE_EXTERNAL_DELEGATE_SRC
${TFLITE_SOURCE_DIR}/tools/delegates/delegate_provider.cc
# ${TFLITE_SOURCE_DIR}/tools/delegates/external_delegate_provider.cc
${TFLITE_SOURCE_DIR}/tools/tool_params.cc
${TFLITE_SOURCE_DIR}/tools/command_line_flags.cc
)
```
```text
target_link_libraries(tensorflow-lite
PUBLIC
Eigen3::Eigen
NEON_2_SSE
absl::flags
absl::hash
absl::status
absl::strings
absl::synchronization
absl::variant
farmhash
fft2d_fftsg2d
flatbuffers
gemmlowp
ruy
${CMAKE_DL_LIBS}
${TFLITE_TARGET_DEPENDENCIES}
)
```
2. 编译生成NNRt库libneural_network_runtime.z.so
请参考编译指导(https://gitee.com/openharmony/build),编译命令如下
```shell
./build.sh --product-name rk3568 ccache --jobs=16 --build-target=neural_network_runtime
```
3. 用cmake编译北向demo
- 将TensorFlow Lite头文件(https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite)和编译生成的TensorFlow Lite库,分别放在```deep_learning_framework/lib_3rd_nnrt_tflite/include/tensorflow/lite/```和```deep_learning_framework/lib_3rd_nnrt_tflite/com/arm64-v8a/lib/```下;
- 指定ohos的cmake, ohos.toolchain.cmake路径,在```foundation/ai/neural_network_runtime/example/cmake_build/build_ohos_tflite.sh```中替换以下两行;
```shell
./tool_chain/native/build-tools/cmake/bin/cmake \
-DCMAKE_TOOLCHAIN_FILE=./tool_chain/native/cmake_build/cmake/ohos.toolchain.cmake \
```
- 进入```foundation/ai/neural_network_runtime/example/cmake_build```:
- 如果需要在arm32架构的CPU上运行:
- 修改```tflite/CMakeLists.txt```
```text
set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv7-a")
```
- 执行编译命令
```shell
bash build_ohos_tflite.sh armeabi-v7a
```
- 如果需要在arm64架构的CPU上运行:
- 修改```tflite/CMakeLists.txt```
```text
set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv8-a")
```
- 执行编译命令
```shell
bash build_ohos_tflite.sh arm64-v8a
```
- 在```example/deep_learning_framework/```目录下创建lib和output两个文件夹:
```shell
mkdir lib output
```
- 进入```foundation/ai/neural_network_runtime/example/cmake_build```, 执行链接命令:
```shell
make
```
- 北向demo成功编译完成后会在```deep_learning_framework/lib```生成libnnrt_delegate.so和libnnrt_implementation.so, 在```deep_learning_framework/output```下生成label_classify可执行文件,目录结构体如下所示。
```text
deep_learning_framework
├── lib
│   ├── libnnrt_delegate.so # 生成的TensorFlow Lite nnrt delegate库
│   └── libnnrt_implementation.so # 生成的nnrt在TensorFlow Lite中接口实现库
└── output
└── label_classify # 生成的可执行文件
```
4. 在开发板上运行北向demo
- 将步骤1生成的libnnrt_implementation.so, libnnrt_delegate.so和可执行文件label_classify, libneural_network_runtime.z.so, tensorflow-lite.so及其依赖的库, mobilenetv2.tflite模型, 标签labels.txt, 测试图片grace_hopper.bmp推送到开发板上:
```shell
# 假设上述待推送文件均放在push_files/文件夹下
hdc_std file send push_files/ /data/demo/
```
- 进入开发板,执行demo前需要添加环境变量,文件执行权限等:
```shell
# 进入开发板
hdc_std shell
# 进入推送文件目录,并增加可执行文件权限
cd /data/demo
chmod +x ./label_classify
# 添加环境变量
export LD_LIBRARY_PATH=/data/demo:$LD_LIBRARY_PATH
# 执行demo-m tflite模型, -i 测试图片, -l 数据标签, -a 1表示使用nnrt, 0表示不使用nnrt推理,-z 1 表示打印输出张量大小的结果
./label_classify -m mobilenetv2.tflite -i grace_hopper.bmp -l labels.txt -a 1 -z 1
```
### 开发实例
完整[Demo实例](xxx, Demo暂时还在黄区代码仓,超链接需等Demo开源后补充)可以参考社区实现。
@@ -0,0 +1,51 @@
#!/bin/bash
#
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
set -e
function help_info() {
echo "arm64-v8a(armeabi-v7a) means the CPU architecture is 64-bit(32-bit), the compile command like the following:"
echo "bash build_ohos_tflite.sh arm64-v8a"
}
function build() {
echo "$1"
./tool_chain/native/build-tools/cmake/bin/cmake \
-DCMAKE_TOOLCHAIN_FILE=./tool_chain/native/build/cmake/ohos.toolchain.cmake \
-DOHOS_ARCH=$1 \
-DOHOS_PLATFORM=OHOS \
-DCMAKE_BUILD_TYPE=RELEASE \
-DBUILD_SHARED_LIBS=true \
-DOHOS_STL=c++_static \
-DCMAKE_BUILD_TYPE=Debug \
..
}
if [ "$#" != 1 ]; then
echo "Incorrect command, please pass the correct number of parameters to the compile command."
help_info
exit 1;
fi
if [ "$1" == "arm64-v8a" ]; then
build arm64-v8a
elif [ "$1" == "armeabi-v7a" ]; then
build armeabi-v7a
else
echo "Incorrect CPU architecture parameter or missing setting it, please pass the correct compile command."
help_info
fi
@@ -0,0 +1,25 @@
#
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
set(NNRT_INTERFACE_HOME ${LOCAL_DIRECTORY_PATH}/tflite/nnrt)
set(NNRT_DELEGATE_HOME ${LOCAL_DIRECTORY_PATH}/tflite/delegates/nnrt_delegate)
set(NNRT_DEMO_HOME ${LOCAL_DIRECTORY_PATH}/tflite/label_classify)
set(TFLITE_LIB_PATH ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite)
add_subdirectory(${NNRT_INTERFACE_HOME})
add_subdirectory(${NNRT_DELEGATE_HOME})
add_subdirectory(${NNRT_DEMO_HOME})
@@ -0,0 +1,34 @@
#
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
set(TFLITE_PATH ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite)
LINK_DIRECTORIES(${TFLITE_PATH}/com/arm64-v8a/lib/)
# Header path
set(OHOS_INC ${LOCAL_DIRECTORY_PATH}/../../interfaces/kits/c)
set(TOOLS_INC ${LOCAL_DIRECTORY_PATH}/tflite/tools)
set(TFLITE_INC ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite/include)
include_directories(${LOCAL_DIRECTORY_PATH} ${NNRT_DELEGATE_HOME} ${TFLITE_INC} ${OHOS_INC} ${TOOLS_INC})
# Scr path
file(GLOB NNRT_DELEGATE_SRCS "${NNRT_DELEGATE_HOME}/*.cpp")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LOCAL_DIRECTORY_PATH}/lib)
add_library(nnrt_delegate SHARED ${NNRT_DELEGATE_SRCS})
target_link_libraries(nnrt_delegate -ltensorflow-lite nnrt_implementation)
@@ -0,0 +1,316 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nnrt_delegate.h"
#include "tensorflow/lite/util.h"
#include "tensorflow/lite/context_util.h"
#include "tensorflow/lite/minimal_logging.h"
#include "nnrt_utils.h"
#include "nnrt_delegate_kernel.h"
namespace tflite {
const char* g_tfliteNnrtDelegateName = "TfLiteNnrtDelegate";
constexpr int32_t TFLITE_NNRT_DELEGATE_VERSION = 1;
NnrtDelegate::Data::Data(const NnrtApi* nnrt) : nnrt(nnrt) {}
NnrtDelegate::Data::~Data() {}
void NnrtDelegate::NnrtDelegateConstructorImpl(const Options& options)
{
m_delegateData.acceleratorName = options.acceleratorName;
m_delegateData.cacheDir = options.cacheDir;
m_delegateData.modelToken = options.modelToken;
m_delegateData.enableFp16 = options.enableFp16;
m_delegateData.executionPriority = options.executionPriority;
m_delegateData.executionPerformance = options.executionPerformance;
m_delegateData.allowDynamicDimensions = options.allowDynamicDimensions;
m_delegateData.maxNumberDelegatedPartitions = options.maxNumberDelegatedPartitions;
m_delegateData.maxCompilationTimeoutDurationNs = options.maxCompilationTimeoutDurationNs;
m_delegateData.maxExecutionTimeoutDurationNs = options.maxExecutionTimeoutDurationNs;
m_delegateData.maxExecutionLoopTimeoutDurationNs = options.maxExecutionLoopTimeoutDurationNs;
Prepare = DoPrepare;
CopyFromBufferHandle = DoCopyFromBufferHandle;
CopyToBufferHandle = DoCopyToBufferHandle;
FreeBufferHandle = DoFreeBufferHandle;
data_ = &m_delegateData;
// NNRT support dynamic shape feature.
flags |= kTfLiteDelegateFlagsAllowDynamicTensors;
flags |= kTfLiteDelegateFlagsRequirePropagatedShapes;
}
NnrtDelegate::NnrtDelegate(const NnrtApi* nnrt) : NnrtDelegate(nnrt, Options()) {}
NnrtDelegate::NnrtDelegate(const Options& options) : NnrtDelegate(NnrtImplementation(), options) {}
NnrtDelegate::NnrtDelegate(const NnrtApi* nnrt, const Options& options)
: TfLiteDelegate(TfLiteDelegateCreate()), m_delegateData(nnrt)
{
NnrtDelegateConstructorImpl(options);
}
NnrtDelegate::NnrtDelegate() : NnrtDelegate(Options()) {}
TfLiteStatus NnrtDelegate::GetOptions(const TfLiteDelegate* pDelegate, Options& options)
{
// Caller guarantees that parameters are legal
auto pDelegateData = static_cast<Data*>(pDelegate->data_);
options.acceleratorName = pDelegateData->acceleratorName;
options.cacheDir = pDelegateData->cacheDir;
options.modelToken = pDelegateData->modelToken;
options.enableFp16 = pDelegateData->enableFp16;
options.executionPriority = pDelegateData->executionPriority;
options.executionPerformance = pDelegateData->executionPerformance;
options.allowDynamicDimensions = pDelegateData->allowDynamicDimensions;
options.maxNumberDelegatedPartitions = pDelegateData->maxNumberDelegatedPartitions;
options.maxCompilationTimeoutDurationNs = pDelegateData->maxCompilationTimeoutDurationNs;
options.maxExecutionTimeoutDurationNs = pDelegateData->maxExecutionTimeoutDurationNs;
options.maxExecutionLoopTimeoutDurationNs = pDelegateData->maxExecutionLoopTimeoutDurationNs;
options.version = pDelegateData->version;
return kTfLiteOk;
}
TfLiteStatus NnrtDelegate::DoCopyFromBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate, TfLiteBufferHandle bufferHandle, TfLiteTensor* tensor)
{
return kTfLiteError;
}
TfLiteStatus NnrtDelegate::DoCopyToBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate, TfLiteBufferHandle bufferHandle, TfLiteTensor* tensor)
{
return kTfLiteError;
}
void NnrtDelegate::DoFreeBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate, TfLiteBufferHandle* handle)
{
return;
}
TfLiteStatus NnrtDelegate::LimitDelegatedPartitions(int32_t maxPartitions,
std::vector<TfLiteDelegateParams> partitionParamsArray, std::vector<int32_t>& nodesToDelegate)
{
int32_t numPartitions = partitionParamsArray.size();
if ((maxPartitions <= 0) || (numPartitions <= maxPartitions)) { // no limit or not exceed limit
return kTfLiteOk;
}
int32_t numberDelegatedPartitions = std::count_if(
partitionParamsArray.begin(), partitionParamsArray.end(),
[nodesToDelegate](const TfLiteDelegateParams& partitionParams) {
return std::find(nodesToDelegate.begin(), nodesToDelegate.end(),
partitionParams.nodes_to_replace->data[0]) != nodesToDelegate.end();
});
// Adapt maxPartitions to limit delegate paritions, sort and abandon the low-ranking nodes.
if (numberDelegatedPartitions > maxPartitions) {
std::sort(partitionParamsArray.begin(), partitionParamsArray.end(),
[](const TfLiteDelegateParams& left, const TfLiteDelegateParams& right) -> bool {
return left.nodes_to_replace->size > right.nodes_to_replace->size;
});
nodesToDelegate.clear();
for (int32_t i = 0; i < maxPartitions; ++i) {
const TfLiteDelegateParams& partitionParams = partitionParamsArray[i];
nodesToDelegate.insert(nodesToDelegate.end(),
partitionParams.nodes_to_replace->data,
partitionParams.nodes_to_replace->data +
partitionParams.nodes_to_replace->size);
}
}
return kTfLiteOk;
}
TfLiteStatus NnrtDelegate::GetSupportedNodes(TfLiteContext* context,
TfLiteDelegate* delegate, std::vector<int32_t>& supportedNodes)
{
// Caller guarantees that parameters are legal
TfLiteIntArray* executionPlan = nullptr;
TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &executionPlan));
TF_LITE_ENSURE_EQ(context, executionPlan != nullptr, true);
// Check for every node if it is supported
TfLiteNode* node = nullptr;
TfLiteRegistration* registration = nullptr;
for (auto nodeIndex : TfLiteIntArrayView(executionPlan)) {
node = nullptr;
registration = nullptr;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(context, nodeIndex, &node, &registration));
if (NnrtDelegateKernel::Validate(registration->builtin_code)) {
supportedNodes.emplace_back(nodeIndex);
} else {
TFLITE_LOG_PROD(TFLITE_LOG_WARNING,
"[NNRT-DELEGATE] Get unsupportted node: %d.", registration->builtin_code);
}
}
return kTfLiteOk;
}
void NnrtDelegate::GetDelegateKernelRegistration(TfLiteDelegate* delegate, TfLiteRegistration& nnrtDelegateKernel)
{
// Caller guarantees that parameters are legal
nnrtDelegateKernel.profiling_string = nullptr;
nnrtDelegateKernel.builtin_code = kTfLiteBuiltinDelegate;
nnrtDelegateKernel.custom_name = g_tfliteNnrtDelegateName;
nnrtDelegateKernel.version = TFLITE_NNRT_DELEGATE_VERSION;
nnrtDelegateKernel.init = [](TfLiteContext* context, const char* buffer, size_t length) -> void* {
if (buffer == nullptr) {
return nullptr;
}
const TfLiteDelegateParams* params = reinterpret_cast<const TfLiteDelegateParams*>(buffer);
auto* delegateData = static_cast<Data*>(params->delegate->data_);
NnrtDelegateKernel* state = new (std::nothrow) NnrtDelegateKernel(delegateData->nnrt);
if (state == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to create NnrtDelegateKernel instance.");
return state;
}
TfLiteStatus status = state->Init(context, params);
if (status != kTfLiteOk) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to init NnrtDelegateKernel.");
delete state;
state = nullptr;
}
return state;
};
nnrtDelegateKernel.free = [](TfLiteContext* context, void* buffer) -> void {
if (buffer != nullptr) {
delete static_cast<NnrtDelegateKernel*>(buffer);
buffer = nullptr;
}
};
nnrtDelegateKernel.prepare = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
if (node == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to prepare delegate kernels, the node is nullptr.");
return kTfLiteError;
}
NnrtDelegateKernel* state = reinterpret_cast<NnrtDelegateKernel*>(node->user_data);
return state->Prepare(context, node);
};
nnrtDelegateKernel.invoke = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
if (node == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to invoke delegate kernels, the node is nullptr.");
return kTfLiteError;
}
NnrtDelegateKernel* state = reinterpret_cast<NnrtDelegateKernel*>(node->user_data);
return state->Invoke(context, node);
};
}
TfLiteStatus NnrtDelegate::CheckDeviceValid(TfLiteContext* context, TfLiteDelegate* delegate)
{
// Caller guarantees that parameters are legal
auto* delegateData = static_cast<Data*>(delegate->data_);
if (delegateData == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Delegate data not be found.");
return kTfLiteDelegateDataNotFound;
}
const NnrtApi* nnrt = delegateData->nnrt;
if (nnrt == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to get nnrt instance.");
return kTfLiteError;
}
NnrtDelegate::Options delegateOptions;
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(delegate, delegateOptions));
if (tflite::IsUseTargetDevice(delegateOptions)) {
size_t nnrtDevice;
TF_LITE_ENSURE_STATUS(GetTargetDevice(context, delegate, nnrt, nnrtDevice));
}
return kTfLiteOk;
}
TfLiteStatus NnrtDelegate::DoPrepare(TfLiteContext* context, TfLiteDelegate* delegate)
{
if ((context == nullptr) || (delegate == nullptr)) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE] Input TFLite-Context or TFLite-Delegate is nullptr.");
return kTfLiteError;
}
auto* delegateData = static_cast<Data*>(delegate->data_);
const NnrtApi* nnrt = delegateData->nnrt;
// Do not delegate nodes_ if NN API is unavailable.
if (!nnrt->nnrtExists) {
return kTfLiteOk;
}
// Check devices validity
TF_LITE_ENSURE_STATUS(CheckDeviceValid(context, delegate));
// Get supportted nodes by tflite.
// We don't care about all nodes_, we only care about ones in the current plan.
std::vector<int32_t> supportedNodes;
GetSupportedNodes(context, delegate, supportedNodes);
// If there are no delegated nodes, short-circuit node replacement.
if (supportedNodes.empty()) {
TFLITE_LOG_PROD(TFLITE_LOG_INFO, "[NNRT-DELEGATE] supportted node list is empty.");
return kTfLiteOk;
}
static TfLiteRegistration nnrtDelegateKernel;
GetDelegateKernelRegistration(delegate, nnrtDelegateKernel);
std::vector<int32_t> nodesToDelegate(supportedNodes);
int32_t numPartitions;
TfLiteDelegateParams* paramsArray = nullptr;
auto supportedNodesArray = BuildTfLiteIntArray(supportedNodes);
TF_LITE_ENSURE_STATUS(context->PreviewDelegatePartitioning(
context, supportedNodesArray.get(), &paramsArray, &numPartitions));
NnrtDelegate::Options delegateOptions;
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(delegate, delegateOptions));
const auto partitionParamsArray = std::vector<TfLiteDelegateParams>(paramsArray, paramsArray + numPartitions);
TF_LITE_ENSURE_STATUS(LimitDelegatedPartitions(
delegateOptions.maxNumberDelegatedPartitions, partitionParamsArray, nodesToDelegate));
auto nodesToDelegateArray = BuildTfLiteIntArray(nodesToDelegate);
if (nodesToDelegateArray->size == 0) {
TFLITE_LOG_PROD(TFLITE_LOG_INFO, "[NNRT-DELEGATE] No node to delegate.");
return kTfLiteOk;
} else {
// Request TFLite to partition the graph and make kernels
// for each independent node sub set a new nnrtDelegateKernel.
return context->ReplaceNodeSubsetsWithDelegateKernels(context,
nnrtDelegateKernel, nodesToDelegateArray.get(), delegate);
}
}
// Return a singleton NNRT Delegate that can check ops supported.
TfLiteDelegate* NnrtDelegateSingleton()
{
static NnrtDelegate delegate;
return &delegate;
}
} // namespace tflite
@@ -0,0 +1,167 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_H
#define TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_H
#include <string>
#include <vector>
#include "neural_network_runtime.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/delegates/serialization.h"
#include "../nnrt/nnrt_implementation.h"
namespace tflite {
namespace delegate {
namespace nnrt {
class NnrtDelegateKernel;
} // namespace nnrt
} // namespace delegate
using tflite::delegate::nnrt::NnrtDelegateKernel;
// TFliteDelegate to interface with NNRT.
class NnrtDelegate : public TfLiteDelegate {
public:
struct Options {
OH_NN_PerformanceMode executionPerformance = OH_NN_PERFORMANCE_NONE;
std::string acceleratorName;
std::string cacheDir;
std::string modelToken;
OH_NN_Priority executionPriority = OH_NN_PRIORITY_MEDIUM;
int32_t maxNumberDelegatedPartitions = -1;
uint64_t maxCompilationTimeoutDurationNs = 0;
uint64_t maxExecutionTimeoutDurationNs = 0;
uint64_t maxExecutionLoopTimeoutDurationNs = 0;
// allow fp32 compuation to be run in fp16.
bool enableFp16 = false;
bool allowDynamicDimensions = false;
uint32_t version {0};
};
// Uses default options.
NnrtDelegate();
// The ownership of the NNRT instance is left to the caller of the
// NnrtDelegate constructor; the caller must ensure that the lifetime
// of the NNRT instance exceeds the lifetime of the NnrtDelegate.
explicit NnrtDelegate(const NnrtApi* nnrt);
// The constructor that accepts options from user.
// This makes a copy of any data that it needs from Options, so
// the caller can safely deallocate any storage pointed to by
// the 'const char *' members of Options immediately after calling this.
explicit NnrtDelegate(const Options& options);
// Constructor that accepts both an NNRT instance and options.
// The ownership of the NNRT instance is left to the caller of the
// NnrtDelegate constructor; the caller must ensure that the lifetime
// of the NNRT instance exceeds the lifetime of the NnrtDelegate.
// This constructor makes a copy of any data that it needs from Options, so
// the caller can safely deallocate any storage pointed to by
// the 'const char *' members of Options immediately after calling this.
NnrtDelegate(const NnrtApi* nnrt, const Options& options);
~NnrtDelegate() = default;
// Returns the delegate options.
// The lifetime of the storage pointed to by the 'const char *' members of the
// returned Options object is the same as the lifetime of the supplied
// TfLiteDelegate instance.
static TfLiteStatus GetOptions(const TfLiteDelegate* pDelegate, Options& options);
private:
struct Data {
const NnrtApi* nnrt = nullptr;
// Preferred Power/perf trade-off.
OH_NN_PerformanceMode executionPerformance = OH_NN_PERFORMANCE_NONE;
// Selected NNRT accelerator name.
std::string acceleratorName;
// The cache dir for NNRT model.
std::string cacheDir;
// The unique token string for NNRT model.
std::string modelToken;
// Maximum number of NNRT partition to delegate. Zero or negative means
// no limit.
int32_t maxNumberDelegatedPartitions = -1;
// Specifies the relative priority for executions of the model.
OH_NN_Priority executionPriority = OH_NN_PRIORITY_MEDIUM;
// Specifies the maximum expected duration in nanosecond for compiling the
// model.
uint64_t maxCompilationTimeoutDurationNs = 0;
// Specifies the maximum expected duration in nanosecond for executing the
// model.
uint64_t maxExecutionTimeoutDurationNs = 0;
// Specifies the maximum expected duration in nanosecond for WHILE loops in
// the execution
uint64_t maxExecutionLoopTimeoutDurationNs = 0;
// allow fp32 compuation to be run in fp16.
bool enableFp16 = false;
// Whether to allow dynamic dimension sizes without re-compilation.
bool allowDynamicDimensions = false;
uint32_t version {0};
explicit Data(const NnrtApi* nnrt);
~Data();
};
static TfLiteStatus DoPrepare(TfLiteContext* context, TfLiteDelegate* delegate);
static TfLiteStatus DoCopyFromBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate, TfLiteBufferHandle bufferHandle, TfLiteTensor* tensor);
static TfLiteStatus DoCopyToBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate, TfLiteBufferHandle bufferHandle, TfLiteTensor* tensor);
static void DoFreeBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate, TfLiteBufferHandle* handle);
static TfLiteStatus LimitDelegatedPartitions(int32_t maxPartitions,
std::vector<TfLiteDelegateParams> partitionParamsArray, std::vector<int32_t>& nodesToDelegate);
static TfLiteStatus GetSupportedNodes(TfLiteContext* context,
TfLiteDelegate* delegate, std::vector<int32_t>& supportedNodes);
static void GetDelegateKernelRegistration(TfLiteDelegate* delegate, TfLiteRegistration& nnrtDelegateKernel);
static TfLiteStatus CheckDeviceValid(TfLiteContext* context, TfLiteDelegate* delegate);
void NnrtDelegateConstructorImpl(const Options& options);
private:
// Delegate data presented through TfLiteDelegate::data_.
Data m_delegateData;
};
TfLiteDelegate* NnrtDelegateSingleton();
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_H
@@ -0,0 +1,420 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nnrt_delegate_kernel.h"
#include <algorithm>
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <functional>
#include <initializer_list>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "tensorflow/lite/context_util.h"
#include "neural_network_runtime.h"
namespace tflite {
namespace delegate {
namespace nnrt {
constexpr int32_t SCALAR_RANK = 1;
#define RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_COMPILE(code, callDesc) \
do { \
if ( (code) != OH_NN_SUCCESS) { \
const auto errorDesc = NnrtErrorDescription((code)); \
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "NN API returned error %s at line %d while %s.\n", errorDesc.c_str(), \
__LINE__, (callDesc)); \
m_nnrt->OH_NNCompilation_Destroy(&m_pNnCompilation); \
return kTfLiteError; \
} \
} while (0)
bool NnrtDelegateKernel::Validate(const int32_t builtinCode)
{
if (TFLITE_TYPE_TO_NNRT_TYPE.count(builtinCode) &&
TFLITE_TYPE_TO_NNRT_TYPE.at(builtinCode) != OH_NN_UNSUPPORT_OPS) {
return true;
}
return false;
}
TfLiteStatus NnrtDelegateKernel::Init(TfLiteContext* context, const TfLiteDelegateParams* params)
{
TF_LITE_ENSURE_EQ(context, params != nullptr, true);
if (m_initialised) {
TFLITE_LOG_PROD(TFLITE_LOG_INFO,
"[NNRT-DELEGATE_KERNEL] NnrtDelegateKernel has completed initialization, no need init again.");
return kTfLiteOk;
}
for (auto nodeIndex : TfLiteIntArrayView(params->nodes_to_replace)) {
m_delegateNodes.emplace_back(nodeIndex);
}
NnrtDelegate::Options delegateOptions;
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(params->delegate, delegateOptions));
TF_LITE_ENSURE_STATUS(tflite::GetTargetDevice(context, params->delegate, m_nnrt, m_nnrtDevice));
if (m_nnModel == nullptr) {
m_nnModel = m_nnrt->OH_NNModel_Construct();
if (m_nnModel == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Fail to create ONNRT model.");
return kTfLiteError;
}
TF_LITE_ENSURE_STATUS(BuildGraph(context, delegateOptions, params->input_tensors, params->output_tensors));
}
m_initialised = true;
return kTfLiteOk;
}
TfLiteStatus NnrtDelegateKernel::Prepare(TfLiteContext* context, TfLiteNode* node)
{
TF_LITE_ENSURE_EQ(context, node != nullptr, true);
if (!m_initialised) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-DELEGATE_KERNEL] NnrtDelegateKernel Prepare failed, not Init yet.");
return kTfLiteError;
}
if (m_compiled) {
return kTfLiteOk; // If model has completed compilation, no need compile again.
}
// Create OH_NNCompilation
m_pNnCompilation = m_nnrt->OH_NNCompilation_Construct(m_nnModel);
if (m_pNnCompilation == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Fail to create OH_NNCompilation instance.");
return kTfLiteError;
}
NnrtDelegate::Options delegateOptions;
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(node->delegate, delegateOptions));
TF_LITE_ENSURE_STATUS(SetNnOptions(context, delegateOptions));
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_COMPILE(m_nnrt->OH_NNCompilation_Build(m_pNnCompilation),
"completing NNRT compilation");
m_compiled = true;
return kTfLiteOk;
}
TfLiteStatus NnrtDelegateKernel::Invoke(TfLiteContext* context, TfLiteNode* node)
{
if (!m_compiled) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-DELEGATE_KERNEL] NnrtDelegateKernel Invoke failed, not compile yet.");
return kTfLiteError;
}
// Create OH_NNExecutor_Construct
OH_NNExecutor* pNnExecution {nullptr};
pNnExecution = m_nnrt->OH_NNExecutor_Construct(m_pNnCompilation);
if (pNnExecution == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Fail to create OH_NNExecutor instance.");
return kTfLiteError;
}
// Set the input tensor buffers.
OH_NN_Tensor inputNnTensor;
TF_LITE_ENSURE_STATUS(SetInputTensors(context, node, pNnExecution, inputNnTensor));
// Get the output tensor buffers.
TF_LITE_ENSURE_STATUS(SetOutputTensors(context, node, pNnExecution));
// Invoke delegated subgraph.
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNExecutor_Run(pNnExecution), "running computation");
m_nnrt->OH_NNExecutor_Destroy(&pNnExecution);
pNnExecution = nullptr;
return kTfLiteOk;
}
TfLiteStatus NnrtDelegateKernel::Map(const int32_t builtinCode, const NnrtOpMappingArgs& mappingArgs,
int32_t& nnOpType) const
{
if (TFLITE_TYPE_TO_NNRT_TYPE.count(builtinCode) == 0) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-DELEGATE_KERNEL] Not support current TF-Lite Operator, builtCode: %d.", builtinCode);
return kTfLiteError;
}
TfLiteStatus retValue = mappingArgs.builder->AddOpFuncParams(mappingArgs, builtinCode);
if (retValue != kTfLiteOk) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Failed to add params to these operations.");
return retValue;
}
nnOpType = TFLITE_TYPE_TO_NNRT_TYPE.at(builtinCode);
return kTfLiteOk;
}
TfLiteStatus NnrtDelegateKernel::BuildGraph(TfLiteContext* context, const NnrtDelegate::Options& delegateOptions,
const TfLiteIntArray* inputTensors, const TfLiteIntArray* outputTensors)
{
if (context == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] The context is nullptr when building the graph.");
return kTfLiteError;
}
TF_LITE_ENSURE_EQ(context, inputTensors != nullptr, true);
TF_LITE_ENSURE_EQ(context, outputTensors != nullptr, true);
// Build the ops and tensors.
TF_LITE_ENSURE_STATUS(AddOpsAndTensors(context, inputTensors, delegateOptions));
// Map input and output tensor indices to NN
// Make the TensorFlow Lite inputs and outputs to nn_indices.
OH_NN_UInt32Array inputIndices;
OH_NN_UInt32Array outputIndices;
std::vector<uint32_t> inputsData;
for (auto i : TfLiteIntArrayView(inputTensors)) {
// Constant tensors are not NNRT inputs.
if ((i != kTfLiteOptionalTensor) && (context->tensors[i].allocation_type != kTfLiteMmapRo) &&
// The delegate might not have mapped this input (this can
// happen if one tensor is split in several ones)
(m_tensorMapping.LiteIndexToNn(i) != INVALID_INDEX)) {
const int32_t inputTensorNnIndex = m_tensorMapping.LiteIndexToNn(i);
inputsData.emplace_back(inputTensorNnIndex);
}
}
std::vector<uint32_t> outputsData;
for (auto i : TfLiteIntArrayView(outputTensors)) {
const int32_t outputTensorNnIndex = m_tensorMapping.LiteIndexToNn(i);
// Unmapped outputs are not added
if (outputTensorNnIndex != INVALID_INDEX) {
outputsData.emplace_back(outputTensorNnIndex);
}
}
inputIndices.data = inputsData.data();
outputIndices.data = outputsData.data();
inputIndices.size = inputsData.size();
outputIndices.size = outputsData.size();
// Tell NN to declare inputs/outputs
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNModel_SpecifyInputsAndOutputs(m_nnModel, &inputIndices,
&outputIndices), "identifying model inputs and outputs");
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNModel_Finish(m_nnModel), "finalizing the model");
return kTfLiteOk;
}
TfLiteStatus NnrtDelegateKernel::AddOpsAndTensors(TfLiteContext* context, const TfLiteIntArray* inputTensors,
const NnrtDelegate::Options& delegateOptions)
{
// The tensor builder allows creating a single op. It is created outside
// the for loop to avoid reallocating the vectors.
NnrtOpBuilderArgs opBuilderArgs = {
.context = context,
.nnModel = m_nnModel,
.inputTensors = const_cast<TfLiteIntArray*>(inputTensors),
.pTensorMapping = &m_tensorMapping,
.delegateOptions = delegateOptions
};
NnrtOpBuilder builder(m_nnrt, opBuilderArgs);
// Clear the input and output lists.
builder.ClearInputOuputLists();
// Add other tensors.
TfLiteNode* node = nullptr;
TfLiteRegistration* reg = nullptr;
for (int32_t nodeIndex : m_delegateNodes) {
node = nullptr;
reg = nullptr;
TF_LITE_ENSURE_STATUS(
context->GetNodeAndRegistration(context, nodeIndex, &node, &reg)); // Obtain the op and registration.
if ((node == nullptr) || (reg == nullptr)) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Get node and registration failed.");
return kTfLiteError;
}
const bool scalarAsTensor = IsScalarInputSupported(reg->builtin_code);
int32_t inputTensorFlags = 0;
if (scalarAsTensor) {
inputTensorFlags |= NN_TENSOR_FLAG_SCALAR_AS_TENSOR;
}
// Get op type and tensors, fails if the Validate function failed.
int32_t nnOpType;
NnrtOpMappingArgs opMappingArgs = { context, &builder, node, nodeIndex };
TF_LITE_ENSURE_STATUS(Map(reg->builtin_code, opMappingArgs, nnOpType));
for (int32_t inputPos = 0; inputPos < node->inputs->size; ++inputPos) {
if ((reg->builtin_code == kTfLiteBuiltinFullyConnected) &&
(node->inputs->data[inputPos] == kTfLiteOptionalTensor)) {
continue; // skip optional bias and handle it during mapping.
}
const auto inputIndex = node->inputs->data[inputPos];
TF_LITE_ENSURE_STATUS(builder.AddTensorInput(inputIndex, reg->builtin_code, inputTensorFlags));
}
// Map outputs to NN API tensor indices.
int32_t outputTensorFlags = 0;
for (int32_t outputPos = 0; outputPos < node->outputs->size; ++outputPos) {
auto outputIndex = node->outputs->data[outputPos];
TF_LITE_ENSURE_STATUS(builder.AddTensorOutput(outputIndex, reg->builtin_code, outputTensorFlags));
}
TF_LITE_ENSURE_STATUS(builder.FinalizeAddOperation(static_cast<OH_NN_OperationType>(nnOpType), nodeIndex));
}
return kTfLiteOk;
}
TfLiteStatus NnrtDelegateKernel::ConvertTensorTypeToNn(TfLiteContext* context,
const std::pair<int32_t, int32_t>& indexPair, OH_NN_QuantParam* nnQuantParam, OH_NN_Tensor& nnTensor)
{
TF_LITE_ENSURE_EQ(context, context->tensors_size > indexPair.first, true);
TfLiteTensor* tensor = &(context->tensors[indexPair.first]);
TF_LITE_ENSURE_EQ(context, tensor != nullptr, true);
OH_NN_DataType nnType {OH_NN_UNKNOWN};
TF_LITE_ENSURE_STATUS(m_tensorMapping.ConvertType(context, indexPair.first, 0, nnType));
uint32_t tensorRank = static_cast<uint32_t>(tensor->dims->size);
int32_t* tensorDims = reinterpret_cast<int32_t*>(tensor->dims->data);
if (tensorDims == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-DELEGATE_KERNEL] The tensorDims is nullptr when converting the type of tensors to nnrt.");
return kTfLiteError;
}
// treat scalar input as single cell tensor in NNRT.
if (tensorRank == 0) {
tensorRank = SCALAR_RANK;
*tensorDims = SCALAR_RANK;
}
nnTensor.dataType = nnType;
nnTensor.dimensionCount = tensorRank;
nnTensor.dimensions = tensorDims;
nnTensor.quantParam = nnQuantParam;
nnTensor.type = OH_NN_TENSOR;
return kTfLiteOk;
}
TfLiteStatus NnrtDelegateKernel::SetInputTensors(TfLiteContext* context, TfLiteNode* node,
OH_NNExecutor* pNnExecution, OH_NN_Tensor& nnTensor)
{
TF_LITE_ENSURE_EQ(context, node != nullptr, true);
TF_LITE_ENSURE_EQ(context, pNnExecution != nullptr, true);
// Note: we access tflite tensors using
// absolute indices but NN api indices inputs by relative indices.
int32_t relativeIndex = 0;
OH_NN_QuantParam* nnQuantParam = nullptr;
TfLiteIntArray* tensors = node->inputs;
TF_LITE_ENSURE_EQ(context, tensors != nullptr, true);
for (auto absoluteIndex : TfLiteIntArrayView(tensors)) {
if (absoluteIndex == kTfLiteOptionalTensor) {
continue;
}
std::pair<int32_t, int32_t> indexPair = std::make_pair(absoluteIndex, relativeIndex);
ConvertTensorTypeToNn(context, indexPair, nnQuantParam, nnTensor);
TfLiteTensor* tensor = &context->tensors[absoluteIndex];
TF_LITE_ENSURE_EQ(context, tensor != nullptr, true);
if (tensor->allocation_type != kTfLiteMmapRo) {
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_TENSOR(m_nnrt->OH_NNExecutor_SetInput(pNnExecution, relativeIndex,
&nnTensor, tensor->data.raw, tensor->bytes),
"associating NNRT execution output with a memory object", tensor);
++relativeIndex;
} else {
continue;
}
}
return kTfLiteOk;
}
TfLiteStatus NnrtDelegateKernel::SetOutputTensors(TfLiteContext* context, TfLiteNode* node,
OH_NNExecutor* pNnExecution)
{
TF_LITE_ENSURE_EQ(context, node != nullptr, true);
TF_LITE_ENSURE_EQ(context, pNnExecution != nullptr, true);
// Note: we access tflite tensors using
// absolute indices but NN api indices inputs by relative indices.
int32_t relativeIndex = 0;
TfLiteIntArray* tensors = node->outputs;
TF_LITE_ENSURE_EQ(context, tensors != nullptr, true);
for (auto absoluteIndex : TfLiteIntArrayView(tensors)) {
if (m_tensorMapping.LiteIndexToNn(absoluteIndex) == INVALID_INDEX) {
continue;
}
TfLiteTensor* tensor = &context->tensors[absoluteIndex];
TF_LITE_ENSURE_EQ(context, tensor != nullptr, true);
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_TENSOR(
m_nnrt->OH_NNExecutor_SetOutput(pNnExecution, relativeIndex, tensor->data.raw, tensor->bytes),
"associating NNRT execution output to a memory object", tensor);
++relativeIndex;
}
return kTfLiteOk;
}
TfLiteStatus NnrtDelegateKernel::SetNnOptions(TfLiteContext* context, const NnrtDelegate::Options& delegateOptions)
{
if (context == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-DELEGATE_KERNEL] The context is nullptr when setting nnrt options.");
return kTfLiteError;
}
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNCompilation_SetDevice(m_pNnCompilation, m_nnrtDevice),
"creating NNRT compilation");
auto performance = delegateOptions.executionPerformance;
if (performance != OH_NN_PERFORMANCE_NONE) {
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_COMPILE(
m_nnrt->OH_NNCompilation_SetPerformanceMode(m_pNnCompilation, performance),
"setting compilation performance");
}
// Set cacahe, if cacheDir & modelToken & device is valid.
std::string cacheDir = delegateOptions.cacheDir;
std::string modelToken = delegateOptions.modelToken;
uint32_t version = delegateOptions.version;
if (!cacheDir.empty() && (!IsUseTargetDevice(delegateOptions) ||
(delegateOptions.acceleratorName == NNRT_REFERENCE_DEVICE))) {
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_COMPILE(
m_nnrt->OH_NNCompilation_SetCache(m_pNnCompilation, cacheDir.c_str(), version),
"setting compilation cache");
} else if (cacheDir.empty()) {
TFLITE_LOG_PROD(TFLITE_LOG_WARNING, "The cacheDir is empty, will not load or save cache.");
}
return kTfLiteOk;
}
} // namespace nnrt
} // namespace delegate
} // tflite
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_KERNEL_H
#define TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_KERNEL_H
#include "neural_network_runtime.h"
#include "tensorflow/lite/c/common.h"
#include "tensor_mapping.h"
#include "nnrt_op_builder.h"
namespace tflite {
namespace delegate {
namespace nnrt {
// Represents a subgraph in TFLite that will be delegated to NNRt.
// It is abstracted as a single kernel node in the main TFLite graph and
// implements Init/Prepare/Invoke as TFLite kernel nodes.
class NnrtDelegateKernel {
public:
explicit NnrtDelegateKernel(const NnrtApi* nnrt)
: m_initialised(false),
m_compiled(false),
m_nnrt(nnrt),
m_nnModel(nullptr),
m_pNnCompilation(nullptr) {}
NnrtDelegateKernel() : NnrtDelegateKernel(NnrtImplementation()) {}
virtual ~NnrtDelegateKernel()
{
m_nnrt->OH_NNModel_Destroy(&m_nnModel);
m_nnrt->OH_NNCompilation_Destroy(&m_pNnCompilation);
m_nnrt = nullptr;
}
// Returns true if the node can be accelerated with NNRT.
static bool Validate(const int32_t builtinCode);
// Initialize the kernel (a NN model) and builds the NN Model.
TfLiteStatus Init(TfLiteContext* context, const TfLiteDelegateParams* params);
// Creates the NNRT Compilation for the NN model. It assumes that Init has
// been called and completed successfully.
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node);
// Invoke the NN Model. Expects Init and Prepare to have been completed successfully.
TfLiteStatus Invoke(TfLiteContext* context, TfLiteNode* node);
private:
TfLiteStatus Map(int32_t builtinCode, const NnrtOpMappingArgs& mappingArgs, int32_t& nnOpType) const;
TfLiteStatus AddOpsAndTensors(TfLiteContext* context, const TfLiteIntArray* inputTensors,
const NnrtDelegate::Options& delegateOptions);
TfLiteStatus BuildGraph(TfLiteContext* context, const NnrtDelegate::Options& options,
const TfLiteIntArray* inputTensors, const TfLiteIntArray* outputTensors);
TfLiteStatus ConvertTensorTypeToNn(TfLiteContext* context, const std::pair<int32_t, int32_t>& indexPair,
OH_NN_QuantParam* nnQuantParam, OH_NN_Tensor& nnTensor);
TfLiteStatus SetInputTensors(TfLiteContext* context, TfLiteNode* node, OH_NNExecutor* pNnExecution,
OH_NN_Tensor& nnTensor);
TfLiteStatus SetOutputTensors(TfLiteContext* context, TfLiteNode* node, OH_NNExecutor* pNnExecution);
TfLiteStatus SetNnOptions(TfLiteContext* context, const NnrtDelegate::Options& delegateOptions);
private:
// True if initialization has been completed successfully
bool m_initialised;
// True if compilation has been completed successfully
bool m_compiled;
// NN device handle.
size_t m_nnrtDevice;
// Access to NNRT.
const NnrtApi* m_nnrt;
// NN API state.
OH_NNModel* m_nnModel;
OH_NNCompilation* m_pNnCompilation;
// Node indices that this delegate is responsible for. Indices here
// indexes into the nodes array in the TfLiteContext.
std::vector<int32_t> m_delegateNodes;
// Track indices we use
TensorMapping m_tensorMapping;
};
} // namespace nnrt
} // namespace delegate
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_KERNEL_H
@@ -0,0 +1,215 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include <utility>
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "nnrt_delegate.h"
#include "../nnrt/nnrt_implementation.h"
namespace tflite {
namespace tools {
constexpr int32_t DEFAULT_THREADS = 1;
constexpr int32_t DEFAULT_DELEGATE_NUM = -1;
class NnrtDelegateProvider : public DelegateProvider {
public:
NnrtDelegateProvider()
{
default_params_.AddParam("use_nnrt", ToolParam::Create<bool>(false));
default_params_.AddParam("performance", ToolParam::Create<std::string>(""));
default_params_.AddParam("priority", ToolParam::Create<std::string>(""));
default_params_.AddParam("device", ToolParam::Create<std::string>(""));
default_params_.AddParam("cache_dir", ToolParam::Create<std::string>(""));
default_params_.AddParam("model_token", ToolParam::Create<std::string>(""));
default_params_.AddParam("max_delegate_num", ToolParam::Create<int32_t>(DEFAULT_DELEGATE_NUM));
default_params_.AddParam("enable_fp16", ToolParam::Create<bool>(false));
default_params_.AddParam("allow_dynamic_dimensions", ToolParam::Create<bool>(false));
}
~NnrtDelegateProvider() {};
std::vector<Flag> CreateFlags(ToolParams* param) const final;
void LogParams(const ToolParams& params, bool verbose) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::pair<TfLiteDelegatePtr, int32_t> CreateRankedTfLiteDelegate(const ToolParams& params) const final;
std::string GetName() const final
{
return "NNRT";
}
};
REGISTER_DELEGATE_PROVIDER(NnrtDelegateProvider);
std::vector<Flag> NnrtDelegateProvider::CreateFlags(ToolParams* params) const
{
std::vector<Flag> flags = {
CreateFlag<int32_t>("max_delegate_num", params, "Delegate max num limit, max_delegate_num <= 0 means no limit"),
CreateFlag<bool>("enable_fp16", params, "Whether to Infer model with FP16."),
CreateFlag<bool>("allow_dynamic_dimensions", params,
"Whether to allow dynamic dimension sizes without re-compilation."),
CreateFlag<std::string>("performance", params,
"Execution performance for nnrt delegate. "
"choose within [low, medium, high, extreme, default]."),
CreateFlag<std::string>("priority", params,
"The model execution priority in nnrt, and it "
"choose within [default, low, medium, high]."),
CreateFlag<std::string>("device", params,
"The name of the nnrt accelerator to use, "
"choose within [cpu, gpu, apu, nnrt-reference], "
"nnrt-reference means chosen automatically by nnrt."),
CreateFlag<std::string>("cache_dir", params, "The directory of load and save cache for delegate"),
CreateFlag<std::string>("model_token", params, "The file_name of load and save cache for delegate"),
};
return flags;
}
void NnrtDelegateProvider::LogParams(const ToolParams& params, bool verbose) const
{
LOG_TOOL_PARAM(params, bool, "use_nnrt", "Use NNRT", verbose);
if (!params.Get<bool>("use_nnrt")) {
return; // no use nnrt, return.
}
LOG_TOOL_PARAM(params, std::string, "performance", "NNRT execution performance", verbose);
LOG_TOOL_PARAM(params, std::string, "priority", "NNRT execution priority", verbose);
LOG_TOOL_PARAM(params, std::string, "device", "NNRT accelerator name", verbose);
LOG_TOOL_PARAM(params, std::string, "cache_dir", "NNRT model cache directory", verbose);
LOG_TOOL_PARAM(params, std::string, "model_token", "NNRT model cache filename", verbose);
LOG_TOOL_PARAM(params, int32_t, "max_delegate_num", "NNRT delegate max partition", verbose);
LOG_TOOL_PARAM(params, bool, "enable_fp16", "NNRT allow fp16 inference", verbose);
LOG_TOOL_PARAM(params, bool, "allow_dynamic_dimensions", "NNRT allow dynamic dimensions", verbose);
}
TfLiteStatus GetExecutionPerformance(const ToolParams& params, NnrtDelegate::Options& options)
{
std::string stringExecutionPerformance = params.Get<std::string>("performance");
if (stringExecutionPerformance.empty()) {
return kTfLiteOk; // no set performance
}
OH_NN_PerformanceMode executionPerformance = OH_NN_PERFORMANCE_NONE;
if (stringExecutionPerformance == "low") {
executionPerformance = OH_NN_PERFORMANCE_LOW;
} else if (stringExecutionPerformance == "medium") {
executionPerformance = OH_NN_PERFORMANCE_MEDIUM;
} else if (stringExecutionPerformance == "high") {
executionPerformance = OH_NN_PERFORMANCE_HIGH;
} else if (stringExecutionPerformance == "extreme") {
executionPerformance = OH_NN_PERFORMANCE_EXTREME;
} else if (stringExecutionPerformance == "default") {
executionPerformance = OH_NN_PERFORMANCE_NONE;
} else {
TFLITE_LOG(ERROR) << "The provided value is not a valid nnrt execution performance.";
return kTfLiteError;
}
options.executionPerformance = executionPerformance;
return kTfLiteOk;
}
TfLiteStatus GetExecutionPriority(const ToolParams& params, NnrtDelegate::Options& options)
{
std::string stringExecutionPriority = params.Get<std::string>("priority");
if (stringExecutionPriority.empty()) {
return kTfLiteOk; // no set priority
}
OH_NN_Priority executionPriority = OH_NN_PRIORITY_MEDIUM;
if (stringExecutionPriority == "low") {
executionPriority = OH_NN_PRIORITY_LOW;
} else if (stringExecutionPriority == "medium") {
executionPriority = OH_NN_PRIORITY_MEDIUM;
} else if (stringExecutionPriority == "high") {
executionPriority = OH_NN_PRIORITY_HIGH;
} else if (stringExecutionPriority == "default") {
executionPriority = OH_NN_PRIORITY_MEDIUM;
} else {
TFLITE_LOG(ERROR) << "The provided value is not a valid nnrt execution priority.";
return kTfLiteError;
}
options.executionPriority = executionPriority;
return kTfLiteOk;
}
TfLiteStatus MapParams(const ToolParams& params, NnrtDelegate::Options& options)
{
std::string acceleratorName = params.Get<std::string>("device");
if (!acceleratorName.empty()) {
options.acceleratorName = acceleratorName;
}
if (params.GetParam("max_delegate_num") != nullptr) {
options.maxNumberDelegatedPartitions = params.Get<int32_t>("max_delegate_num");
}
std::string cacheDir = params.Get<std::string>("cache_dir");
if (!cacheDir.empty()) {
options.cacheDir = cacheDir;
}
std::string modelToken = params.Get<std::string>("model_token");
if (!modelToken.empty()) {
options.modelToken = modelToken;
}
if (params.Get<bool>("enable_fp16")) {
options.enableFp16 = true;
}
if (params.Get<bool>("allow_dynamic_dimensions")) {
options.allowDynamicDimensions = true;
}
return kTfLiteOk;
}
TfLiteDelegatePtr NnrtDelegateProvider::CreateTfLiteDelegate(const ToolParams& params) const
{
TfLiteDelegatePtr delegate(nullptr, [](TfLiteDelegate*) {});
if (!params.Get<bool>("use_nnrt")) {
return delegate;
}
NnrtDelegate::Options options;
TFLITE_TOOLS_CHECK(MapParams(params, options) == kTfLiteOk) << "Map params to NNRT Delegate options failed.";
TFLITE_TOOLS_CHECK(GetExecutionPerformance(params, options) == kTfLiteOk) <<
"Create TfLite NNRT Delegate failed.";
TFLITE_TOOLS_CHECK(GetExecutionPriority(params, options) == kTfLiteOk) << "Create TfLite NNRT Delegate failed.";
const auto* nnrtImpl = NnrtImplementation();
if (!nnrtImpl->nnrtExists) {
TFLITE_LOG(WARN) << "NNRT acceleration is unsupported on this platform.";
return delegate;
}
return TfLiteDelegatePtr(new (std::nothrow) NnrtDelegate(nnrtImpl, options),
[](TfLiteDelegate* delegate) { delete reinterpret_cast<NnrtDelegate*>(delegate); });
}
std::pair<TfLiteDelegatePtr, int32_t> NnrtDelegateProvider::CreateRankedTfLiteDelegate(const ToolParams& params) const
{
auto ptr = CreateTfLiteDelegate(params);
LogParams(params, false);
return std::make_pair(std::move(ptr), params.GetPosition<bool>("use_nnrt"));
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,503 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define __STDC_WANT_LIB_EXT1__ 1
#include "nnrt_op_builder.h"
#include <cstring>
#include "neural_network_runtime.h"
#include "tensorflow/lite/util.h"
#include "tensorflow/lite/context_util.h"
#include "nnrt_utils.h"
namespace tflite {
namespace delegate {
namespace nnrt {
constexpr int32_t SCALAR_TENSOR_RANK = 1;
constexpr int32_t ADDZEROS_BIAS_INDEX = -1;
constexpr int32_t UNSPECIFIED_DIMENSION_VALUE = -1;
const std::vector<int32_t> DEPTHWISE_TRANSPOSE_AXISS = { 3, 1, 2, 0 };
NnrtOpBuilder::NnrtOpBuilder(const NnrtApi* nnrt, NnrtOpBuilderArgs& opBuilderArgs)
: m_nnrt(nnrt),
m_context(opBuilderArgs.context),
m_pTensorMapping(opBuilderArgs.pTensorMapping),
m_nnModel(opBuilderArgs.nnModel),
m_allowDynamicDimensions(opBuilderArgs.delegateOptions.allowDynamicDimensions)
{
// Map Op func pointer
MapBuiltinCodeToFunc();
// Get model inputs
for (int32_t i : TfLiteIntArrayView(opBuilderArgs.inputTensors)) {
// Constant tensors are not NNRT inputs.
if (i != kTfLiteOptionalTensor && opBuilderArgs.context->tensors[i].allocation_type != kTfLiteMmapRo) {
m_inputs.emplace_back(i);
}
}
}
TfLiteStatus NnrtOpBuilder::AddZerosBias(const NnrtOpMappingArgs& mappingArgs, int32_t inputId, int32_t filterId,
int32_t channelNum)
{
int32_t biasIndex = ADDZEROS_BIAS_INDEX;
mappingArgs.context->AddTensors(mappingArgs.context, 1, &biasIndex);
TfLiteTensor* biasTensor = &mappingArgs.context->tensors[biasIndex];
const auto inputType = mappingArgs.context->tensors[inputId].type;
if (inputType == kTfLiteFloat32) {
biasTensor->type = kTfLiteFloat32;
} else {
biasTensor->type = kTfLiteInt32;
}
// Create an array with a required bias shape and resize the bias tensor.
TfLiteIntArray* biasShape = TfLiteIntArrayCreate(1); // 1-dimension
biasShape->data[0] = channelNum;
biasTensor->allocation_type = kTfLiteDynamic;
mappingArgs.context->ResizeTensor(mappingArgs.context, biasTensor, biasShape);
// Set tensor's values to zeroes and add it using AddVector*, so that the values are copied to NNRT.
#ifdef __STDC_LIB_EXT1__
if (inputType == kTfLiteFloat32) {
memset_s(biasTensor->data.f, biasTensor->bytes, 0, channelNum * sizeof(float));
TF_LITE_ENSURE_STATUS(mappingArgs.builder->AddVectorFloat32Tensor(biasTensor->data.f, channelNum,
OH_NN_TENSOR));
} else {
memset_s(biasTensor->data.i32, biasTensor->bytes, 0, channelNum * sizeof(int32_t));
const TfLiteTensor& inputTensor = mappingArgs.context->tensors[inputId];
const TfLiteTensor& filterTensor = mappingArgs.context->tensors[filterId];
// NNRT requires bias scale to be a product of an input scale and a filter scale.
biasTensor->params.scale = inputTensor.params.scale * filterTensor.params.scale;
TF_LITE_ENSURE_STATUS(mappingArgs.builder->AddVectorInt32Tensor(biasTensor->data.i32, channelNum,
OH_NN_TENSOR));
}
#endif
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddBasicComputeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
if (builtinCode == kTfLiteBuiltinAdd) {
auto builtin = reinterpret_cast<TfLiteAddParams*>(mappingArgs.node->builtin_data);
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteAddParams>(mappingArgs, builtin, builtinCode,
OH_NN_ADD_ACTIVATIONTYPE));
} else if (builtinCode == kTfLiteBuiltinMul) {
auto builtin = reinterpret_cast<TfLiteMulParams*>(mappingArgs.node->builtin_data);
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteMulParams>(mappingArgs, builtin, builtinCode,
OH_NN_MUL_ACTIVATION_TYPE));
} else if (builtinCode == kTfLiteBuiltinSub) {
auto builtin = reinterpret_cast<TfLiteSubParams*>(mappingArgs.node->builtin_data);
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteSubParams>(mappingArgs, builtin, builtinCode,
OH_NN_SUB_ACTIVATIONTYPE));
} else {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] unsupportted basic compute type %d.", builtinCode);
return kTfLiteError;
}
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddAvgPoolingParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
auto builtin = reinterpret_cast<TfLitePoolParams*>(mappingArgs.node->builtin_data);
std::vector<int64_t> kernel = { static_cast<int64_t>(builtin->filter_height),
static_cast<int64_t>(builtin->filter_width) };
std::vector<int64_t> stride = { static_cast<int64_t>(builtin->stride_height),
static_cast<int64_t>(builtin->stride_width) };
mappingArgs.builder->AddVectorInt64Tensor(kernel.data(), kernel.size(), OH_NN_AVG_POOL_KERNEL_SIZE);
mappingArgs.builder->AddVectorInt64Tensor(stride.data(), stride.size(), OH_NN_AVG_POOL_STRIDE);
TF_LITE_ENSURE_STATUS(AddPadParamsInOperator<TfLitePoolParams>(mappingArgs, builtin, builtinCode,
OH_NN_AVG_POOL_PAD_MODE));
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLitePoolParams>(mappingArgs, builtin, builtinCode,
OH_NN_AVG_POOL_ACTIVATION_TYPE));
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddMaxPoolingParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
auto builtin = reinterpret_cast<TfLitePoolParams*>(mappingArgs.node->builtin_data);
std::vector<int64_t> kernel = { static_cast<int64_t>(builtin->filter_height),
static_cast<int64_t>(builtin->filter_width) };
std::vector<int64_t> stride = { static_cast<int64_t>(builtin->stride_height),
static_cast<int64_t>(builtin->stride_width) };
mappingArgs.builder->AddVectorInt64Tensor(kernel.data(), kernel.size(), OH_NN_MAX_POOL_KERNEL_SIZE);
mappingArgs.builder->AddVectorInt64Tensor(stride.data(), stride.size(), OH_NN_MAX_POOL_STRIDE);
TF_LITE_ENSURE_STATUS(AddPadParamsInOperator<TfLitePoolParams>(mappingArgs, builtin, builtinCode,
OH_NN_MAX_POOL_PAD_MODE));
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLitePoolParams>(mappingArgs, builtin, builtinCode,
OH_NN_MAX_POOL_ACTIVATION_TYPE));
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddFullConnectedParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
// IF bias is not presented, bias input index will be -1.
const bool isBiasPresent =
(mappingArgs.node->inputs->size == 3) && (mappingArgs.node->inputs->data[2] != kTfLiteOptionalTensor);
if (!isBiasPresent) {
const int32_t inputTensorId = mappingArgs.node->inputs->data[0]; // kInputTensor
const int32_t filterTensorId = mappingArgs.node->inputs->data[1]; // kWeightsTensor
const int32_t numUnits = mappingArgs.context->tensors[filterTensorId].dims->data[0]; // bias channel num
TF_LITE_ENSURE_STATUS(AddZerosBias(mappingArgs, inputTensorId, filterTensorId, numUnits));
}
auto builtin = reinterpret_cast<TfLiteFullyConnectedParams*>(mappingArgs.node->builtin_data);
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteFullyConnectedParams>(mappingArgs, builtin, builtinCode,
OH_NN_FULL_CONNECTION_ACTIVATIONTYPE));
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddConcatenationParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
auto builtin = reinterpret_cast<TfLiteConcatenationParams*>(mappingArgs.node->builtin_data);
const int64_t axis = static_cast<int64_t>(builtin->axis);
mappingArgs.builder->AddScalarInt64Tensor(axis, OH_NN_CONCAT_AXIS);
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddSoftmaxParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
auto builtin = reinterpret_cast<TfLiteSoftmaxParams*>(mappingArgs.node->builtin_data);
const int64_t axis = static_cast<int64_t>(builtin->beta);
mappingArgs.builder->AddScalarInt64Tensor(axis, OH_NN_SOFTMAX_AXIS);
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddQuantizeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
OH_NN_DataType nnType {OH_NN_FLOAT32};
int32_t inputIndex = mappingArgs.node->inputs->data[0];
m_pTensorMapping->ConvertType(m_context, inputIndex, 0, nnType);
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(nnType), OH_NN_QUANT_DTYPE_CAST_SRC_T);
int32_t outputIndex = mappingArgs.node->outputs->data[0];
m_pTensorMapping->ConvertType(m_context, outputIndex, 0, nnType);
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(nnType), OH_NN_QUANT_DTYPE_CAST_DST_T);
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddPackParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
auto builtin = reinterpret_cast<TfLitePackParams*>(mappingArgs.node->builtin_data);
const int64_t axis = static_cast<int64_t>(builtin->axis);
mappingArgs.builder->AddScalarInt64Tensor(axis, OH_NN_STACK_AXIS);
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddPadParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
float padValue = 0.0;
mappingArgs.builder->AddScalarFloat32Tensor(padValue, OH_NN_PAD_CONSTANT_VALUE);
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddReduceMeanParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
auto builtin = reinterpret_cast<TfLiteReducerParams*>(mappingArgs.node->builtin_data);
const int32_t keepDims = (builtin->keep_dims);
mappingArgs.builder->AddScalarBoolTensor(keepDims, OH_NN_REDUCE_MEAN_KEEP_DIMS);
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddStridedSliceParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
auto builtin = reinterpret_cast<TfLiteStridedSliceParams*>(mappingArgs.node->builtin_data);
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->begin_mask),
OH_NN_STRIDED_SLICE_BEGIN_MASK);
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->end_mask),
OH_NN_STRIDED_SLICE_END_MASK);
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->ellipsis_mask),
OH_NN_STRIDED_SLICE_ELLIPSIS_MASK);
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->new_axis_mask),
OH_NN_STRIDED_SLICE_NEW_AXIS_MASK);
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->shrink_axis_mask),
OH_NN_STRIDED_SLICE_SHRINK_AXIS_MASK);
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddReshapeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
if (mappingArgs.node->inputs->size == 1) {
auto builtin = reinterpret_cast<TfLiteReshapeParams*>(mappingArgs.node->builtin_data);
int32_t numDimensions = builtin->num_dimensions;
std::vector<int32_t> outputShape(numDimensions);
for (int32_t i = 0; i < numDimensions; ++i) {
outputShape[i] = builtin->shape[i];
}
mappingArgs.builder->AddVectorInt32Tensor(outputShape.data(), outputShape.size(), OH_NN_TENSOR);
}
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddConv2DParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
auto builtin = reinterpret_cast<TfLiteConvParams*>(mappingArgs.node->builtin_data);
std::vector<int64_t> stride = { static_cast<int64_t>(builtin->stride_height),
static_cast<int64_t>(builtin->stride_width) };
std::vector<int64_t> dilation = { static_cast<int64_t>(builtin->dilation_height_factor),
static_cast<int64_t>(builtin->dilation_width_factor) };
int64_t groupNum = 1;
mappingArgs.builder->AddVectorInt64Tensor(stride.data(), stride.size(), OH_NN_CONV2D_STRIDES);
mappingArgs.builder->AddVectorInt64Tensor(dilation.data(), dilation.size(), OH_NN_CONV2D_DILATION);
TF_LITE_ENSURE_STATUS(AddPadParamsInOperator<TfLiteConvParams>(mappingArgs, builtin, builtinCode,
OH_NN_CONV2D_PAD_MODE));
mappingArgs.builder->AddScalarInt64Tensor(groupNum, OH_NN_CONV2D_GROUP);
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteConvParams>(mappingArgs, builtin, builtinCode,
OH_NN_CONV2D_ACTIVATION_TYPE));
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddDepthwiseConv2DParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
auto builtin = reinterpret_cast<TfLiteDepthwiseConvParams*>(mappingArgs.node->builtin_data);
std::vector<int64_t> stride = { static_cast<int64_t>(builtin->stride_height),
static_cast<int64_t>(builtin->stride_width) };
std::vector<int64_t> dilation = { static_cast<int64_t>(builtin->dilation_height_factor),
static_cast<int64_t>(builtin->dilation_width_factor) };
TF_LITE_ENSURE_STATUS(mappingArgs.builder->AddVectorInt64Tensor(stride.data(), stride.size(),
OH_NN_DEPTHWISE_CONV2D_NATIVE_STRIDES));
TF_LITE_ENSURE_STATUS(mappingArgs.builder->AddVectorInt64Tensor(dilation.data(), dilation.size(),
OH_NN_DEPTHWISE_CONV2D_NATIVE_DILATION));
TF_LITE_ENSURE_STATUS(AddPadParamsInOperator<TfLiteDepthwiseConvParams>(mappingArgs, builtin, builtinCode,
OH_NN_DEPTHWISE_CONV2D_NATIVE_PAD));
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteDepthwiseConvParams>(mappingArgs, builtin, builtinCode,
OH_NN_DEPTHWISE_CONV2D_NATIVE_ACTIVATION_TYPE));
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::FinalizeAddOperation(OH_NN_OperationType type, int32_t liteNodeIndex)
{
// Actually add a NN API Operation
OH_NN_UInt32Array inputIndices;
OH_NN_UInt32Array outputIndices;
OH_NN_UInt32Array paramIndices;
inputIndices.data = m_augmentedInputs.data();
inputIndices.size = static_cast<uint32_t>(m_augmentedInputs.size());
outputIndices.data = m_augmentedOutputs.data();
outputIndices.size = static_cast<uint32_t>(m_augmentedOutputs.size());
paramIndices.size = static_cast<uint32_t>(m_augmentedParams.size());
paramIndices.data = (m_augmentedParams.size() == 0) ? nullptr : m_augmentedParams.data();
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNModel_AddOperation(m_nnModel,
type, &paramIndices, &inputIndices, &outputIndices), "adding operation");
m_augmentedInputs.clear();
m_augmentedOutputs.clear();
m_augmentedParams.clear();
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddTensor(int32_t tensorIndex, int32_t builtinCode, std::vector<uint32_t>& indices,
int32_t tensorFlags)
{
int32_t nnTensorIndex = m_pTensorMapping->LiteIndexToNn(tensorIndex);
if (nnTensorIndex != INVALID_INDEX) {
indices.emplace_back(nnTensorIndex);
return kTfLiteOk;
}
// Parameters needed for new type.
TfLiteTensor* tensor = &(m_context->tensors[tensorIndex]);
if (kTfLiteNoType == tensor->type) {
indices.emplace_back(INVALID_INDEX);
return kTfLiteOk;
}
TF_LITE_ENSURE_STATUS(AddTensor(tensorIndex, builtinCode, tensorFlags, nnTensorIndex));
indices.emplace_back(nnTensorIndex);
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddTensor(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags,
int32_t& nnTensorIndex)
{
TfLiteTensor* tensor = &(m_context->tensors[tensorIndex]);
const bool scalarAsTensor = tensorFlags & NN_TENSOR_FLAG_SCALAR_AS_TENSOR;
OH_NN_Tensor nnTensor;
OH_NN_QuantParam nnQuantParam;
std::vector<int32_t> weightDims;
void* tensorData = tensor->data.data;
std::vector<int8_t> depthwiseTensorData;
TF_LITE_ENSURE_STATUS(ConstructNNTensor(tensorIndex, builtinCode, scalarAsTensor, nnQuantParam, nnTensor));
// For depth-wise conv operator, we should transpose weight tensor to adapt NN tensor format.
if ((builtinCode == kTfLiteBuiltinDepthwiseConv2d) && (tensor->allocation_type == kTfLiteMmapRo) &&
(nnTensor.dimensionCount == DEPTHWISE_WEIGHT_DIMENSION_COUNT)) {
size_t typeBytes = 0;
int64_t tensorSize = 0;
TF_LITE_ENSURE_STATUS(GetSizeOfType(m_context, tensor->type, &typeBytes));
TF_LITE_ENSURE_STATUS(GetTensorSize(m_context, nnTensor.dimensions, nnTensor.dimensionCount, tensorSize));
depthwiseTensorData.assign(tensorSize * typeBytes, 0);
TfLiteStatus retCode = TransposeDepthwiseTensor(tensorIndex, nnTensor, weightDims, depthwiseTensorData);
if (retCode != kTfLiteOk) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] Fail to transpose depthwise tensor.");
return kTfLiteError;
}
tensorData = static_cast<void*>(depthwiseTensorData.data());
}
int32_t nnRet = m_nnrt->OH_NNModel_AddTensor(m_nnModel, &nnTensor);
if (nnRet != OH_NN_SUCCESS) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] Fail to add nnTensor to NN model.");
return kTfLiteError;
}
// Allocate a new tensor index
nnTensorIndex = m_pTensorMapping->AddNewNnTensorIndex(tensorIndex);
if (tensor->allocation_type == kTfLiteMmapRo) {
nnRet = m_nnrt->OH_NNModel_SetTensorData(m_nnModel, nnTensorIndex,
tensorData, tensor->bytes);
if (nnRet != OH_NN_SUCCESS) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] Fail to setting new nnTensor value.");
return kTfLiteError;
}
}
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::TransposeDepthwiseTensor(int32_t tensorIndex, OH_NN_Tensor& nnTensor,
std::vector<int32_t>& weightDims, std::vector<int8_t>& tensorData)
{
const int32_t* tensorDims = nnTensor.dimensions;
uint32_t tensorRank = nnTensor.dimensionCount;
// For Depth-wise Convolution, NNRT choose to Transpose dimension with [3, 1, 2, 0]
TF_LITE_ENSURE_STATUS(TransposeDims(m_context, tensorDims, tensorRank, DEPTHWISE_TRANSPOSE_AXISS, weightDims));
nnTensor.dimensions = weightDims.data();
TfLiteTensor* tensor = &(m_context->tensors[tensorIndex]);
if (tensor->type == kTfLiteFloat32) {
TF_LITE_ENSURE_STATUS(
TransposeTensor(m_context, tensorIndex, tensorDims, reinterpret_cast<float*>(tensorData.data())));
} else if (tensor->type == kTfLiteInt32) {
TF_LITE_ENSURE_STATUS(
TransposeTensor(m_context, tensorIndex, tensorDims, reinterpret_cast<int32_t*>(tensorData.data())));
} else if (tensor->type == kTfLiteInt8) {
TF_LITE_ENSURE_STATUS(
TransposeTensor(m_context, tensorIndex, tensorDims, reinterpret_cast<int8_t*>(tensorData.data())));
} else if (tensor->type == kTfLiteUInt8) {
TF_LITE_ENSURE_STATUS(
TransposeTensor(m_context, tensorIndex, tensorDims, reinterpret_cast<uint8_t*>(tensorData.data())));
} else {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] unsupportted weight tensor type %d.", tensor->type);
return kTfLiteError;
}
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::ConstructNNTensor(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags,
OH_NN_QuantParam& nnQuantParam, OH_NN_Tensor& nnTensor)
{
OH_NN_DataType nnType {OH_NN_UNKNOWN};
TF_LITE_ENSURE_STATUS(m_pTensorMapping->ConvertType(m_context, tensorIndex, tensorFlags, nnType));
TF_LITE_ENSURE_STATUS(m_pTensorMapping->ConvertQuantParams(m_context, tensorIndex, nnQuantParam));
TfLiteTensor* tensor = &(m_context->tensors[tensorIndex]);
uint32_t tensorRank = static_cast<uint32_t>(tensor->dims->size);
m_dimsUnspecified.assign(tensorRank, UNSPECIFIED_DIMENSION_VALUE);
int32_t* tensorDims = (m_allowDynamicDimensions && (tensor->allocation_type != kTfLiteMmapRo) &&
std::find(m_inputs.begin(), m_inputs.end(), tensorIndex) != m_inputs.end()) ?
reinterpret_cast<int32_t*>(m_dimsUnspecified.data()) :
tensor->dims->data;
const bool scalarAsTensor = tensorFlags & NN_TENSOR_FLAG_SCALAR_AS_TENSOR;
if (scalarAsTensor && tensorRank == 0) {
tensorRank = SCALAR_TENSOR_RANK; // Use rank 1, shape {1} nnTensor for TFLite scalar tensors.
tensorDims = const_cast<int32_t*>(&SCALAR_TENSOR_RANK);
}
if (tensorRank == 0) {
// if the tensorRank is 0, the dimension ptr must be nullptr.
tensorDims = nullptr;
}
nnTensor.dataType = nnType;
nnTensor.dimensionCount = tensorRank;
nnTensor.dimensions = tensorDims;
nnTensor.quantParam = nnQuantParam.quantCount ? &nnQuantParam : nullptr;
nnTensor.type = OH_NN_TENSOR;
return kTfLiteOk;
}
TfLiteStatus NnrtOpBuilder::AddOpFuncParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
if (!m_keyToOpFunc.count(builtinCode)) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] unsupportted Op builtinCode : %d.", builtinCode);
return kTfLiteError;
}
OpFuncPtr pfunc = m_keyToOpFunc[builtinCode];
return (this->*pfunc)(mappingArgs, builtinCode);
}
TfLiteStatus NnrtOpBuilder::MapBuiltinCodeToFunc()
{
m_keyToOpFunc[kTfLiteBuiltinAdd] = &NnrtOpBuilder::AddBasicComputeParams;
m_keyToOpFunc[kTfLiteBuiltinAveragePool2d] = &NnrtOpBuilder::AddAvgPoolingParams;
m_keyToOpFunc[kTfLiteBuiltinConcatenation] = &NnrtOpBuilder::AddConcatenationParams;
m_keyToOpFunc[kTfLiteBuiltinConv2d] = &NnrtOpBuilder::AddConv2DParams;
m_keyToOpFunc[kTfLiteBuiltinDepthwiseConv2d] = &NnrtOpBuilder::AddDepthwiseConv2DParams;
m_keyToOpFunc[kTfLiteBuiltinDequantize] = &NnrtOpBuilder::AddQuantizeParams;
m_keyToOpFunc[kTfLiteBuiltinFullyConnected] = &NnrtOpBuilder::AddFullConnectedParams;
m_keyToOpFunc[kTfLiteBuiltinMaxPool2d] = &NnrtOpBuilder::AddMaxPoolingParams;
m_keyToOpFunc[kTfLiteBuiltinMul] = &NnrtOpBuilder::AddBasicComputeParams;
m_keyToOpFunc[kTfLiteBuiltinSub] = &NnrtOpBuilder::AddBasicComputeParams;
m_keyToOpFunc[kTfLiteBuiltinReshape] = &NnrtOpBuilder::AddReshapeParams;
m_keyToOpFunc[kTfLiteBuiltinSoftmax] = &NnrtOpBuilder::AddSoftmaxParams;
m_keyToOpFunc[kTfLiteBuiltinStridedSlice] = &NnrtOpBuilder::AddStridedSliceParams;
m_keyToOpFunc[kTfLiteBuiltinPack] = &NnrtOpBuilder::AddPackParams;
m_keyToOpFunc[kTfLiteBuiltinPad] = &NnrtOpBuilder::AddPadParams;
m_keyToOpFunc[kTfLiteBuiltinMean] = &NnrtOpBuilder::AddReduceMeanParams;
m_keyToOpFunc[kTfLiteBuiltinQuantize] = &NnrtOpBuilder::AddQuantizeParams;
m_keyToOpFunc[kTfLiteBuiltinHardSwish] = &NnrtOpBuilder::AddDefaultOpParams;
m_keyToOpFunc[kTfLiteBuiltinShape] = &NnrtOpBuilder::AddDefaultOpParams;
m_keyToOpFunc[kTfLiteBuiltinLogistic] = &NnrtOpBuilder::AddDefaultOpParams;
return kTfLiteOk;
}
} // namespace nnrt
} // namespace delegate
} // namespace tflite
@@ -0,0 +1,325 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_OP_BUILDER_H
#define TENSORFLOW_LITE_DELEGATES_NNRT_OP_BUILDER_H
#include <vector>
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/c/builtin_op_data.h"
#include "tensorflow/lite/kernels/kernel_util.h"
#include "tensorflow/lite/minimal_logging.h"
#include "../nnrt/nnrt_implementation.h"
#include "tensor_mapping.h"
namespace tflite {
namespace delegate {
namespace nnrt {
constexpr int32_t PADDING_SAME = 0;
constexpr int32_t PADDING_VALID = 1;
// NN API Operator Builder
class NnrtOpBuilder;
// The kernel that represents the node sub set of TF Lite being run on NN API.
struct NnrtOpMappingArgs {
TfLiteContext* context {nullptr};
NnrtOpBuilder* builder {nullptr};
TfLiteNode* node {nullptr};
int32_t nodeIndex {-1};
};
struct NnrtOpBuilderArgs {
TfLiteContext* context {nullptr};
OH_NNModel* nnModel {nullptr};
TfLiteIntArray* inputTensors {nullptr};
TensorMapping* pTensorMapping {nullptr};
NnrtDelegate::Options delegateOptions;
};
// Abstract builder for building an op in the NN API graph. This handles
// the disparity between TFLite and NN API nnTensor types. NN API has singular
// nnTensors for both tensors and parameters, and TFLite separates the two.
class NnrtOpBuilder {
public:
NnrtOpBuilder(const NnrtApi* nnrt, NnrtOpBuilderArgs& opBuilderArgs);
~NnrtOpBuilder() = default;
// Add scalar nnTensor, the datatypes involved are bool, Int32, Int8, Int64, Float32
TfLiteStatus AddScalarBoolTensor(bool value, OH_NN_TensorType nnTensorType)
{
return AddScalarTensor<bool>(value, OH_NN_BOOL, nnTensorType);
}
TfLiteStatus AddScalarInt32Tensor(int32_t value, OH_NN_TensorType nnTensorType)
{
return AddScalarTensor<int32_t>(value, OH_NN_INT32, nnTensorType);
}
TfLiteStatus AddScalarInt8Tensor(int32_t value, OH_NN_TensorType nnTensorType)
{
return AddScalarTensor<int8_t>(value, OH_NN_INT8, nnTensorType);
}
TfLiteStatus AddScalarInt64Tensor(int64_t value, OH_NN_TensorType nnTensorType)
{
return AddScalarTensor<int64_t>(value, OH_NN_INT64, nnTensorType);
}
TfLiteStatus AddScalarFloat32Tensor(float value, OH_NN_TensorType nnTensorType)
{
return AddScalarTensor<float>(value, OH_NN_FLOAT32, nnTensorType);
}
// Add vector nnTensor, the datatypes involved are Int32, Int64, Int16, Int8, Float32
TfLiteStatus AddVectorInt32Tensor(const int32_t* values, uint32_t numValues, OH_NN_TensorType nnTensorType)
{
return AddVectorTensor<int32_t>(values, numValues, OH_NN_UINT32, nnTensorType);
}
TfLiteStatus AddVectorInt64Tensor(const int64_t* values, uint32_t numValues, OH_NN_TensorType nnTensorType)
{
return AddVectorTensor<int64_t>(values, numValues, OH_NN_INT64, nnTensorType);
}
TfLiteStatus AddVectorFloat32Tensor(const float* values, uint32_t numValues, OH_NN_TensorType nnTensorType)
{
return AddVectorTensor<float>(values, numValues, OH_NN_FLOAT32, nnTensorType);
}
// Add input tensor
TfLiteStatus AddTensorInput(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags = 0)
{
return AddTensor(tensorIndex, builtinCode, m_augmentedInputs, tensorFlags);
}
// Add output tensor
TfLiteStatus AddTensorOutput(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags = 0)
{
return AddTensor(tensorIndex, builtinCode, m_augmentedOutputs, tensorFlags);
}
// Finish emitting the op (of type `type`) into the NN API.
TfLiteStatus FinalizeAddOperation(OH_NN_OperationType type, int32_t liteNodeIndex);
void ClearInputOuputLists()
{
m_augmentedInputs.clear();
m_augmentedOutputs.clear();
m_augmentedParams.clear();
}
TfLiteStatus AddOpFuncParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus MapBuiltinCodeToFunc();
private:
template<typename T>
TfLiteStatus AddScalarTensor(T value, OH_NN_DataType nnType, OH_NN_TensorType nnTensorType)
{
OH_NN_Tensor tensor {
.dataType = nnType,
.dimensionCount = 0,
.dimensions = nullptr,
.quantParam = nullptr,
.type = nnTensorType,
};
RETURN_TFLITE_ERROR_IF_NN_ERROR(
m_nnrt->OH_NNModel_AddTensor(m_nnModel, &tensor), "adding nnTensor");
const int32_t nnIndex = m_pTensorMapping->AddNewNonTensorTensor();
RETURN_TFLITE_ERROR_IF_NN_ERROR(
m_nnrt->OH_NNModel_SetTensorData(m_nnModel, nnIndex, &value, sizeof(value)),
"setting new nnTensor value");
m_augmentedParams.emplace_back(nnIndex);
return kTfLiteOk;
}
template<typename T>
TfLiteStatus AddVectorTensor(const T* values, int32_t numValues, OH_NN_DataType nnType,
OH_NN_TensorType nnTensorType)
{
if (values == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-OPBUILDER] The variable of values is nullptr when adding vector to operator.");
return kTfLiteError;
}
uint32_t numBits = 8;
double doubleScale = 0.f;
int32_t zeroPoint = 0;
OH_NN_QuantParam quantParam = {
.quantCount = 1,
.numBits = &numBits,
.scale = &doubleScale,
.zeroPoint = &zeroPoint
};
OH_NN_Tensor tensor {
.dataType = nnType,
.dimensionCount = 1, // For 1-dim vector, dimensionCount is one.
.dimensions = &numValues,
.quantParam = &quantParam,
.type = nnTensorType,
};
RETURN_TFLITE_ERROR_IF_NN_ERROR(
m_nnrt->OH_NNModel_AddTensor(m_nnModel, &tensor), "adding nnTensor");
const int32_t nnIndex = m_pTensorMapping->AddNewNonTensorTensor();
RETURN_TFLITE_ERROR_IF_NN_ERROR(
m_nnrt->OH_NNModel_SetTensorData(m_nnModel, nnIndex, values, sizeof(*(values)) * numValues),
"settings new nnTensor value");
m_augmentedParams.emplace_back(nnIndex);
return kTfLiteOk;
}
template<typename T>
TfLiteStatus AddActivateParamsInOperator(const NnrtOpMappingArgs& mappingArgs, T* builtinParams,
int32_t builtinCode, OH_NN_TensorType nnTensorType)
{
if (builtinParams == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-OPBUILDER] The builtin params is nullptr when adding activate params to operator.");
return kTfLiteError;
}
if ((builtinParams->activation >= 0) &&
(builtinParams->activation < ACTIVATE_FUSE_TYPE_LIST.size()) &&
(ACTIVATE_FUSE_TYPE_LIST[builtinParams->activation] != OH_NN_FUSE_UNSUPPORTED)) {
mappingArgs.builder->AddScalarInt8Tensor(ACTIVATE_FUSE_TYPE_LIST[builtinParams->activation], nnTensorType);
} else {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-OPBUILDER] unsupportted fused activation type %d for OpType %d.",
builtinParams->activation, builtinCode);
return kTfLiteError;
}
return kTfLiteOk;
}
template<typename T>
TfLiteStatus AddPadParamsInOperator(const NnrtOpMappingArgs& mappingArgs, T* builtinParams, int32_t builtinCode,
OH_NN_TensorType nnTensorType)
{
if (builtinParams == nullptr) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-OPBUILDER] The builtin params is nullptr when adding pad params to operator.");
return kTfLiteError;
}
int32_t padding = 0;
if (builtinParams->padding == kTfLitePaddingUnknown) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] unknown padding mode for OpType %d.", builtinCode);
return kTfLiteError;
} else {
padding = (builtinParams->padding == kTfLitePaddingSame) ? PADDING_SAME : PADDING_VALID;
}
mappingArgs.builder->AddScalarInt8Tensor(padding, nnTensorType);
return kTfLiteOk;
}
// NNRT requires a bias tensor, so we allocate a new tensor to fill it with zeroes.
// It is deleted with other tensors in the context during subgraph destructor call.
TfLiteStatus AddZerosBias(const NnrtOpMappingArgs& mappingArgs, int32_t inputId, int32_t filterId,
int32_t channelNum);
TfLiteStatus AddBasicComputeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddAvgPoolingParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddMaxPoolingParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddFullConnectedParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddConv2DParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddConcatenationParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddSoftmaxParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddQuantizeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddPackParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddPadParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddReduceMeanParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddReshapeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddStridedSliceParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddDepthwiseConv2DParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
TfLiteStatus AddDefaultOpParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
{
return kTfLiteOk;
}
// Adds a new NN API tensor that shadows the TF Lite tensor `tensorIndex`.
// This restores the NN API tensor index corresponding to the created tensor.
// If another caller previously created a NN API tensor for `tensorIndex`
// then the existing one is restored.
TfLiteStatus AddTensor(int32_t tensorIndex, int32_t builtinCode, std::vector<uint32_t>& indices,
int32_t tensorFlags = 0);
// Adds a new NN API nnTensor to NNModel.
// If the builtinCode is kTfLiteBuiltinDepthwiseConv2d, the weight tensor will be transposed to CHWN format.
TfLiteStatus AddTensor(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags, int32_t& nnTensorIndex);
// Transpose dimension for Depth-wise Convolution Operator.
TfLiteStatus TransposeDepthwiseTensor(int32_t tensorIndex, OH_NN_Tensor& nnTensor, std::vector<int32_t>& destDims,
std::vector<int8_t>& tensorData);
// Get NN nnTensor from tensor
TfLiteStatus ConstructNNTensor(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags,
OH_NN_QuantParam& nnQuantParam, OH_NN_Tensor& nnTensor);
private:
// Access to NNRT.
const NnrtApi* const m_nnrt;
// TfLiteContext for error handling.
TfLiteContext* const m_context;
// Indices of all inputs of tflite subgraph.
std::vector<int32_t> m_inputs;
// Tracks relationship between indices.
TensorMapping* const m_pTensorMapping;
// The NNRT model.
OH_NNModel* const m_nnModel;
// Inputs and outputs for the current op. These are augmented in the sense
// that NN API uses nnTensors for all arguments, not just tensors, unlike
// TensorFlow Lite.
std::vector<uint32_t> m_augmentedInputs;
std::vector<uint32_t> m_augmentedParams;
std::vector<uint32_t> m_augmentedOutputs;
// Whether to allow dynamic batch size without re-compilation.
bool m_allowDynamicDimensions;
// the dynamic dimension information.
std::vector<int32_t> m_dimsUnspecified;
// key builtInCode to OpFunc Map
using OpFuncPtr = TfLiteStatus(NnrtOpBuilder::*)(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
std::map<int32_t, OpFuncPtr> m_keyToOpFunc;
};
} // namespace nnrt
} // namespace delegate
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_OP_BUILDER_H
@@ -0,0 +1,347 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nnrt_utils.h"
#include <iostream>
#include "tensorflow/lite/util.h"
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/context_util.h"
#include "tensorflow/lite/minimal_logging.h"
#include "tensorflow/lite/kernels/kernel_util.h"
#include "neural_network_runtime_type.h"
namespace tflite {
std::string NnrtErrorDescription(int32_t errorCode)
{
switch (errorCode) {
case OH_NN_SUCCESS:
return "OH_NN_SUCCESS";
case OH_NN_FAILED:
return "OH_NN_FAILED";
case OH_NN_INVALID_PARAMETER:
return "OH_NN_INVALID_PARAMETER";
case OH_NN_MEMORY_ERROR:
return "OH_NN_MEMORY_ERROR";
case OH_NN_OPERATION_FORBIDDEN:
return "OH_NN_OPERATION_FORBIDDEN";
case OH_NN_NULL_PTR:
return "OH_NN_NULL_PTR";
case OH_NN_INVALID_FILE:
return "OH_NN_INVALID_FILE";
case OH_NN_UNAVALIDABLE_DEVICE:
return "OH_NN_UNAVALIDABLE_DEVICE";
case OH_NN_INVALID_PATH:
return "OH_NN_INVALID_PATH";
default:
return "Unknown NNRT error code: " + std::to_string(errorCode);
}
}
bool IsFloat(TfLiteType type)
{
return type == kTfLiteFloat32;
}
bool IsQuantized(TfLiteType type)
{
return ((type == kTfLiteUInt8) || (type == kTfLiteInt8));
}
bool IsScalarInputSupported(int32_t builtinCode)
{
switch (builtinCode) {
case kTfLiteBuiltinAdd:
case kTfLiteBuiltinMul:
case kTfLiteBuiltinSub:
case kTfLiteBuiltinDiv:
case kTfLiteBuiltinEqual:
case kTfLiteBuiltinNotEqual:
case kTfLiteBuiltinGreater:
case kTfLiteBuiltinGreaterEqual:
case kTfLiteBuiltinLess:
case kTfLiteBuiltinLessEqual:
case kTfLiteBuiltinPow:
case kTfLiteBuiltinMaximum:
case kTfLiteBuiltinMinimum:
case kTfLiteBuiltinPrelu:
case kTfLiteBuiltinLeakyRelu:
return true;
default:
return false;
}
}
bool IsUseTargetDevice(NnrtDelegate::Options delegateOptions, bool excludeNnrtReference)
{
const std::string& deviceName = delegateOptions.acceleratorName;
bool hasSelectedAccelerator = !deviceName.empty();
if (!excludeNnrtReference && hasSelectedAccelerator) {
if (!deviceName.compare(NNRT_REFERENCE_DEVICE)) {
hasSelectedAccelerator = false;
}
}
return hasSelectedAccelerator;
}
TfLiteStatus GetTargetDevice(TfLiteContext* context, TfLiteDelegate* delegate, const NnrtApi* nnrt, size_t& dev)
{
TF_LITE_ENSURE_EQ(context, nnrt != nullptr, true);
TF_LITE_ENSURE_EQ(context, delegate != nullptr, true);
NnrtDelegate::Options delegateOptions;
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(delegate, delegateOptions));
const std::string& deviceName = delegateOptions.acceleratorName;
uint32_t numDevices {0};
const size_t* alldevicesID {nullptr};
RETURN_TFLITE_ERROR_IF_NN_ERROR(nnrt->OH_NNDevice_GetAllDevicesID(&alldevicesID, &numDevices),
"Get available device number and deviceID.");
if (numDevices == 0) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-UTILS] Have no available device.");
return kTfLiteError;
}
std::vector<OH_NN_DeviceType> deviceTypes;
for (uint32_t i = 0; i < numDevices; ++i) {
OH_NN_DeviceType tempDeviceType {OH_NN_ACCELERATOR};
RETURN_TFLITE_ERROR_IF_NN_ERROR(nnrt->OH_NNDevice_GetType(alldevicesID[i], &tempDeviceType),
"Get available devicesType.");
deviceTypes.emplace_back(tempDeviceType);
}
OH_NN_DeviceType deviceType {OH_NN_CPU};
std::vector<OH_NN_DeviceType>::iterator pos = std::find(deviceTypes.begin(), deviceTypes.end(), deviceType);
if (pos != deviceTypes.end()) {
int index = distance(deviceTypes.begin(), pos);
dev = alldevicesID[index];
} else {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-UTILS] Cannot find the %s device, please choose another process unit.",
deviceName.c_str());
return kTfLiteError;
}
return kTfLiteOk;
}
TfLiteStatus TransposeDims(TfLiteContext* context, const int32_t* dims, uint32_t dimCount,
std::vector<int32_t> destAxis, std::vector<int32_t>& weightDims)
{
TF_LITE_ENSURE_EQ(context, dims != nullptr, true);
if (dimCount != destAxis.size()) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-UTILS] Invalid dimension count %d.", dimCount);
return kTfLiteError;
}
for (auto axis : destAxis) {
if (axis < 0) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-UTILS] Invalid axis %d.", axis);
return kTfLiteError;
}
weightDims.emplace_back(*(dims + axis));
}
return kTfLiteOk;
}
TfLiteStatus GetTensorSize(TfLiteContext* context, const int32_t* dims, int32_t dimCount, int64_t& tensorSize)
{
TF_LITE_ENSURE_EQ(context, dims != nullptr, true);
if (dimCount != DEPTHWISE_WEIGHT_DIMENSION_COUNT) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[NNRT-UTILS] Dimension count is not equal to destination axis number, should be 4.");
return kTfLiteError;
}
tensorSize = 1;
for (int32_t i = 0; i < dimCount; ++i) {
if (*(dims + i) <= 0) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-UTILS] Get invalid dimenision.");
return kTfLiteError;
}
tensorSize *= *(dims + i);
}
return kTfLiteOk;
}
namespace delegate {
namespace nnrt {
const std::vector<int32_t> ACTIVATE_FUSE_TYPE_LIST = {
OH_NN_FUSED_NONE,
OH_NN_FUSED_RELU,
OH_NN_FUSE_UNSUPPORTED,
OH_NN_FUSED_RELU6,
OH_NN_FUSE_UNSUPPORTED,
OH_NN_FUSE_UNSUPPORTED,
OH_NN_FUSE_UNSUPPORTED
};
const unorderedTypeMap TFLITE_TYPE_TO_NNRT_TYPE = {
{kTfLiteBuiltinAdd, OH_NN_OPS_ADD},
{kTfLiteBuiltinAveragePool2d, OH_NN_OPS_AVG_POOL},
{kTfLiteBuiltinConcatenation, OH_NN_OPS_CONCAT},
{kTfLiteBuiltinConv2d, OH_NN_OPS_CONV2D},
{kTfLiteBuiltinDepthwiseConv2d, OH_NN_OPS_DEPTHWISE_CONV2D_NATIVE},
{kTfLiteBuiltinDepthToSpace, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinDequantize, OH_NN_OPS_QUANT_DTYPE_CAST},
{kTfLiteBuiltinEmbeddingLookup, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinFloor, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinFullyConnected, OH_NN_OPS_FULL_CONNECTION},
{kTfLiteBuiltinHashtableLookup, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinL2Normalization, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinL2Pool2d, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLocalResponseNormalization, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLogistic, OH_NN_OPS_SIGMOID},
{kTfLiteBuiltinLshProjection, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLstm, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinMaxPool2d, OH_NN_OPS_MAX_POOL},
{kTfLiteBuiltinMul, OH_NN_OPS_MUL},
{kTfLiteBuiltinRelu, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReluN1To1, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinRelu6, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReshape, OH_NN_OPS_RESHAPE},
{kTfLiteBuiltinResizeBilinear, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinRnn, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSoftmax, OH_NN_OPS_SOFTMAX},
{kTfLiteBuiltinSpaceToDepth, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSvdf, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinTanh, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinConcatEmbeddings, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSkipGram, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinCall, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinCustom, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinEmbeddingLookupSparse, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinPad, OH_NN_OPS_PAD},
{kTfLiteBuiltinUnidirectionalSequenceRnn, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinGather, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinBatchToSpaceNd, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSpaceToBatchNd, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinTranspose, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinMean, OH_NN_OPS_REDUCE_MEAN},
{kTfLiteBuiltinSub, OH_NN_OPS_SUB},
{kTfLiteBuiltinDiv, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSqueeze, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinUnidirectionalSequenceLstm, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinStridedSlice, OH_NN_OPS_STRIDED_SLICE},
{kTfLiteBuiltinBidirectionalSequenceRnn, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinExp, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinTopkV2, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSplit, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLogSoftmax, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinDelegate, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinBidirectionalSequenceLstm, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinCast, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinPrelu, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinMaximum, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinArgMax, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinMinimum, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLess, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinNeg, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinPadv2, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinGreater, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinGreaterEqual, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLessEqual, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSelect, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSlice, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSin, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinTransposeConv, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSparseToDense, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinTile, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinExpandDims, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinEqual, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinNotEqual, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLog, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSum, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSqrt, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinRsqrt, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinShape, OH_NN_OPS_SHAPE},
{kTfLiteBuiltinPow, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinArgMin, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinFakeQuant, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReduceProd, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReduceMax, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinPack, OH_NN_OPS_STACK},
{kTfLiteBuiltinLogicalOr, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinOneHot, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLogicalAnd, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLogicalNot, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinUnpack, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReduceMin, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinFloorDiv, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReduceAny, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSquare, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinZerosLike, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinFill, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinFloorMod, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinRange, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinResizeNearestNeighbor, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinLeakyRelu, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSquaredDifference, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinMirrorPad, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinAbs, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSplitV, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinUnique, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinCeil, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReverseV2, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinAddN, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinGatherNd, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinCos, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinWhere, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinRank, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinElu, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReverseSequence, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinMatrixDiag, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinQuantize, OH_NN_OPS_QUANT_DTYPE_CAST},
{kTfLiteBuiltinMatrixSetDiag, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinRound, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinHardSwish, OH_NN_OPS_HSWISH},
{kTfLiteBuiltinIf, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinWhile, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinNonMaxSuppressionV4, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinNonMaxSuppressionV5, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinScatterNd, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSelectV2, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinDensify, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinSegmentSum, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinBatchMatmul, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinPlaceholderForGreaterOpCodes, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinCumsum, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinCallOnce, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinBroadcastTo, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinRfft2d, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinConv3d, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinImag, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReal, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinComplexAbs, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinHashtable, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinHashtableFind, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinHashtableImport, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinHashtableSize, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReduceAll, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinConv3dTranspose, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinVarHandle, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinReadVariable, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinAssignVariable, OH_NN_UNSUPPORT_OPS},
{kTfLiteBuiltinBroadcastTo, OH_NN_UNSUPPORT_OPS},
};
} // nnrt
} // namespace
} // namespace tflite
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_UTILS_H
#define TENSORFLOW_LITE_DELEGATES_NNRT_UTILS_H
#include <map>
#include <vector>
#include <unordered_map>
#include "nnrt_delegate.h"
namespace tflite {
constexpr int32_t DEPTHWISE_WEIGHT_BATCH_DIMENSION = 0;
constexpr int32_t DEPTHWISE_WEIGHT_HEIGHT_DIMENSION = 1;
constexpr int32_t DEPTHWISE_WEIGHT_WIDTH_DIMENSION = 2;
constexpr int32_t DEPTHWISE_WEIGHT_CHANNEL_DIMENSION = 3;
constexpr int32_t DEPTHWISE_WEIGHT_DIMENSION_COUNT = 4;
const std::string NNRT_REFERENCE_DEVICE = "nnrt-reference";
// Bit mask for tensor flags.
enum BIT_MASK {
NN_TENSOR_FLAG_SCALAR_AS_TENSOR = 1U << 0,
NN_TENSOR_FLAG_INT8_CONVERSION = 1U << 1,
NN_TENSOR_FLAG_USE_INT8_ASYMM_SIGNED = 1U << 2,
NN_TENSOR_FLAG_FORCE_PER_CHANNEL = 1U << 3,
NN_TENSOR_FLAG_HALF_TO_FLOAT_CONVERSION = 1U << 4,
};
// Returns the enum name corresponding to the given error code if the given
// value corresponds to an of the error codes in the enumeration above or
// an message with the unknown code.
// LINT.IfChange(NnrtErrorDescription)
extern std::string NnrtErrorDescription(int32_t errorCode);
#define RETURN_TFLITE_ERROR_IF_NN_ERROR(code, callDesc) \
do { \
if ((code) != OH_NN_SUCCESS) { \
const auto errorDesc = NnrtErrorDescription((code)); \
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "NN API returned error %s at line %d while %s.\n", \
errorDesc.c_str(), __LINE__, (callDesc)); \
return kTfLiteError; \
} \
} while (0)
#define RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_TENSOR(code, callDesc, pTensor) \
do { \
if ((code) != OH_NN_SUCCESS) { \
const auto errorDesc = NnrtErrorDescription((code)); \
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, \
"NN API returned error %s at line %d while %s for tensor '%s'.\n", errorDesc.c_str(), \
__LINE__, (callDesc), (pTensor)->name ? (pTensor)->name : "no-name"); \
return kTfLiteError; \
} \
} while (0)
// Return true if type is kTfLiteFloat32.
extern bool IsFloat(TfLiteType type);
// Return true if type is kTfLiteUInt8 or kTfLiteInt8.
extern bool IsQuantized(TfLiteType type);
// Return true if the operator supports scalar data as input.
extern bool IsScalarInputSupported(int32_t builtinCode);
// Returns true if this delegate is configured to use a specific set of devices.
// If the acceleratorName in the delegate options is equal to "nnrt-reference"
// this method will return true only if the excludeNnrtReference is true.
extern bool IsUseTargetDevice(
NnrtDelegate::Options delegateOptions, bool excludeNnrtReference = false);
// Fills the given result vector with the list of devices the given delegate
// is referring to.
// There are three possible results,
// - An empty array (not the full list of available accelerators,
// for efficiency reasons) if no accelerator is chosen and the
// disallowNnrtCpu delegate option is false.
// - A single element array with the target processor, if an accelerator name
// is specified in the delegate options.
// - The target available device on device.
extern TfLiteStatus GetTargetDevice(TfLiteContext* context, TfLiteDelegate* delegate,
const NnrtApi* nnrt, size_t& dev);
// Transpose demension following fixed axis.
// If exist -1 in destAxis, return kTfLiteError.
extern TfLiteStatus TransposeDims(TfLiteContext* context, const int32_t* dims, uint32_t dimCount,
std::vector<int32_t> destAxis, std::vector<int32_t>& weightDims);
// Get Tensor size by byte.
// Calculate Tesnorsize by mul all dimension in dims.
// Return kTfLiteError if element dimension is less 0.
extern TfLiteStatus GetTensorSize(TfLiteContext* context, const int32_t* dims, int32_t dimCount, int64_t& tensorSize);
// Transpose dimension for Tensor.
// Only change NHWC format tensor to CHWN format tensor, and
// the capacity of result vec must equal to input tensor size.
template <class T>
TfLiteStatus TransposeTensor(TfLiteContext* context, int32_t tensorIndex, const int32_t* dims,
T* transposeTensor)
{
TF_LITE_ENSURE_EQ(context, dims != nullptr, true);
// NHWC -> CHWN
TfLiteTensor* tensor = &(context->tensors[tensorIndex]);
const T* tensorData = reinterpret_cast<T*>(tensor->data.data);
const int32_t batch = dims[DEPTHWISE_WEIGHT_BATCH_DIMENSION];
const int32_t height = dims[DEPTHWISE_WEIGHT_HEIGHT_DIMENSION];
const int32_t width = dims[DEPTHWISE_WEIGHT_WIDTH_DIMENSION];
const int32_t channel = dims[DEPTHWISE_WEIGHT_CHANNEL_DIMENSION];
for (int32_t c = 0; c < channel; ++c) {
for (int32_t j = 0; j < height * width; ++j) {
for (int32_t n = 0; n < batch; ++n) {
int32_t newPos = c * (height * width) * batch + j * batch + n;
int32_t orgPos = n * (height * width) * channel + j * channel + c;
*(transposeTensor + newPos) = *(tensorData + orgPos);
}
}
}
return kTfLiteOk;
};
namespace delegate {
namespace nnrt {
using unorderedTypeMap = std::unordered_map<int32_t, int32_t>;
extern const std::vector<int32_t> ACTIVATE_FUSE_TYPE_LIST;
extern const unorderedTypeMap TFLITE_TYPE_TO_NNRT_TYPE;
const int32_t INVALID_INDEX = -1;
const int32_t OH_NN_UNSUPPORT_OPS = -1;
const int32_t OH_NN_FUSE_UNSUPPORTED = -1;
} // namespace nnrt
} // namespace delegate
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_UTILS_H
@@ -0,0 +1,177 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_TENSOR_MAPPING_H
#define TENSORFLOW_LITE_DELEGATES_NNRT_TENSOR_MAPPING_H
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/minimal_logging.h"
#include "nnrt_utils.h"
namespace tflite {
namespace delegate {
namespace nnrt {
constexpr uint32_t QUANT_NUMBITS = 8;
class TensorMapping {
public:
// Given a TFLite index return the NN index. If it doesn't exist
// return -1.
int32_t LiteIndexToNn(int32_t index) const
{
const int64_t maxSize = m_liteTensorToNnTensor.size();
if (index >= 0 && index < maxSize) {
return m_liteTensorToNnTensor[index];
} else {
return INVALID_INDEX;
}
}
// NN API uses non tensor tensors instead of structs. This creates one
// and returns the index. It uses a std::vector and resizes it as needed
// keeping -1 to unmapped values. Intermediate tensors likely will not
// be mapped.
const int32_t AddNewNonTensorTensor()
{
return m_nextNnTensorIndex++;
}
// Add a new mapping from `tfliteIndex` and return the NN API tensor index.
int32_t AddNewNnTensorIndex(int32_t tfliteIndex)
{
const int64_t currentSize = m_liteTensorToNnTensor.size();
if (tfliteIndex >= currentSize) {
m_liteTensorToNnTensor.resize(tfliteIndex + 1, INVALID_INDEX);
}
const int32_t newTensorIndex = m_nextNnTensorIndex++;
m_liteTensorToNnTensor[tfliteIndex] = newTensorIndex;
return newTensorIndex;
}
// Get nn tensor tensor tensor num.
int32_t GetTensorTensorNum() const
{
return m_nextNnTensorIndex;
}
// Given a TFLite index returns a TFLite type to which a tensor must be
// converted during copying the data to the memory allocated for NN API.
// kTfLiteNoType means no conversion is needed.
TfLiteType GetEqualLiteTypeFromLiteIndex(int32_t index) const
{
const int64_t maxSize = m_indexToTypeConversion.size();
if (index >= 0 && index < maxSize)
return m_indexToTypeConversion[index];
else
return kTfLiteNoType;
}
// Add a new mapping from TFLite index to a type conversion.
void AddTypeConversion(int32_t tfliteIndex, TfLiteType tfliteType)
{
const int64_t currentSize = m_indexToTypeConversion.size();
if (tfliteIndex >= currentSize) {
m_indexToTypeConversion.resize(tfliteIndex + 1, kTfLiteNoType);
}
m_indexToTypeConversion[tfliteIndex] = tfliteType;
}
// Convert TFLite tensor quant params to NNRT tensor quant params
TfLiteStatus ConvertQuantParams(TfLiteContext* context, int32_t tensorIndex, OH_NN_QuantParam& quantParam)
{
TfLiteTensor* tensor = &(context->tensors[tensorIndex]);
TfLiteType tfType = tensor->type;
if ((tfType != kTfLiteFloat32) && (tfType != kTfLiteFloat16) && (tfType != kTfLiteBool) &&
(tfType != kTfLiteInt32) && (tfType != kTfLiteUInt8) && (tfType != kTfLiteInt8)) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[TENSOR_MAPPING] type %s is not supported.", TfLiteTypeGetName(tensor->type));
return kTfLiteError;
}
if (tensor->quantization.type) {
TfLiteAffineQuantization* params = reinterpret_cast<TfLiteAffineQuantization*>(tensor->quantization.params);
int number = params->scale->size;
std::vector<double> scale;
for (int i = 0; i < number; ++i) {
scale.emplace_back(static_cast<double>(params->scale->data[i]));
}
m_scale.emplace_back(scale);
quantParam.scale = m_scale.back().data();
quantParam.zeroPoint = params->zero_point->data;
quantParam.quantCount = number;
m_numBits.emplace_back(number, QUANT_NUMBITS);
quantParam.numBits = m_numBits.back().data();
} else {
quantParam.quantCount = 0;
}
return kTfLiteOk;
}
// Convert TFLite tensor type to NNRT tensor type
TfLiteStatus ConvertType(TfLiteContext* context, int32_t tensorIndex, int32_t tensorFlags, OH_NN_DataType& nnType)
{
const bool scalarAsTensor = tensorFlags & NN_TENSOR_FLAG_SCALAR_AS_TENSOR;
TfLiteTensor* tensor = &(context->tensors[tensorIndex]);
TfLiteType nnTypeEquivalent = GetEqualLiteTypeFromLiteIndex(tensorIndex);
if (tensor->type == kTfLiteFloat32) {
nnType = OH_NN_FLOAT32;
} else if (tensor->type == kTfLiteFloat16) {
nnType = OH_NN_FLOAT16;
if (scalarAsTensor) {
nnType = OH_NN_FLOAT32;
AddTypeConversion(tensorIndex, kTfLiteFloat32);
}
} else if (tensor->type == kTfLiteInt32) {
nnType = OH_NN_INT32;
} else if (tensor->type == kTfLiteBool) {
nnType = OH_NN_INT8;
} else if (tensor->type == kTfLiteUInt8) {
nnType = (nnTypeEquivalent == kTfLiteInt32) ? OH_NN_INT32 : OH_NN_INT8;
} else if (tensor->type == kTfLiteInt8) {
nnType = (nnTypeEquivalent == kTfLiteInt32) ? OH_NN_INT32 : OH_NN_UINT8;
} else {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
"[TENSOR_MAPPING] type %s is not supported.", TfLiteTypeGetName(tensor->type));
return kTfLiteError;
}
return kTfLiteOk;
}
private:
// Next index of nnrt tensor
int32_t m_nextNnTensorIndex = 0;
// Mapping from lite index. Use a std::vector for speed and code size
// rather than a map.
std::vector<int32_t> m_liteTensorToNnTensor;
// Mapping from lite index to a type which tensor must be converted to during
// the copying of the data to the memory allocated for NN API. kTfLiteNoType
// means no conversion is needed. Use an std::vector for speed and code size
// rather than a map.
std::vector<TfLiteType> m_indexToTypeConversion;
std::vector<std::vector<uint32_t>> m_numBits;
std::vector<std::vector<double>> m_scale;
};
} // namespace nnrt
} // namespace delegate
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_TENSOR_MAPPING_H
@@ -0,0 +1,39 @@
#
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Header path
set(OHOS_INC ${LOCAL_DIRECTORY_PATH}/../../interfaces/kits/c)
set(TOOLS_INC ${LOCAL_DIRECTORY_PATH}/tflite/tools)
set(TFLITE_INC ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite/include)
set(TFLITE_FLATBUFFER_INC ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite/include/tensorflow/lite)
include_directories(${NNRT_DEMO_HOME} ${TFLITE_INC} ${OHOS_INC} ${TOOLS_INC} ${TFLITE_FLATBUFFER_INC} ${LOCAL_DIRECTORY_PATH})
# Scr path
aux_source_directory(${NNRT_DEMO_HOME} NNRT_DEMO_SRCS)
file(GLOB TOOLS_SRCS "${TOOLS_INC}/*.cpp")
LINK_DIRECTORIES(${TFLITE_LIB_PATH}/com/arm64-v8a/lib/)
add_executable(label_classify ${NNRT_DEMO_SRCS} ${TOOLS_SRCS})
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-z,relro,-z,now,-z,noexecstack")
target_link_libraries(label_classify ${LOCAL_DIRECTORY_PATH}/lib/libnnrt_implementation.so)
target_link_libraries(label_classify ${LOCAL_DIRECTORY_PATH}/lib/libnnrt_delegate.so)
target_link_libraries(label_classify -ltensorflow-lite)
set (EXECUTABLE_OUTPUT_PATH ${LOCAL_DIRECTORY_PATH}/output)
@@ -0,0 +1,341 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "label_classify.h"
#include <fcntl.h>
#include <getopt.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>
#include "tensorflow/lite/kernels/register.h"
#include "tensorflow/lite/optional_debug_tools.h"
#include "tensorflow/lite/string_util.h"
#include "tensorflow/lite/tools/command_line_flags.h"
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
#include "log.h"
#include "utils.h"
namespace tflite {
namespace label_classify {
using TfLiteDelegatePtr = tflite::Interpreter::TfLiteDelegatePtr;
using ProvidedDelegateList = tflite::tools::ProvidedDelegateList;
constexpr int BASE_NUMBER = 10;
constexpr int CONVERSION_RATE = 1000;
static struct option LONG_OPTIONS[] = {
{"help", no_argument, nullptr, 'h'},
{"use_nnrt", required_argument, nullptr, 'a'},
{"count", required_argument, nullptr, 'c'},
{"image", required_argument, nullptr, 'i'},
{"labels", required_argument, nullptr, 'l'},
{"tflite_model", required_argument, nullptr, 'm'},
{"num_results", required_argument, nullptr, 'n'},
{"input_mean", required_argument, nullptr, 'b'},
{"input_std", required_argument, nullptr, 's'},
{"verbose", required_argument, nullptr, 'v'},
{"warmup_nums", required_argument, nullptr, 'w'},
{"print_result", required_argument, nullptr, 'z'},
{"input_shape", required_argument, nullptr, 'p'},
{nullptr, 0, nullptr, 0},
};
class DelegateProviders {
public:
DelegateProviders() : m_delegateListUtil(&params)
{
m_delegateListUtil.AddAllDelegateParams(); // Add all registered delegate params to the contained 'params_'.
}
~DelegateProviders() {}
bool InitFromCmdlineArgs(int32_t* argc, const char** argv)
{
std::vector<tflite::Flag> flags;
m_delegateListUtil.AppendCmdlineFlags(&flags);
const bool parseResult = Flags::Parse(argc, argv, flags);
if (!parseResult) {
std::string usage = Flags::Usage(argv[0], flags);
LOG(ERROR) << usage;
}
return parseResult;
}
void MergeSettingsIntoParams(const Settings& settings)
{
if (settings.accel) {
if (!params.HasParam("use_nnrt")) {
LOG(WARN) << "NNRT deleate execution provider isn't linked or NNRT "
<< "delegate isn't supported on the platform!";
} else {
params.Set<bool>("use_nnrt", true);
}
}
}
std::vector<ProvidedDelegateList::ProvidedDelegate> CreateAllDelegates() const
{
return m_delegateListUtil.CreateAllRankedDelegates();
}
private:
// Contain delegate-related parameters that are initialized from command-line flags.
tflite::tools::ToolParams params;
// A helper to create TfLite delegates.
ProvidedDelegateList m_delegateListUtil;
};
void PrepareModel(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter,
DelegateProviders& delegateProviders)
{
const std::vector<int32_t> inputs = interpreter->inputs();
const std::vector<int32_t> outputs = interpreter->outputs();
if (settings.verbose) {
LOG(INFO) << "number of inputs: " << inputs.size();
LOG(INFO) << "number of outputs: " << outputs.size();
}
std::map<int, std::vector<int>> neededInputShapes;
if (settings.inputShape != "") {
if (FilterDynamicInputs(settings, interpreter, neededInputShapes) != kTfLiteOk) {
return;
}
}
delegateProviders.MergeSettingsIntoParams(settings);
auto delegates = delegateProviders.CreateAllDelegates();
for (auto& delegate : delegates) {
const auto delegateName = delegate.provider->GetName();
if (interpreter->ModifyGraphWithDelegate(std::move(delegate.delegate)) != kTfLiteOk) {
LOG(ERROR) << "Failed to apply " << delegateName << " delegate.";
return;
} else {
LOG(INFO) << "Applied " << delegateName << " delegate.";
}
}
if (settings.inputShape != "") {
for (const auto& inputShape : neededInputShapes) {
if (IsEqualShape(inputShape.first, inputShape.second, interpreter)) {
LOG(WARNING) << "The input shape is same as the model shape, not resize.";
continue;
}
if (interpreter->ResizeInputTensor(inputShape.first, inputShape.second) != kTfLiteOk) {
LOG(ERROR) << "Fail to resize index " << inputShape.first << ".";
return;
} else {
LOG(INFO) << "Susccess to resize index " << inputShape.first << ".";
}
}
}
if (interpreter->AllocateTensors() != kTfLiteOk) {
LOG(ERROR) << "Failed to allocate tensors!";
return;
}
if (settings.verbose) {
PrintInterpreterState(interpreter.get());
}
}
void LogInterpreterParams(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter)
{
if (!interpreter) {
LOG(ERROR) << "Failed to construct interpreter";
return;
}
if (settings.verbose) {
LOG(INFO) << "tensors size: " << interpreter->tensors_size();
LOG(INFO) << "nodes size: " << interpreter->nodes_size();
LOG(INFO) << "inputs: " << interpreter->inputs().size();
LOG(INFO) << "input(0) name: " << interpreter->GetInputName(0);
size_t tSize = interpreter->tensors_size();
for (size_t i = 0; i < tSize; ++i) {
if (interpreter->tensor(i)->name) {
LOG(INFO) << i << ": " << interpreter->tensor(i)->name << ", " << interpreter->tensor(i)->bytes <<
", " << interpreter->tensor(i)->type << ", " << interpreter->tensor(i)->params.scale << ", " <<
interpreter->tensor(i)->params.zero_point;
}
}
}
}
void InferenceModel(Settings& settings, DelegateProviders& delegateProviders)
{
if (!settings.modelName.c_str()) {
LOG(ERROR) << "no model file name";
return;
}
std::unique_ptr<tflite::FlatBufferModel> model;
std::unique_ptr<tflite::Interpreter> interpreter;
model = tflite::FlatBufferModel::BuildFromFile(settings.modelName.c_str());
if (!model) {
LOG(ERROR) << "Failed to mmap model " << settings.modelName;
return;
}
settings.model = model.get();
model->error_reporter();
tflite::ops::builtin::BuiltinOpResolver resolver;
tflite::InterpreterBuilder(*model, resolver)(&interpreter);
if (!interpreter) {
LOG(ERROR) << "Failed to construct interpreter, please check the model.";
return;
}
LogInterpreterParams(settings, interpreter);
// set settings input type
PrepareModel(settings, interpreter, delegateProviders);
std::vector<int> imageSize { 224, 224, 3};
ImportData(settings, imageSize, interpreter);
if (settings.loopCount > 0 && settings.numberOfWarmupRuns > 0) {
LOG(INFO) << "Warm-up for " << settings.numberOfWarmupRuns << " times";
for (int32_t i = 0; i < settings.numberOfWarmupRuns; ++i) {
if (interpreter->Invoke() != kTfLiteOk) {
LOG(ERROR) << "Failed to invoke tflite!";
return;
}
}
}
struct timeval startTime, stopTime;
LOG(INFO) << "Invoke for " << settings.loopCount << " times";
gettimeofday(&startTime, nullptr);
for (int32_t i = 0; i < settings.loopCount; ++i) {
if (interpreter->Invoke() != kTfLiteOk) {
LOG(ERROR) << "Failed to invoke tflite!";
return;
}
}
gettimeofday(&stopTime, nullptr);
LOG(INFO) << "invoked, average time: " <<
(GetUs(stopTime) - GetUs(startTime)) / (settings.loopCount * CONVERSION_RATE) << " ms";
AnalysisResults(settings, interpreter);
}
void DisplayUsage()
{
LOG(INFO) << "label_classify\n"
<< "\t--help, -h: show the usage of the demo\n"
<< "\t--use_nnrt, -a: [0|1], use NNRT or not\n"
<< "\t--input_mean, -b: input mean\n"
<< "\t--count, -c: loop interpreter->Invoke() for certain times\n"
<< "\t--image, -i: image_name.bmp\n"
<< "\t--labels, -l: labels for the model\n"
<< "\t--tflite_model, -m: modelName.tflite\n"
<< "\t--num_results, -n: number of results to show\n"
<< "\t--input_std, -s: input standard deviation\n"
<< "\t--verbose, -v: [0|1] print more information\n"
<< "\t--warmup_nums, -w: number of warmup runs\n"
<< "\t--print_result, -z: flag to print results\n"
<< "\t--input_shape, -p: Indicates the specified dynamic input node and the corresponding shape.\n";
}
void InitSettings(int32_t argc, char** argv, Settings& settings)
{
// getopt_long stores the option index here.
int32_t optionIndex = 0;
while ((optionIndex = getopt_long(argc, argv, "a:b:c:h:i:l:m:n:p:s:v:w:z:", LONG_OPTIONS, nullptr)) != -1) {
switch (optionIndex) {
case 'a':
settings.accel = strtol(optarg, nullptr, BASE_NUMBER);
break;
case 'b':
settings.inputMean = strtod(optarg, nullptr);
break;
case 'c':
settings.loopCount = strtol(optarg, nullptr, BASE_NUMBER);
break;
case 'i':
settings.inputBmpName = optarg;
break;
case 'l':
settings.labelsFileName = optarg;
break;
case 'm':
settings.modelName = optarg;
break;
case 'n':
settings.numberOfResults = strtol(optarg, nullptr, BASE_NUMBER);
break;
case 'p':
settings.inputShape = optarg;
break;
case 's':
settings.inputStd = strtod(optarg, nullptr);
break;
case 'v':
settings.verbose = strtol(optarg, nullptr, BASE_NUMBER);
break;
case 'w':
settings.numberOfWarmupRuns = strtol(optarg, nullptr, BASE_NUMBER);
break;
case 'z':
settings.printResult = strtol(optarg, nullptr, BASE_NUMBER);
break;
case 'h':
case '?':
// getopt_long already printed an error message.
DisplayUsage();
return;
default:
return;
}
}
}
int32_t Main(int32_t argc, char** argv)
{
DelegateProviders delegateProviders;
bool parseResult = delegateProviders.InitFromCmdlineArgs(&argc, const_cast<const char**>(argv));
if (!parseResult) {
return EXIT_FAILURE;
}
Settings settings;
InitSettings(argc, argv, settings);
InferenceModel(settings, delegateProviders);
return 0;
}
} // namespace label_classify
} // namespace tflite
int32_t main(int32_t argc, char** argv)
{
return tflite::label_classify::Main(argc, argv);
}
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_H
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_H
#include <iostream>
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/string_type.h"
#include "tensorflow/lite/c/c_api_types.h"
namespace tflite {
namespace label_classify {
struct Settings {
tflite::FlatBufferModel* model;
bool verbose = false;
bool accel = false;
bool printResult = false;
TfLiteType inputType = kTfLiteFloat32;
int32_t loopCount = 1;
float inputMean = 127.5f;
float inputStd = 127.5f;
string modelName = "./mbv2.tflite";
string inputBmpName = "./grace_hopper.bmp";
string labelsFileName = "./labels.txt";
string inputShape = "";
int32_t numberOfThreads = 1;
int32_t numberOfResults = 5;
int32_t numberOfWarmupRuns = 0;
};
} // namespace label_classify
} // namespace tflite
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_H
@@ -0,0 +1,26 @@
#
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Header path
set(OHOS_INC ${LOCAL_DIRECTORY_PATH}/../../interfaces/kits/c)
include_directories(${NNRT_INTERFACE_HOME} ${OHOS_INC})
# Scr path
file(GLOB NNRT_SRCS "${NNRT_INTERFACE_HOME}/*.cpp")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LOCAL_DIRECTORY_PATH}/lib)
add_library(nnrt_implementation SHARED ${NNRT_SRCS})
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nnrt_implementation.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
#include <algorithm>
#include <string>
namespace tflite {
// These function parameters are guaranteed to be nullptr by the caller
template<class T>
void LoadFunction(void* handle, const char* name, T* nnrtFunction)
{
if (name == nullptr) {
NNRT_LOG("nnrt error: the function %s does not exist.", name);
return;
}
void* fn = dlsym(handle, name);
if (fn == nullptr) {
NNRT_LOG("nnrt error: unable to open function %s", name);
return;
}
*nnrtFunction = reinterpret_cast<T>(fn);
return;
}
const NnrtApi LoadNnrt()
{
NnrtApi nnrt;
nnrt.nnrtExists = false;
void* libNeuralNetworks = nullptr;
// Assumes there can be multiple instances of NN API
static std::string nnrtLibraryName = "libneural_network_runtime.z.so";
libNeuralNetworks = dlopen(nnrtLibraryName.c_str(), RTLD_LAZY | RTLD_NODELETE);
if (libNeuralNetworks == nullptr) {
NNRT_LOG("nnrt error: unable to open library %s", nnrtLibraryName.c_str());
return nnrt;
} else {
nnrt.nnrtExists = true;
}
// NNModel
LoadFunction(libNeuralNetworks, "OH_NNModel_Construct", &nnrt.OH_NNModel_Construct);
LoadFunction(libNeuralNetworks, "OH_NNModel_AddTensor", &nnrt.OH_NNModel_AddTensor);
LoadFunction(libNeuralNetworks, "OH_NNModel_SetTensorData", &nnrt.OH_NNModel_SetTensorData);
LoadFunction(libNeuralNetworks, "OH_NNModel_AddOperation", &nnrt.OH_NNModel_AddOperation);
LoadFunction(libNeuralNetworks, "OH_NNModel_SpecifyInputsAndOutputs", &nnrt.OH_NNModel_SpecifyInputsAndOutputs);
LoadFunction(libNeuralNetworks, "OH_NNModel_Finish", &nnrt.OH_NNModel_Finish);
LoadFunction(libNeuralNetworks, "OH_NNModel_Destroy", &nnrt.OH_NNModel_Destroy);
LoadFunction(libNeuralNetworks, "OH_NNModel_GetAvailableOperations", &nnrt.OH_NNModel_GetAvailableOperations);
// NNCompilation
LoadFunction(libNeuralNetworks, "OH_NNCompilation_Construct", &nnrt.OH_NNCompilation_Construct);
LoadFunction(libNeuralNetworks, "OH_NNCompilation_SetDevice", &nnrt.OH_NNCompilation_SetDevice);
LoadFunction(libNeuralNetworks, "OH_NNCompilation_SetCache", &nnrt.OH_NNCompilation_SetCache);
LoadFunction(libNeuralNetworks, "OH_NNCompilation_SetPerformanceMode", &nnrt.OH_NNCompilation_SetPerformanceMode);
LoadFunction(libNeuralNetworks, "OH_NNCompilation_SetPriority", &nnrt.OH_NNCompilation_SetPriority);
LoadFunction(libNeuralNetworks, "OH_NNCompilation_EnableFloat16", &nnrt.OH_NNCompilation_EnableFloat16);
LoadFunction(libNeuralNetworks, "OH_NNCompilation_Build", &nnrt.OH_NNCompilation_Build);
LoadFunction(libNeuralNetworks, "OH_NNCompilation_Destroy", &nnrt.OH_NNCompilation_Destroy);
// NNExecutor
LoadFunction(libNeuralNetworks, "OH_NNExecutor_Construct", &nnrt.OH_NNExecutor_Construct);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_SetInput", &nnrt.OH_NNExecutor_SetInput);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_SetOutput", &nnrt.OH_NNExecutor_SetOutput);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_GetOutputShape", &nnrt.OH_NNExecutor_GetOutputShape);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_Run", &nnrt.OH_NNExecutor_Run);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_AllocateInputMemory", &nnrt.OH_NNExecutor_AllocateInputMemory);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_AllocateOutputMemory", &nnrt.OH_NNExecutor_AllocateOutputMemory);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_DestroyInputMemory", &nnrt.OH_NNExecutor_DestroyInputMemory);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_DestroyOutputMemory", &nnrt.OH_NNExecutor_DestroyOutputMemory);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_SetInputWithMemory", &nnrt.OH_NNExecutor_SetInputWithMemory);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_SetOutputWithMemory", &nnrt.OH_NNExecutor_SetOutputWithMemory);
LoadFunction(libNeuralNetworks, "OH_NNExecutor_Destroy", &nnrt.OH_NNExecutor_Destroy);
// NNDevice
LoadFunction(libNeuralNetworks, "OH_NNDevice_GetAllDevicesID", &nnrt.OH_NNDevice_GetAllDevicesID);
LoadFunction(libNeuralNetworks, "OH_NNDevice_GetName", &nnrt.OH_NNDevice_GetName);
LoadFunction(libNeuralNetworks, "OH_NNDevice_GetType", &nnrt.OH_NNDevice_GetType);
return nnrt;
}
const NnrtApi* NnrtImplementation()
{
static const NnrtApi nnrt = LoadNnrt();
if (!nnrt.nnrtExists) {
return nullptr;
}
return &nnrt;
}
} // namespace tflite
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_NNRT_IMPLEMENTATION_H
#define TENSORFLOW_LITE_NNRT_IMPLEMENTATION_H
#include <cstdlib>
#include <dlfcn.h>
#include <fcntl.h>
#include <cstdio>
#include <cstdlib>
#include <memory>
#include "neural_network_runtime_type.h"
namespace tflite {
#define NNRT_LOG(format, ...) fprintf(stderr, format "\n", __VA_ARGS__)
struct NnrtApi {
// This indicates the availability of nnrt library. If it is false, it means that loading
// the nnrt library failed and tflite will not use nnrt to run the model, vice versa.
bool nnrtExists;
// Create model interface
OH_NNModel* (*OH_NNModel_Construct)(void);
OH_NN_ReturnCode (*OH_NNModel_AddTensor)(OH_NNModel* model, const OH_NN_Tensor* nnTensor);
OH_NN_ReturnCode (*OH_NNModel_SetTensorData)(OH_NNModel* model, uint32_t index, const void* buffer,
size_t length);
OH_NN_ReturnCode (*OH_NNModel_AddOperation)(OH_NNModel* model, OH_NN_OperationType op,
const OH_NN_UInt32Array* paramIndices, const OH_NN_UInt32Array* inputIndices,
const OH_NN_UInt32Array* outputIndices);
OH_NN_ReturnCode (*OH_NNModel_SpecifyInputsAndOutputs)(OH_NNModel* model, const OH_NN_UInt32Array* inputIndices,
const OH_NN_UInt32Array* outputIndices);
OH_NN_ReturnCode (*OH_NNModel_Finish)(OH_NNModel* model);
void (*OH_NNModel_Destroy)(OH_NNModel** model);
OH_NN_ReturnCode (*OH_NNModel_GetAvailableOperations)(OH_NNModel* model, size_t deviceID, const bool** isSupported,
uint32_t* opCount);
// Compilation interface
OH_NNCompilation* (*OH_NNCompilation_Construct)(const OH_NNModel* model);
OH_NN_ReturnCode (*OH_NNCompilation_SetCache)(OH_NNCompilation* compilation, const char* cacheDir,
uint32_t version);
OH_NN_ReturnCode (*OH_NNCompilation_SetPerformanceMode)(OH_NNCompilation* compilation,
OH_NN_PerformanceMode performanceMode);
OH_NN_ReturnCode (*OH_NNCompilation_SetPriority)(OH_NNCompilation* compilation, OH_NN_Priority priority);
OH_NN_ReturnCode (*OH_NNCompilation_EnableFloat16)(OH_NNCompilation* compilation, bool enablefloat16);
OH_NN_ReturnCode (*OH_NNCompilation_SetDevice)(OH_NNCompilation* compilation, size_t deviceID);
OH_NN_ReturnCode (*OH_NNCompilation_Build)(OH_NNCompilation* compilation);
void (*OH_NNCompilation_Destroy)(OH_NNCompilation** compilation);
// Executor interface
OH_NNExecutor* (*OH_NNExecutor_Construct)(OH_NNCompilation* compilation);
OH_NN_ReturnCode (*OH_NNExecutor_SetInput)(OH_NNExecutor* executor, uint32_t inputIndex,
const OH_NN_Tensor* nnTensor, const void* buffer, size_t length);
OH_NN_ReturnCode (*OH_NNExecutor_SetOutput)(const OH_NNExecutor* executor, uint32_t outputIndex, void* buffer,
size_t length);
OH_NN_ReturnCode (*OH_NNExecutor_GetOutputShape)(const OH_NNExecutor* executor, uint32_t outputIndex,
const uint32_t** dimensions, uint32_t* dimensionCount);
OH_NN_ReturnCode (*OH_NNExecutor_Run)(OH_NNExecutor* executor);
OH_NN_Memory* (*OH_NNExecutor_AllocateInputMemory)(OH_NNExecutor* executor, uint32_t inputIndex, size_t length);
OH_NN_Memory* (*OH_NNExecutor_AllocateOutputMemory)(OH_NNExecutor* executor, uint32_t outputIndex, size_t length);
void (*OH_NNExecutor_DestroyOutputMemory)(OH_NNExecutor* executor, uint32_t outputIndex, OH_NN_Memory** memory);
void (*OH_NNExecutor_DestroyInputMemory)(OH_NNExecutor* executor, uint32_t inputIndex, OH_NN_Memory** memory);
OH_NN_ReturnCode (*OH_NNExecutor_SetInputWithMemory)(OH_NNExecutor* executor, uint32_t inputIndex,
const OH_NN_Tensor* nnTensor, const OH_NN_Memory* memory);
OH_NN_ReturnCode (*OH_NNExecutor_SetOutputWithMemory)(OH_NNExecutor* executor, uint32_t outputIndex,
const OH_NN_Memory* memory);
void (*OH_NNExecutor_Destroy)(OH_NNExecutor** executor);
// Device interface
OH_NN_ReturnCode (*OH_NNDevice_GetAllDevicesID)(const size_t** allDevicesID, uint32_t* deviceCount);
OH_NN_ReturnCode (*OH_NNDevice_GetName)(size_t deviceID, const char** name);
OH_NN_ReturnCode (*OH_NNDevice_GetType)(size_t deviceID, OH_NN_DeviceType* deviceType);
};
const NnrtApi* NnrtImplementation();
} // namespace tflite
#endif // TENSORFLOW_LITE_NNRT_IMPLEMENTATION_H
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "tflite/tools/bitmap_helpers.h"
#include <fstream>
#include <iostream>
#include "tflite/tools/log.h"
namespace tflite {
namespace label_classify {
void DecodeBmp(const uint8_t* input, int32_t rowSize, ImageInfo imageInfo, bool topDown, std::vector<uint8_t>& output)
{
ColorChannelOffset colorChannelOffset = { BLUE_OFFSET, GREEN_OFFSET, ALPHA_OFFSET };
for (int32_t i = 0; i < imageInfo.height; ++i) {
int32_t srcPos;
int32_t dstPos;
for (int32_t j = 0; j < imageInfo.width; j++) {
if (!topDown) {
srcPos = ((imageInfo.height - 1 - i) * rowSize) + j * imageInfo.channels;
} else {
srcPos = i * rowSize + j * imageInfo.channels;
}
dstPos = (i * imageInfo.width + j) * imageInfo.channels;
switch (imageInfo.channels) {
case GRAYSCALE_DIM:
output[dstPos] = input[srcPos];
break;
case BGR_DIM:
// BGR -> RGB
output[dstPos] = input[srcPos + colorChannelOffset.blueOffset];
output[dstPos + colorChannelOffset.greenOffset] = input[srcPos + colorChannelOffset.greenOffset];
output[dstPos + colorChannelOffset.blueOffset] = input[srcPos];
break;
case BGRA_DIM:
// BGRA -> RGBA
output[dstPos] = input[srcPos + colorChannelOffset.blueOffset];
output[dstPos + colorChannelOffset.greenOffset] = input[srcPos + colorChannelOffset.greenOffset];
output[dstPos + colorChannelOffset.blueOffset] = input[srcPos];
output[dstPos + colorChannelOffset.alphaOffset] = input[srcPos + colorChannelOffset.alphaOffset];
break;
default:
LOG(FATAL) << "Unexpected number of channels: " << imageInfo.channels;
break;
}
}
}
return;
}
void ReadBmp(const std::string& inputBmpName, ImageInfo& imageInfo, Settings* s, std::vector<uint8_t>& inputImage)
{
int32_t begin, end;
std::ifstream file(inputBmpName, std::ios::in | std::ios::binary);
if (!file) {
LOG(FATAL) << "input file " << inputBmpName << " not found";
return;
}
begin = file.tellg();
file.seekg(0, std::ios::end);
end = file.tellg();
size_t len = end - begin;
if (s->verbose) {
LOG(INFO) << "len: " << len;
}
std::vector<uint8_t> img_bytes(len);
BmpAddressOffset bmpAddressOffset = { HEADER_ADDRESS_OFFSET, WIDTH_ADDRESS_OFFSET,
HEIGHT_ADDRESS_OFFSET, BBP_ADDRESS_OFFSET };
file.seekg(0, std::ios::beg);
file.read(reinterpret_cast<char*>(img_bytes.data()), len);
const int32_t headerSize =
*(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.headerAddressOffset));
imageInfo.width = *(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.widthAddressOffset));
imageInfo.height =
abs(*(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.heightAddressOffset)));
const int32_t bpp = *(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.bbpAddressOffset));
imageInfo.channels = bpp / BIT_TO_BYTE;
inputImage.resize(imageInfo.height * imageInfo.width * imageInfo.channels);
if (s->verbose) {
LOG(INFO) << "width, height, channels: " << imageInfo.width << ", " << imageInfo.height << ", "
<< imageInfo.channels;
}
// there may be padding bytes when the width is not a multiple of 4 bytes.
// 8 * channels == bits per pixel
const int32_t rowSize = ((8 * imageInfo.channels * imageInfo.width + 31) >> 5) << 2;
// if height is negative, data layout is top down. otherwise, it's bottom up.
bool topDown = (imageInfo.height < 0);
// Decode image, allocating tensor once the image size is known.
const uint8_t* bmpPixels = &img_bytes[headerSize];
DecodeBmp(bmpPixels, rowSize, imageInfo, topDown, inputImage);
return;
}
} // namespace label_classify
} // namespace tflite
@@ -0,0 +1,141 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_BITMAP_HELPERS_H
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_BITMAP_HELPERS_H
#include "tflite/label_classify/label_classify.h"
#include "tensorflow/lite/builtin_op_data.h"
#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/kernels/register.h"
#include "tensorflow/lite/string_util.h"
#include "log.h"
namespace tflite {
namespace label_classify {
const int INPUT_NUMBER = 2;
const int OUPUT_NUMBER = 1;
const int INT8_OFFSET_NUMBER = 128;
const int BIT_TO_BYTE = 8;
const int BLUE_OFFSET = 2;
const int GREEN_OFFSET = 1;
const int ALPHA_OFFSET = 3;
const int HEADER_ADDRESS_OFFSET = 10;
const int WIDTH_ADDRESS_OFFSET = 18;
const int HEIGHT_ADDRESS_OFFSET = 22;
const int BBP_ADDRESS_OFFSET = 28;
enum ChannelDim : int {
GRAYSCALE_DIM = 1,
BGR_DIM = 3,
BGRA_DIM = 4
};
struct BmpAddressOffset {
int headerAddressOffset = 0;
int widthAddressOffset = 0;
int heightAddressOffset = 0;
int bbpAddressOffset = 0;
};
struct ColorChannelOffset {
int blueOffset = 0;
int greenOffset = 0;
int alphaOffset = 0;
};
struct ImageInfo {
int32_t width = 0;
int32_t height = 0;
int32_t channels = 0;
};
void ReadBmp(const std::string& input_bmp_name, ImageInfo& imageInfo, Settings* s, std::vector<uint8_t>& input);
template <typename T>
void Resize(T* out, uint8_t* in, ImageInfo inputImageInfo, ImageInfo wantedImageInfo, Settings* s)
{
std::unique_ptr<Interpreter> interpreter = std::make_unique<Interpreter>();
int32_t baseIndex = 0;
int32_t outputIndex = 2;
// two inputs: input and new_sizes
interpreter->AddTensors(INPUT_NUMBER, &baseIndex);
// one output
interpreter->AddTensors(OUPUT_NUMBER, &baseIndex);
// set input and output tensors
interpreter->SetInputs({ 0, 1 });
interpreter->SetOutputs({ 2 });
// set parameters of tensors
TfLiteQuantizationParams quant;
interpreter->SetTensorParametersReadWrite(0, kTfLiteFloat32, "input",
{ 1, inputImageInfo.height, inputImageInfo.width, inputImageInfo.channels }, quant);
interpreter->SetTensorParametersReadWrite(1, kTfLiteInt32, "new_size", { 2 }, quant);
interpreter->SetTensorParametersReadWrite(outputIndex, kTfLiteFloat32, "output",
{ 1, wantedImageInfo.height, wantedImageInfo.width, wantedImageInfo.channels }, quant);
ops::builtin::BuiltinOpResolver resolver;
const TfLiteRegistration* resizeOp = resolver.FindOp(BuiltinOperator_RESIZE_BILINEAR, 1);
auto* params = reinterpret_cast<TfLiteResizeBilinearParams*>(malloc(sizeof(TfLiteResizeBilinearParams)));
if (params == nullptr) {
LOG(ERROR) << "Malloc memory failed in BitmapHelperslmpl.";
return;
}
params->align_corners = false;
params->half_pixel_centers = false;
interpreter->AddNodeWithParameters({ 0, 1 }, { 2 }, nullptr, 0, params, resizeOp, nullptr);
interpreter->AllocateTensors();
// fill input image
// in[] are integers, cannot do memcpy() directly
auto input = interpreter->typed_tensor<float>(0);
for (int32_t i = 0; i < inputImageInfo.height * inputImageInfo.width * inputImageInfo.channels; ++i) {
input[i] = in[i];
}
// fill new_sizes
interpreter->typed_tensor<int32_t>(1)[0] = wantedImageInfo.height;
interpreter->typed_tensor<int32_t>(1)[1] = wantedImageInfo.width;
interpreter->Invoke();
auto output = interpreter->typed_tensor<float>(2);
for (int32_t i = 0; i < wantedImageInfo.height * wantedImageInfo.width * wantedImageInfo.channels; ++i) {
switch (s->inputType) {
case kTfLiteFloat32:
out[i] = (output[i] - s->inputMean) / s->inputStd;
break;
case kTfLiteInt8:
out[i] = static_cast<int8_t>(output[i] - INT8_OFFSET_NUMBER);
break;
case kTfLiteUInt8:
out[i] = static_cast<uint8_t>(output[i]);
break;
default:
break;
}
}
}
// explicit instantiation
template void Resize<float>(float*, uint8_t*, ImageInfo, ImageInfo, Settings*);
template void Resize<int8_t>(int8_t*, uint8_t*, ImageInfo, ImageInfo, Settings*);
template void Resize<uint8_t>(uint8_t*, uint8_t*, ImageInfo, ImageInfo, Settings*);
template void Resize<int64_t>(int64_t*, uint8_t*, ImageInfo, ImageInfo, Settings*);
} // namespace label_classify
} // namespace tflite
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_BITMAP_HELPERS_H
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_GET_TOP_N_H
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_GET_TOP_N_H
#include <algorithm>
#include <functional>
#include <queue>
#include "tensorflow/lite/c/common.h"
namespace tflite {
namespace label_classify {
template <class T>
void GetTopN(T* prediction, int32_t predictionSize, size_t numResults, float threshold,
std::vector<std::pair<float, int32_t>>* topResults, TfLiteType inputType)
{
// Will contain top N results in ascending order.
std::priority_queue<std::pair<float, int32_t>, std::vector<std::pair<float, int32_t>>,
std::greater<std::pair<float, int32_t>>>
topResultPQ;
const long count = predictionSize; // NOLINT(runtime/int32_t)
float value = 0.0;
float intNormalizedFactor = 256.0;
float uintNormalizedFactor = 255.0;
uint32_t offsetNumber = 128;
for (int32_t i = 0; i < count; ++i) {
switch (inputType) {
case kTfLiteFloat32:
value = prediction[i];
break;
case kTfLiteInt8:
value = (prediction[i] + offsetNumber) / intNormalizedFactor;
break;
case kTfLiteUInt8:
value = prediction[i] / uintNormalizedFactor;
break;
default:
break;
}
// Only add it if it beats the threshold and has a chance at being in the top N.
if (value < threshold) {
continue;
}
topResultPQ.push(std::pair<float, int32_t>(value, i));
// If at capacity, kick the smallest value out.
if (topResultPQ.size() > numResults) {
topResultPQ.pop();
}
}
// Copy to output vector and reverse into descending order.
while (!topResultPQ.empty()) {
topResults->push_back(topResultPQ.top());
topResultPQ.pop();
}
std::reverse(topResults->begin(), topResults->end());
}
// explicit instantiation so that we can use them otherwhere
template void GetTopN<float>(float*, int32_t, size_t, float, std::vector<std::pair<float, int32_t>>*, TfLiteType);
template void GetTopN<int8_t>(int8_t*, int32_t, size_t, float, std::vector<std::pair<float, int32_t>>*, TfLiteType);
template void GetTopN<uint8_t>(uint8_t*, int32_t, size_t, float, std::vector<std::pair<float, int32_t>>*, TfLiteType);
template void GetTopN<int64_t>(int64_t*, int32_t, size_t, float, std::vector<std::pair<float, int32_t>>*, TfLiteType);
} // namespace label_classify
} // namespace tflite
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_GET_TOP_N_H
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_LOG_H
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_LOG_H
#include <iostream>
#include <sstream>
namespace tflite {
namespace label_classify {
class Log {
std::stringstream stream_;
public:
explicit Log(const char* severity)
{
stream_ << severity << ": ";
}
std::stringstream& Stream()
{
return stream_;
}
~Log()
{
std::cerr << stream_.str() << std::endl;
}
};
#define LOG(severity) tflite::label_classify::Log(#severity).Stream()
} // namespace label_classify
} // namespace tflite
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_LOG_H
@@ -0,0 +1,267 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utils.h"
#include <fstream>
#include <sys/time.h>
#include <iostream>
#include "tflite/tools/bitmap_helpers.h"
#include "tflite/tools/get_topn.h"
#include "tflite/tools/log.h"
namespace tflite {
namespace label_classify {
constexpr int32_t DATA_PRINT_NUM = 1000;
constexpr int32_t DATA_EACHLINE_NUM = 1000;
constexpr int32_t SECOND_TO_MICROSECOND_RATIO = 1000000;
constexpr uint8_t WEIGHT_DIMENSION = 2;
constexpr uint8_t CHANNEL_DIMENSION = 3;
double GetUs(struct timeval t)
{
return (t.tv_sec * SECOND_TO_MICROSECOND_RATIO + t.tv_usec);
}
TfLiteStatus ReadLabelsFile(const string& fileName, std::vector<string>& result, size_t& foundLabelCount)
{
std::ifstream file(fileName);
if (!file) {
LOG(ERROR) << "Labels file " << fileName << " not found";
return kTfLiteError;
}
result.clear();
string line;
while (std::getline(file, line)) {
result.push_back(line);
}
foundLabelCount = result.size();
const int32_t padding = 16;
while (result.size() % padding) {
result.emplace_back();
}
return kTfLiteOk;
}
void GetInputNameAndShape(string& inputShapeString, std::map<string, std::vector<int>>& userInputShapes)
{
if (inputShapeString == "") {
return;
}
int pos = inputShapeString.find_last_of(":");
string userInputName = inputShapeString.substr(0, pos);
string dimString = inputShapeString.substr(pos + 1);
int dimPos = dimString.find(",");
std::vector<int> inputDims;
while (dimPos != dimString.npos) {
inputDims.push_back(std::stoi(dimString.substr(0, dimPos)));
dimString = dimString.substr(dimPos + 1);
dimPos = dimString.find(",");
}
inputDims.push_back(std::stoi(dimString));
userInputShapes.insert(std::map<string, std::vector<int>>::value_type(userInputName, inputDims));
}
TfLiteStatus FilterDynamicInputs(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter,
std::map<int, std::vector<int>>& neededInputShapes)
{
std::vector<int> inputIndexes = interpreter->inputs();
std::map<string, int> nameIndexs;
for (int i = 0; i < inputIndexes.size(); i++) {
LOG(INFO) << "input index: " << inputIndexes[i];
nameIndexs.insert(std::map<string, int>::value_type(interpreter->GetInputName(i), inputIndexes[i]));
}
if (settings.inputShape.find(":") == settings.inputShape.npos) {
LOG(ERROR) << "The format of input shapes string is not supported.";
return kTfLiteError;
}
// Get input names and shapes
std::map<string, std::vector<int>> userInputShapes;
string inputShapeString = settings.inputShape;
int pos = inputShapeString.find(";");
while (pos != inputShapeString.npos) {
GetInputNameAndShape(inputShapeString, userInputShapes);
inputShapeString = inputShapeString.substr(pos + 1);
pos = inputShapeString.find(";");
}
GetInputNameAndShape(inputShapeString, userInputShapes);
for (const auto& inputShape : userInputShapes) {
string inputName = inputShape.first;
auto findName = nameIndexs.find(inputName);
if (findName == nameIndexs.end()) {
LOG(ERROR) << "The input name is error: " << inputShape.first << ".";
return kTfLiteError;
} else {
neededInputShapes.insert(std::map<int, std::vector<int>>::value_type(findName->second, inputShape.second));
}
}
return kTfLiteOk;
}
template <class T> void PrintData(T* data, int32_t dataSize, int32_t printSize)
{
if (printSize > dataSize) {
printSize = dataSize;
}
for (int32_t i = 0; i < printSize; ++i) {
std::cout << static_cast<float>(*(data + i)) << "\t";
}
std::cout << std::endl;
}
void PrintResult(std::unique_ptr<tflite::Interpreter>& interpreter)
{
for (int32_t index = 0; index < interpreter->outputs().size(); ++index) {
int32_t output_index = interpreter->outputs()[index];
TfLiteIntArray* outputsDims = interpreter->tensor(output_index)->dims;
int32_t dimSize = outputsDims->size;
int32_t outputTensorSize = 1;
for (int32_t i = 0; i < dimSize; ++i) {
outputTensorSize *= outputsDims->data[i];
}
TfLiteTensor* outputTensor = interpreter->tensor(output_index);
switch (outputTensor->type) {
case kTfLiteFloat32:
PrintData<float>(interpreter->typed_output_tensor<float>(index), outputTensorSize, DATA_PRINT_NUM);
break;
case kTfLiteInt32:
PrintData<int32_t>(interpreter->typed_output_tensor<int32_t>(index), outputTensorSize, DATA_PRINT_NUM);
break;
case kTfLiteUInt8:
PrintData<uint8_t>(interpreter->typed_output_tensor<uint8_t>(index), outputTensorSize, DATA_PRINT_NUM);
break;
case kTfLiteInt8:
PrintData<int8_t>(interpreter->typed_output_tensor<int8_t>(index), outputTensorSize, DATA_PRINT_NUM);
break;
default:
LOG(ERROR) << "Unsupportted tensor datatype: " << outputTensor->type << "!";
return;
}
}
}
void AnalysisResults(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter)
{
const float threshold = 0.001f;
std::vector<std::pair<float, int32_t>> topResults;
if (settings.printResult) {
LOG(INFO) << "Outputs Data:";
PrintResult(interpreter);
}
int32_t output = interpreter->outputs()[0];
TfLiteIntArray* outputDims = interpreter->tensor(output)->dims;
// assume output dims to be something like (1, 1, ... ,size)
auto outputSize = outputDims->data[outputDims->size - 1];
auto tfType = interpreter->tensor(output)->type;
switch (tfType) {
case kTfLiteFloat32:
GetTopN<float>(interpreter->typed_output_tensor<float>(0), outputSize, settings.numberOfResults, threshold,
&topResults, settings.inputType);
break;
case kTfLiteInt8:
GetTopN<int8_t>(interpreter->typed_output_tensor<int8_t>(0), outputSize, settings.numberOfResults,
threshold, &topResults, settings.inputType);
break;
case kTfLiteUInt8:
GetTopN<uint8_t>(interpreter->typed_output_tensor<uint8_t>(0), outputSize, settings.numberOfResults,
threshold, &topResults, settings.inputType);
break;
case kTfLiteInt64:
GetTopN<int64_t>(interpreter->typed_output_tensor<int64_t>(0), outputSize, settings.numberOfResults,
threshold, &topResults, settings.inputType);
break;
default:
LOG(ERROR) << "cannot handle output type " << tfType << " yet";
return;
}
std::vector<string> labels;
size_t labelCount;
if (ReadLabelsFile(settings.labelsFileName, labels, labelCount) != kTfLiteOk) {
return;
}
for (const auto& result : topResults) {
const float confidence = result.first;
const int32_t index = result.second;
LOG(INFO) << confidence << ": " << index << " " << labels[index];
}
}
void ImportData(Settings& settings, std::vector<int>& imageSize, std::unique_ptr<tflite::Interpreter>& interpreter)
{
ImageInfo inputImageInfo = {imageSize[0], imageSize[1], imageSize[2]};
std::vector<uint8_t> in;
ReadBmp(settings.inputBmpName, inputImageInfo, &settings, in);
int32_t input = interpreter->inputs()[0];
if (settings.verbose) {
LOG(INFO) << "input: " << input;
}
// get input dimension from the model.
TfLiteIntArray* dims = interpreter->tensor(input)->dims;
ImageInfo wantedimageInfo;
wantedimageInfo.height = dims->data[1];
wantedimageInfo.width = dims->data[WEIGHT_DIMENSION];
wantedimageInfo.channels = (dims->size > CHANNEL_DIMENSION) ? dims->data[CHANNEL_DIMENSION] : 1;
settings.inputType = interpreter->tensor(input)->type;
switch (settings.inputType) {
case kTfLiteFloat32:
Resize<float>(interpreter->typed_tensor<float>(input), in.data(), inputImageInfo, wantedimageInfo,
&settings);
break;
case kTfLiteInt8:
Resize<int8_t>(interpreter->typed_tensor<int8_t>(input), in.data(), inputImageInfo, wantedimageInfo,
&settings);
break;
case kTfLiteUInt8:
Resize<uint8_t>(interpreter->typed_tensor<uint8_t>(input), in.data(), inputImageInfo, wantedimageInfo,
&settings);
break;
case kTfLiteInt64:
Resize<int64_t>(interpreter->typed_tensor<int64_t>(input), in.data(), inputImageInfo, wantedimageInfo,
&settings);
break;
default:
LOG(ERROR) << "cannot handle input type " << settings.inputType << " yet";
return;
}
}
bool IsEqualShape(int tensorIndex, const std::vector<int>& dims, std::unique_ptr<tflite::Interpreter>& interpreter)
{
TfLiteTensor* tensor = interpreter->tensor(tensorIndex);
for (int i = 0; i < tensor->dims->size; ++i) {
if (tensor->dims->data[i] != dims[i]) {
return false;
}
}
return true;
}
} // namespace label_classify
} // namespace tflite
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_UTILS_H
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_UTILS_H
#include "../label_classify/label_classify.h"
#include "sys/time.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/string_type.h"
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/interpreter.h"
#include "neural_network_runtime.h"
namespace tflite {
namespace label_classify {
double GetUs(struct timeval t);
TfLiteStatus ReadLabelsFile(const string& fileName, std::vector<string>& result, size_t& foundLabelCount);
TfLiteStatus FilterDynamicInputs(Settings& settings,
std::unique_ptr<tflite::Interpreter>& interpreter, std::map<int, std::vector<int>>& neededInputShapes);
bool IsEqualShape(int tensorIndex, const std::vector<int>& dim, std::unique_ptr<tflite::Interpreter>& interpreter);
void GetInputNameAndShape(string &inputShapeString, std::map<string, std::vector<int>>& userInputShapes);
void PrintResult(std::unique_ptr<tflite::Interpreter>& interpreter);
void AnalysisResults(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter);
void ImportData(Settings& settings, std::vector<int>& imageSize, std::unique_ptr<tflite::Interpreter>& interpreter);
} // namespace label_classify
} // namespace tflite
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_UTILS_H
+329
View File
@@ -0,0 +1,329 @@
# NNRt开发指导
## NNRt开发概述
### 功能简介
神经网络运行时部件(NNRt)是跨设备的AI运行时框架,作为端侧推理框架和专用加速芯片的中间桥梁,为端侧推理框架提供了统一的Native接口,使能端侧推理框架在专有加速芯片上推理;为芯片厂商提供了统一的HDI接口,使能专有加速芯片接入OpenHarmony社区生态。
本文介绍芯片厂商如何在将专有加速芯片接入NNRt,接入OpenHarmony社区生态。
### 基本概念
在开发前,开发者需要先了解以下概念,以便更好地理解全文内容:
- NNRtNeural Network Runtime,神经网络运行时,是本指导主要介绍的部件。
- OHOSOpenHarmony Operating System,开源鸿蒙操作系统。
- HDIHardware Device Interface,硬件设备接口,是OHOS中系统组件与芯片组件通信的接口。
- IDL: Interface Description Language,接口描述语言,是HDI接口的语言格式。
### 约束与限制
- 系统版本:OpenHarmony 3.2及以上。
- 开发环境:Ubuntu 18.04及以上。
- 接入设备:OpenHarmony定义的标准设备。
### 运作机制
NNRt通过HDI接口实现与设备芯片的对接,由HDI接口实现跨进程通信。
**图1** NNRt架构图
![架构图](./arch_diagram.png)
整个架构主要分为三层,AI应用在应用层,AI推理框架和神经网络运行时在系统层,设备服务在芯片层。AI应用要在专用加速芯片上完成模型推理,需要经过AI推理框架和神经网络运行时才能调用到底层的芯片设备,而神经网络运行时就是负责适配底层各种芯片设备,它开放了标准统一的南向接口,众多的第三方芯片设备都可以通过HDI接口接入OHOS。
程序运行时,AI应用、AI推理框架、神经网络运行时都在同一个进程,底层设备服务在另一个进程,进程间是通过IPC的机制通信,神经网络运行时根据南向HDI接口实现了HDI Client,服务端也需要根据南向HDI接口实现HDI Service。
## NNRt开发指导
### 场景介绍
下文以rk3568芯片为例,展示rk3568 CPU如何通过HDI接口接入NNRt,并完成AI模型推理。
### 开发流程
适配操作的整体流程如下:
**图2** NNRt适配流程
![开发流程](./dev_flow.png)
### 开发步骤
开发者具体可通过以下步骤在芯片侧对接NNRt:
1. 开源社区下载OpenHarmony的代码,编译drivers_interface部件,生成HDI接口的头文件。
- [下载源码](../get-code/sourcecode-acquire.md)。
- 编译接口IDL文件。
```shell
./build.sh --product-name rk3568 ccache --target-cpu arm64 --build-target=drivers_interface_nnrt
```
--target-cpu arm64:是64位编译选项,若编译32位,则不需添加--target-cpu arm64
编译之后,可以在```out/rk3568/gen/drivers/interface/nnrt```目录下找到生成的头文件,默认生成C++头文件,若需要生成C头文件,则修改```drivers/interface/nnrt/v1_0/BUILD.gn```文件中的language。
```shell
language = "c"
```
生成头文件目录如下所示:
```text
out/rk3568/gen/drivers/interface/nnrt
└── v1_0
├── drivers_interface_nnrt__libnnrt_proxy_1.0_external_deps_temp.json
├── drivers_interface_nnrt__libnnrt_stub_1.0_external_deps_temp.json
├── innrt_device.h # 设备接口头文件
├── iprepared_model.h # 编译AI模型对象头文件
├── libnnrt_proxy_1.0__notice.d
├── libnnrt_stub_1.0__notice.d
├── model_types.cpp # AI模型结构定义实现文件
├── model_types.h # AI模型结构定义头文件
├── nnrt_device_driver.cpp # 设备驱动实现参考样例
├── nnrt_device_proxy.cpp
├── nnrt_device_proxy.h
├── nnrt_device_service.cpp # 设备服务端实现参考样例
├── nnrt_device_service.h # 设备服务端头文件
├── nnrt_device_stub.cpp
├── nnrt_device_stub.h
├── nnrt_types.cpp # 数据类型定义实现文件
├── nnrt_types.h # 数据类型定义头文件
├── node_attr_types.cpp # AI模型算子属性定义实现文件
├── node_attr_types.h # AI模型算子属性定义
├── prepared_model_proxy.cpp
├── prepared_model_proxy.h
├── prepared_model_service.cpp # 编译AI模型对象服务端实现参考样例
├── prepared_model_service.h # 编译AI模型对象服务端头文件
├── prepared_model_stub.cpp
└── prepared_model_stub.h
```
2. 实现HDI服务
- 在drivers/peripheral目录下新建开发目录,用于HDI服务开发,开发目录结构如下所示。
```text
drivers/peripheral/nnrt
├── BUILD.gn # 代码编译脚本文件
├── bundle.json
└── hdi_cpu_service # 自定义目录
├── BUILD.gn # 代码编译脚本文件
├── include
│   ├── nnrt_device_service.h # 设备服务端头文件
│   ├── node_functions.h # 非必须,由具体实现决定
│   ├── node_registry.h # 非必须,由具体实现决定
│   └── prepared_model_service.h # 编译AI模型对象服务端头文件
└── src
├── nnrt_device_driver.cpp # 设备驱动实现文件
├── nnrt_device_service.cpp # 设备服务端实现文件
├── nnrt_device_stub.cpp # 非必须,由具体实现决定
├── node_attr_types.cpp # 非必须,由具体实现决定
├── node_functions.cpp # 非必须,由具体实现决定
├── node_registry.cpp # 非必须,由具体实现决定
└── prepared_model_service.cpp # 编译AI模型对象服务端实现文件
```
- 实现设备驱动,无特殊需求可直接使用步骤1中生成的nnrt_device_driver.cpp文件,否则根据具体驱动开发。
- 实现服务接口,主要实现nnrt_device_service.cpp和prepared_model_service.cpp文件,接口定义可以参考```drivers/interface/nnrt```。
- 编译驱动和服务实现为共享库。
在```drivers/peripheral/nnrt/hdi_cpu_service/```下新建```BUILD.gn```文件,对驱动入口和服务实现编译为共享库。
```shell
import("//build/ohos.gni")
import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")
ohos_shared_library("libnnrt_service_1.0") {
include_dirs = []
sources = [
"src/nnrt_device_service.cpp",
"src/prepared_model_service.cpp",
"src/node_registry.cpp",
"src/node_functions.cpp",
"src/node_attr_types.cpp"
]
public_deps = [ "//drivers/interface/nnrt/v1_0:nnrt_idl_headers" ]
external_deps = [
"hdf_core:libhdf_utils",
"hiviewdfx_hilog_native:libhilog",
"ipc:ipc_single",
"c_utils:utils",
]
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_nnrt"
}
ohos_shared_library("libnnrt_driver") {
include_dirs = []
sources = [ "src/nnr_device_driver.cpp" ]
deps = [ "//drivers/peripheral/nnrt/hdi_cpu_service:libnnrt_service_1.0" ]
external_deps = [
"hdf_core:libhdf_host",
"hdf_core:libhdf_ipc_adapter",
"hdf_core:libhdf_utils",
"hiviewdfx_hilog_native:libhilog",
"ipc:ipc_single",
"c_utils:utils",
]
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_nnrt"
}
group("hdf_nnrt_service") {
deps = [
":libnnrt_driver",
":libnnrt_service_1.0",
]
}
```
将```group("hdf_nnrt_service")```添加到```drivers/peripheral/nnrt/BUILD.gn```文件中
```shell
if (defined(ohos_lite)) {
group("nnrt_entry") {
deps = [ ]
}
} else {
group("nnrt_entry") {
deps = [
"./hdi_cpu_service:hdf_nnrt_service",
]
}
}
```
新建```drivers/peripheral/nnrt/bundle.json```用于定义新增的```drivers_peripheral_nnrt```部件。
```json
{
"name": "drivers_peripheral_nnrt",
"description": "Neural network runtime device driver",
"version": "3.2",
"license": "Apache License 2.0",
"component": {
"name": "drivers_peripheral_nnrt",
"subsystem": "hdf",
"syscap": [""],
"adapter_system_type": ["standard"],
"rom": "1024KB",
"ram": "2048KB",
"deps": {
"components": [
"ipc",
"hdf_core",
"hiviewdfx_hilog_native",
"c_utils"
],
"third_part": [
"bounds_checking_function"
]
},
"build": {
"sub_component": [
"//drivers/peripheral/nnrt:nnrt_entry"
],
"test": [
],
"inner_kits": [
]
}
}
}
```
3. 声明HDI服务
在对应产品的uhdf hcs配置文件中声明用户态驱动与服务,本例中rk3568对应在```vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs```文件中新增如下配置:
```text
nnrt :: host {
hostName = "nnrt_host";
priority = 50;
uid = "";
gid = "";
caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"];
nnrt_device :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
moduleName = "libnnrt_driver.z.so";
serviceName = "nnrt_device_service";
}
}
}
```
注意:修改hcs文件后请删除out目录重新编译,才能生效。
4. 配置host进程用户和组
对于新增host进程的场景,需要新增配置对应进程的用户ID和组ID。 进程的用户ID在文件```base/startup/init/services/etc/passwd```中配置,进程的组ID在文件```base/startup/init/services/etc/group```中配置。
```text
# 在base/startup/init/services/etc/passwd新增
nnrt_host:x:3311:3311:::/bin/false
# 在base/startup/init/services/etc/group新增
nnrt_host:x:3311:
```
完成上述所有配置后,全量编译版本后应该可以观察到新增host进程启动,也可以通过hilog输出检索新增的服务名称nnrt_interface_service观察到服务发布成功
5. SELinux配置
OHOS已经开启SELinux特性,需要对新增的进程和服务配置相应的SELinux规则,用于运行host进程启动访问某些资源、发布HDI服务。对于调用者来说,也需要配置SELinux规则运行获取和调用某个HDI服务。
在```base/security/selinux/sepolicy/ohos_policy/drivers/adapter/vendor/type.te```文件中配置nnrt_host进程安全上下文
```text
# 新增
type nnrt_host, hdfdomain, domain;
```
由于SeLinux是白名单访问的权限机制,需要根据实际权限需求配置,将服务启动起来之后,通过以下dmesg命令可能查看avc告警,
avc告警会给出缺少的权限,SeLinux的配置也可以参考[OpenHarmony SeLinux子系统的说明](https://gitee.com/openharmony/security_selinux/blob/master/README.md)
```shell
hdc_std shell
dmesg | grep nnrt
```
新建nnrt_host.te配置文件,将权限配置到nnrt_host.te文件中
```shell
# 创建nnrt文件夹
mkdir base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt
# 创建vendor文件夹
mkdir base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt/vendor
# 创建nnrt_host.te文件
touch base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt/vendor/nnrt_host.te
```
然后再将所需的权限写入nnrt_host.te文件中,比如:
```text
allow nnrt_host dev_hdf_kevent:chr_file { ioctl };
allow nnrt_host hilog_param:file { read };
allow nnrt_host sh:binder { transfer };
allow nnrt_host dev_ashmem_file:chr_file { open };
allow sh nnrt_host:fd { use };
```
6. 删除out目录编译整个系统
```shell
./build.sh --product-name rk3568 ccache --jobs=4
```
### 调测验证
服务开发完成后,可以使用XTS用例验证基本功能和兼容性,开发者可通过以下步骤进行验证:
1. 开源社区下载[OpenHarmony代码](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/get-code/sourcecode-acquire.md),相关用例在test/xts/hats/hdf/nnrt目录下。
2. 编译XTS用例。
```shell
cd test/xts/hats
./build.sh suite=hats system_size=standard --product-name rk3568
```
编译好的测试用例会输出到out/rk3568/suites/hats/testcases/HatsHdfNnrtFunctionTest
3. 将测试用例push到设备上。
```shell
# 将测试用例可执行文件推送到设备上,HatsHdfNnrtFunctionTest是测试用例可执行文件。
hdc_std file send out/rk3568/suites/hats/testcases/HatsHdfNnrtFunctionTest /data/local/tmp/
# 给测试用例可执行文件加上权限。
hdc_std shell "chmod +x /data/local/tmp/HatsHdfNnrtFunctionTest"
# 执行测试用例
hdc_std shell "/data/local/tmp/HatsHdfNnrtFunctionTest"
```
### 开发实例
完整[Demo实例](xxx, Demo暂时还在黄区代码仓,超链接需等Demo开源后补充)可以参考社区实现。
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

+24
View File
@@ -0,0 +1,24 @@
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if (defined(ohos_lite)) {
group("nnrt_entry") {
deps = [ ]
}
} else {
group("nnrt_entry") {
deps = [
"./hdi_cpu_service:hdf_nnrt_service",
]
}
}
@@ -0,0 +1,90 @@
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/ohos.gni")
import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")
ohos_prebuilt_shared_library("mindspore_demo") {
source = "//drivers/peripheral/nnrt/mindspore/mindspore/libmindspore-lite.huawei.so"
install_images = [chipset_base_dir]
subsystem_name = "hdf"
part_name = "drivers_peripheral_nnrt"
}
ohos_shared_library("libnnrt_device_service_1.0") {
include_dirs = [
"//drivers/peripheral/nnrt/hdi_cpu_service/include",
"//drivers/peripheral/nnrt/mindspore",
"//third_party/flatbuffers/include",
"//commonlibrary/c_utils/base/include"
]
sources = [
"src/nnrt_device_service.cpp",
"src/prepared_model_service.cpp",
"src/node_registry.cpp",
"src/node_functions.cpp",
"src/shared_buffer_parser.cpp",
"src/validation.cpp"
]
deps = [
"//drivers/interface/nnrt/v1_0:nnrt_idl_headers",
"//drivers/interface/nnrt/v1_0:libnnrt_stub_1.0",
":mindspore_demo"
]
external_deps = [
"hdf_core:libhdf_utils",
"hiviewdfx_hilog_native:libhilog",
"ipc:ipc_single",
"c_utils:utils"
]
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_nnrt"
}
ohos_shared_library("libnnrt_driver") {
include_dirs = []
sources = [
"src/nnrt_device_driver.cpp"
]
deps = [
"//drivers/peripheral/nnrt/hdi_cpu_service:libnnrt_device_service_1.0",
"//drivers/interface/nnrt/v1_0:libnnrt_stub_1.0"
]
external_deps = [
"hdf_core:libhdf_host",
"hdf_core:libhdf_ipc_adapter",
"hdf_core:libhdf_utils",
"hiviewdfx_hilog_native:libhilog",
"ipc:ipc_single",
"c_utils:utils",
"hdf_core:libhdi"
]
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_nnrt"
}
group("hdf_nnrt_service") {
deps = [
":mindspore_demo",
":libnnrt_driver",
":libnnrt_device_service_1.0",
]
}
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NNRT_V1_0_NNRTDEVICESERVICE_H
#define OHOS_HDI_NNRT_V1_0_NNRTDEVICESERVICE_H
#include <memory>
#include "v1_0/innrt_device.h"
#include "ashmem.h"
#include "include/api/model.h"
#include "mindspore_schema/model_generated.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
class NnrtDeviceService : public INnrtDevice {
public:
NnrtDeviceService() = default;
virtual ~NnrtDeviceService();
int32_t GetDeviceName(std::string& name) override;
int32_t GetVendorName(std::string& name) override;
int32_t GetDeviceType(DeviceType& deviceType) override;
int32_t GetDeviceStatus(DeviceStatus& status) override;
int32_t GetSupportedOperation(const Model& model, std::vector<bool>& ops) override;
int32_t IsFloat16PrecisionSupported(bool& isSupported) override;
int32_t IsPerformanceModeSupported(bool& isSupported) override;
int32_t IsPrioritySupported(bool& isSupported) override;
int32_t IsDynamicInputSupported(bool& isSupported) override;
int32_t PrepareModel(const Model& model, const ModelConfig& config, sptr<IPreparedModel>& preparedModel) override;
int32_t IsModelCacheSupported(bool& isSupported) override;
int32_t PrepareModelFromModelCache(const std::vector<SharedBuffer>& modelCache, const ModelConfig& config,
sptr<IPreparedModel>& preparedModel) override;
int32_t AllocateBuffer(uint32_t length, SharedBuffer& buffer) override;
int32_t ReleaseBuffer(const SharedBuffer& buffer) override;
private:
int32_t ValidateModelConfig(const ModelConfig& config) const;
int32_t ValidateModel(const Model& model) const;
std::shared_ptr<mindspore::schema::MetaGraphT> TransModelToGraph(const Model& model) const;
std::unique_ptr<mindspore::schema::TensorT> TransTensor(const Tensor& tensor) const;
std::unique_ptr<mindspore::schema::CNodeT> TransNode(const Node& node) const;
std::unique_ptr<mindspore::schema::SubGraphT> TransSubGraph(const SubGraph& graph, const size_t numTensor) const;
std::shared_ptr<mindspore::Context> TransModelConfig(const ModelConfig& config) const;
private:
std::shared_ptr<mindspore::Model> m_model {nullptr};
std::unordered_map<int, sptr<Ashmem>> m_ashmems;
};
} // V1_0
} // Nnrt
} // HDI
} // OHOS
#endif // OHOS_HDI_NNRT_V1_0_NNRTDEVICESERVICE_H
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NNR_NODE_FUNCTIONS_H
#define OHOS_HDI_NNR_NODE_FUNCTIONS_H
#include <functional>
#include "hdf_base.h"
#include "utils/hdf_log.h"
#include "node_registry.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
template<typename T>
int32_t ParsePrimitive(const std::vector<int8_t>& primitive, T& attr,
std::function<bool(OHOS::MessageParcel&, T&)> parseFunc)
{
if (primitive.empty()) {
HDF_LOGE("Primitive data is empty.");
return HDF_FAILURE;
}
OHOS::MessageParcel parcelData;
bool ret = parcelData.WriteBuffer(primitive.data(), primitive.size());
if (!ret) {
HDF_LOGE("Write data to MessageParcel failed.");
return HDF_FAILURE;
}
ret = parseFunc(parcelData, attr);
if (!ret) {
HDF_LOGE("Unmarshalling data failed.");
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
PrimUniquePtr GetAddPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetAvgPoolPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetConcatPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetConv2dPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetFullConnectionPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetMaxPoolFusionPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetMatMulFusionPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetSoftmaxPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetReshapePrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetScaleFusionPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetActivationPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetQuantDTypeCastPrimitive(const std::vector<int8_t>& primitive);
PrimUniquePtr GetMulFusionPrimitive(const std::vector<int8_t>& primitive);
} // namespace V1_0
} // namespace Nnrt
} // namespace HDI
} // namespace OHOS
#endif // OHOS_HDI_NNR_NODE_FUNCTIONS_H
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NNR_NODE_REGISTRY_H
#define OHOS_HDI_NNR_NODE_REGISTRY_H
#include <memory>
#include <functional>
#include <unordered_map>
#include "v1_0/nnrt_types.h"
#include "mindspore_schema/model_generated.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
using PrimUniquePtr = std::unique_ptr<mindspore::schema::PrimitiveT>;
class NodeRegistry {
public:
struct Registrar {
Registrar() = delete;
Registrar(NodeType type, std::function<PrimUniquePtr(const std::vector<int8_t>&)> nodeFunc);
};
public:
static NodeRegistry& GetSingleton();
std::function<PrimUniquePtr(const std::vector<int8_t>&)> GetNodeFunc(NodeType type) const;
bool IsNodeTypeExist(NodeType type) const;
private:
NodeRegistry() {};
NodeRegistry(const NodeRegistry&) = delete;
NodeRegistry& operator=(const NodeRegistry&) = delete;
private:
std::unordered_map<NodeType, std::function<PrimUniquePtr(const std::vector<int8_t>&)>> m_nodeRegs;
};
#define REGISTER_NODE(nodeName, nodeType, funcPtr) static NodeRegistry::Registrar g_##nodeName(nodeType, funcPtr)
} // namespace V1_0
} // namespace Nnrt
} // namespace HDI
} // namespace OHOS
#endif // OHOS_HDI_NNR_NODE_REGISTRY_H
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NNR_V1_0_PREPAREDMODELSERVICE_H
#define OHOS_HDI_NNR_V1_0_PREPAREDMODELSERVICE_H
#include "v1_0/iprepared_model.h"
#include "include/api/data_type.h"
#include "include/api/context.h"
#include "include/api/types.h"
#include "include/api/model.h"
#include "mindspore_schema/model_generated.h"
#include "ashmem.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
constexpr int DYNAMIC_SHAPE_FLAG = -1;
class PreparedModelService : public IPreparedModel {
public:
PreparedModelService() = default;
virtual ~PreparedModelService();
explicit PreparedModelService(std::shared_ptr<mindspore::Context> context);
int32_t Compile(std::shared_ptr<mindspore::schema::MetaGraphT> graph);
int32_t Compile(const void* modelBuffer, size_t length);
int32_t ExportModelCache(std::vector<SharedBuffer>& modelCache) override;
int32_t Run(const std::vector<IOTensor>& inputs, const std::vector<IOTensor>& outputs,
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough) override;
private:
int32_t SetInputs(const std::vector<IOTensor>& inputs);
int32_t SetOutputs(const std::vector<IOTensor>& outputs);
int32_t GetMSInputsAndOutputs();
int32_t CompareTensor(const IOTensor& tensor, const mindspore::MSTensor& msTensor);
sptr<Ashmem> ParseBuffer(const SharedBuffer& buffer);
int32_t UpdateOutput(const std::vector<IOTensor>& outputs,
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough);
void ResetInputAndOutput();
private:
std::shared_ptr<mindspore::schema::MetaGraphT> m_graph {nullptr};
std::shared_ptr<mindspore::Context> m_context {nullptr};
flatbuffers::FlatBufferBuilder m_builder;
std::shared_ptr<mindspore::Model> m_model {nullptr};
sptr<Ashmem> m_cacheBuffer {nullptr};
std::vector<sptr<Ashmem>> m_inputAshmems;
std::vector<mindspore::MSTensor> m_inputs;
std::vector<sptr<Ashmem>> m_outputAshmems;
std::vector<mindspore::MSTensor> m_outputs;
std::vector<std::vector<int64_t>> m_inputDims;
bool m_isDynamicShape {false};
};
} // V1_0
} // Nnrt
} // HDI
} // OHOS
#endif // OHOS_HDI_NNR_V1_0_PREPAREDMODELSERVICE_H
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NNR_V1_0_SHARED_BUFFER_PARSER_H
#define OHOS_HDI_NNR_V1_0_SHARED_BUFFER_PARSER_H
#include "ashmem.h"
#include "v1_0/nnrt_types.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
namespace {
const int INVALID_FD = -1;
}
class SharedBufferParser {
public:
SharedBufferParser() {};
~SharedBufferParser();
int32_t Init(const SharedBuffer& buffer);
int32_t Init(const std::string& name, int32_t size);
void* GetBufferPtr();
SharedBuffer GetBuffer();
private:
SharedBuffer m_buffer;
sptr<Ashmem> m_ashptr {nullptr};
void* m_bufferAddr {nullptr};
};
} // V1_0
} // Nnrt
} // HDI
} // OHOS
#endif // OHOS_HDI_NNR_V1_0_SHARED_BUFFER_PARSER_H
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NNRT_VALIDATION_H
#define OHOS_HDI_NNRT_VALIDATION_H
#include "v1_0/nnrt_types.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
int32_t ValidatePerformanceMode(PerformanceMode mode);
int32_t ValidatePriority(Priority priority);
int32_t ValidateDataType(DataType dataType);
int32_t ValidateFormat(Format format);
} // namespace V1_0
} // namespace Nnrt
} // namespace HDI
} // namespace OHOS
#endif // OHOS_HDI_NNRT_VALIDATION_H
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <hdf_base.h>
#include <hdf_device_desc.h>
#include <hdf_log.h>
#include <hdf_sbuf_ipc.h>
#include "v1_0/nnrt_device_stub.h"
using namespace OHOS::HDI::Nnrt::V1_0;
struct HdfNnrtDeviceHost {
struct IDeviceIoService ioService;
OHOS::sptr<OHOS::IRemoteObject> stub;
};
static int32_t NnrtDeviceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data,
struct HdfSBuf *reply)
{
auto *hdfNnrtDeviceHost = CONTAINER_OF(client->device->service, struct HdfNnrtDeviceHost, ioService);
OHOS::MessageParcel *dataParcel = nullptr;
OHOS::MessageParcel *replyParcel = nullptr;
OHOS::MessageOption option;
if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) {
HDF_LOGE("%{public}s:invalid data sbuf object to dispatch", __func__);
return HDF_ERR_INVALID_PARAM;
}
if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) {
HDF_LOGE("%{public}s:invalid reply sbuf object to dispatch", __func__);
return HDF_ERR_INVALID_PARAM;
}
return hdfNnrtDeviceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option);
}
static int HdfNnrtDeviceDriverInit(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("HdfNnrtDeviceDriverInit enter");
return HDF_SUCCESS;
}
static int HdfNnrtDeviceDriverBind(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("HdfNnrtDeviceDriverBind enter");
auto *hdfNnrtDeviceHost = new (std::nothrow) HdfNnrtDeviceHost;
if (hdfNnrtDeviceHost == nullptr) {
HDF_LOGE("%{public}s: failed to create create HdfNnrtDeviceHost object", __func__);
return HDF_FAILURE;
}
hdfNnrtDeviceHost->ioService.Dispatch = NnrtDeviceDriverDispatch;
hdfNnrtDeviceHost->ioService.Open = NULL;
hdfNnrtDeviceHost->ioService.Release = NULL;
auto serviceImpl = INnrtDevice::Get(true);
if (serviceImpl == nullptr) {
HDF_LOGE("%{public}s: failed to get of implement service", __func__);
delete hdfNnrtDeviceHost;
return HDF_FAILURE;
}
hdfNnrtDeviceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl,
INnrtDevice::GetDescriptor());
if (hdfNnrtDeviceHost->stub == nullptr) {
HDF_LOGE("%{public}s: failed to get stub object", __func__);
delete hdfNnrtDeviceHost;
return HDF_FAILURE;
}
deviceObject->service = &hdfNnrtDeviceHost->ioService;
return HDF_SUCCESS;
}
static void HdfNnrtDeviceDriverRelease(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("HdfNnrtDeviceDriverRelease enter");
if (deviceObject->service == nullptr) {
HDF_LOGE("HdfNnrtDeviceDriverRelease not initted");
return;
}
auto *hdfNnrtDeviceHost = CONTAINER_OF(deviceObject->service, struct HdfNnrtDeviceHost, ioService);
delete hdfNnrtDeviceHost;
}
struct HdfDriverEntry g_nnrtdeviceDriverEntry = {
.moduleVersion = 1,
.moduleName = "nnrt",
.Bind = HdfNnrtDeviceDriverBind,
.Init = HdfNnrtDeviceDriverInit,
.Release = HdfNnrtDeviceDriverRelease,
};
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
HDF_INIT(g_nnrtdeviceDriverEntry);
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -0,0 +1,436 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nnrt_device_service.h"
#include <hdf_base.h>
#include "utils/hdf_log.h"
#include "ashmem.h"
#include "securec.h"
#include "node_registry.h"
#include "prepared_model_service.h"
#include "shared_buffer_parser.h"
#include "validation.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
extern "C" INnrtDevice *NnrtDeviceImplGetInstance(void)
{
return new (std::nothrow) NnrtDeviceService();
}
NnrtDeviceService::~NnrtDeviceService()
{
for (auto ash : m_ashmems) {
ash.second->UnmapAshmem();
ash.second->CloseAshmem();
}
}
int32_t NnrtDeviceService::GetDeviceName(std::string& name)
{
name = "RK3568-CPU";
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::GetVendorName(std::string& name)
{
name = "Rockchip";
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::GetDeviceType(DeviceType& deviceType)
{
deviceType = DeviceType::CPU;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::GetDeviceStatus(DeviceStatus& status)
{
status = DeviceStatus::AVAILABLE;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::GetSupportedOperation(const Model& model, std::vector<bool>& ops)
{
size_t nodeSize = model.nodes.size();
auto nodes = model.nodes;
ops.resize(nodeSize, false);
auto& regInstance = NodeRegistry::GetSingleton();
for (size_t i = 0; i < nodeSize; i++) {
ops[i] = regInstance.IsNodeTypeExist(nodes[i].nodeType);
}
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::IsFloat16PrecisionSupported(bool& isSupported)
{
isSupported = true;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::IsPerformanceModeSupported(bool& isSupported)
{
isSupported = true;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::IsPrioritySupported(bool& isSupported)
{
isSupported = false;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::IsDynamicInputSupported(bool& isSupported)
{
isSupported = true;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::PrepareModel(const Model& model, const ModelConfig& config,
sptr<IPreparedModel>& preparedModel)
{
auto ret = ValidateModel(model);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Model is invalid.");
return ret;
}
auto graph = TransModelToGraph(model);
if (graph == nullptr) {
HDF_LOGE("Transfrom model to graph failed.");
return HDF_ERR_INVALID_PARAM;
}
ret = ValidateModelConfig(config);
if (ret != HDF_SUCCESS) {
HDF_LOGE("ModelConfig is invalid.");
return ret;
}
auto context = TransModelConfig(config);
sptr<PreparedModelService> service = new (std::nothrow) PreparedModelService(context);
if (service == nullptr) {
HDF_LOGE("Create new PreparedModelService instance failed.");
return HDF_ERR_MALLOC_FAIL;
}
ret = service->Compile(graph);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Prepared model failed.");
return ret;
}
preparedModel = service;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::IsModelCacheSupported(bool& isSupported)
{
isSupported = true;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::PrepareModelFromModelCache(const std::vector<SharedBuffer>& modelCache,
const ModelConfig& config, sptr<IPreparedModel>& preparedModel)
{
HDF_LOGD("Using cache to prepare model.");
// modelCache must be 1, because PreparedModel only export one cache file.
if (modelCache.size() != 1) {
HDF_LOGE("The size of modelCache vector is not valid, it should be one elememt in that vector.");
return HDF_ERR_INVALID_PARAM;
}
SharedBufferParser parser;
auto ret = parser.Init(modelCache[0]);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse modle buffer failed.");
return HDF_ERR_INVALID_PARAM;
}
void* modelBuffer = parser.GetBufferPtr();
auto context = TransModelConfig(config);
sptr<PreparedModelService> service = new (std::nothrow) PreparedModelService(context);
if (service == nullptr) {
HDF_LOGE("Create new instance PreparedModelService failed.");
return HDF_ERR_MALLOC_FAIL;
}
ret = service->Compile(modelBuffer, modelCache[0].dataSize);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Prepared model failed.");
return ret;
}
preparedModel = service;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::AllocateBuffer(uint32_t length, SharedBuffer& buffer)
{
sptr<Ashmem> ashptr = Ashmem::CreateAshmem("allocateBuffer", length);
if (ashptr == nullptr) {
HDF_LOGE("Create shared memory failed.");
return HDF_FAILURE;
}
if (!ashptr->MapReadAndWriteAshmem()) {
HDF_LOGE("Map allocate buffer failed.");
return HDF_FAILURE;
}
buffer.fd = ashptr->GetAshmemFd();
buffer.bufferSize = ashptr->GetAshmemSize();
buffer.offset = 0;
buffer.dataSize = length;
m_ashmems[buffer.fd] = ashptr;
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::ReleaseBuffer(const SharedBuffer& buffer)
{
// parser will close current fd.
SharedBufferParser parser;
auto ret = parser.Init(buffer);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse buffer failed.");
return HDF_ERR_INVALID_PARAM;
}
for (auto& ash : m_ashmems) {
ash.second->UnmapAshmem();
ash.second->CloseAshmem();
}
m_ashmems.clear();
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::ValidateModelConfig(const ModelConfig& config) const
{
if (!ValidatePerformanceMode(config.mode)) {
HDF_LOGE("PerformanceMode is invalid. mode=%d", config.mode);
return HDF_ERR_INVALID_PARAM;
}
if (!ValidatePriority(config.priority)) {
HDF_LOGE("Priority is invalid. priority=%d", config.priority);
return HDF_ERR_INVALID_PARAM;
}
return HDF_SUCCESS;
}
int32_t NnrtDeviceService::ValidateModel(const Model& model) const
{
if (model.allTensors.empty()) {
HDF_LOGE("Model has no tensors.");
return HDF_ERR_INVALID_PARAM;
}
if (model.subGraph.empty()) {
HDF_LOGE("Model has no subGraphs.");
return HDF_ERR_INVALID_PARAM;
}
if (model.nodes.empty()) {
HDF_LOGE("Model has no nodes.");
return HDF_ERR_INVALID_PARAM;
}
if (model.inputIndex.empty()) {
HDF_LOGE("Model has no input.");
return HDF_ERR_INVALID_PARAM;
}
if (model.outputIndex.empty()) {
HDF_LOGE("Model has no output.");
return HDF_ERR_INVALID_PARAM;
}
size_t tensorSize = model.allTensors.size();
for (auto index : model.inputIndex) {
if (index > tensorSize) {
HDF_LOGE("Input index is invalid, index=%u", index);
return HDF_ERR_INVALID_PARAM;
}
}
for (auto index : model.outputIndex) {
if (index > tensorSize) {
HDF_LOGE("Output index is invalid, index=%u", index);
return HDF_ERR_INVALID_PARAM;
}
}
return HDF_SUCCESS;
}
std::shared_ptr<mindspore::schema::MetaGraphT> NnrtDeviceService::TransModelToGraph(const Model& model) const
{
auto metaGraph = std::make_shared<mindspore::schema::MetaGraphT>();
metaGraph->name = model.name;
metaGraph->version = mindspore::Version();
std::unique_ptr<mindspore::schema::TensorT> transTensor{nullptr};
for (auto tensor : model.allTensors) {
transTensor = TransTensor(tensor);
if (transTensor == nullptr) {
HDF_LOGE("Transform tensor failed.");
return nullptr;
}
metaGraph->allTensors.emplace_back(std::move(transTensor));
}
metaGraph->inputIndex = model.inputIndex;
metaGraph->outputIndex = model.outputIndex;
// Transform node
std::unique_ptr<mindspore::schema::CNodeT> transNode {nullptr};
for (auto& node : model.nodes) {
transNode = TransNode(node);
if (transNode == nullptr) {
HDF_LOGE("Transform node failed, node name=%{public}s", node.name.c_str());
return nullptr;
}
metaGraph->nodes.emplace_back(std::move(transNode));
}
// Transform subgraph
const size_t numTensor = model.allTensors.size();
for (auto graph : model.subGraph) {
metaGraph->subGraph.emplace_back(TransSubGraph(graph, numTensor));
}
return metaGraph;
}
std::unique_ptr<mindspore::schema::TensorT> NnrtDeviceService::TransTensor(const Tensor& tensor) const
{
if (!ValidateDataType(tensor.dataType)) {
HDF_LOGE("DataType of tensor is invalid. dataType=%d", tensor.dataType);
return nullptr;
}
if (!ValidateFormat(tensor.format)) {
HDF_LOGE("Format of tensor is invalid. format=%d", tensor.format);
return nullptr;
}
auto schemaTensor = std::make_unique<mindspore::schema::TensorT>();
schemaTensor->name = tensor.name;
schemaTensor->dataType = static_cast<int32_t>(tensor.dataType);
schemaTensor->format = static_cast<mindspore::schema::Format>(tensor.format);
schemaTensor->dims = tensor.dims;
for (auto param : tensor.quantParams) {
auto quantParam = std::make_unique<mindspore::schema::QuantParamT>();
quantParam->scale = param.scale;
quantParam->zeroPoint = param.zeroPoint;
quantParam->numBits = param.numBits;
quantParam->inited = true;
schemaTensor->quantParams.emplace_back(std::move(quantParam));
}
if (tensor.data.fd != INVALID_FD) {
SharedBufferParser parser;
auto ret = parser.Init(tensor.data);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse tensor data failed.");
return nullptr;
}
auto data = parser.GetBufferPtr();
schemaTensor->data.resize(tensor.data.dataSize);
auto memRet = memcpy_s(const_cast<uint8_t*>(schemaTensor->data.data()),
tensor.data.dataSize, data, tensor.data.dataSize);
if (memRet != EOK) {
HDF_LOGW("Copy tensor data failed.");
return nullptr;
}
}
return schemaTensor;
}
std::unique_ptr<mindspore::schema::CNodeT> NnrtDeviceService::TransNode(const Node& node) const
{
auto cnode = std::make_unique<mindspore::schema::CNodeT>();
cnode->name = node.name;
cnode->inputIndex = node.inputIndex;
cnode->outputIndex = node.outputIndex;
cnode->quantType = static_cast<mindspore::schema::QuantType>(node.quantType);
auto& regInstance = NodeRegistry::GetSingleton();
auto parseFunc = regInstance.GetNodeFunc(node.nodeType);
auto primitive = parseFunc(node.nodeAttr);
if (primitive == nullptr) {
HDF_LOGE("Parse primitve data failed. node name=%{public}s", node.name.c_str());
return nullptr;
}
cnode->primitive = std::move(primitive);
return cnode;
}
std::unique_ptr<mindspore::schema::SubGraphT> NnrtDeviceService::TransSubGraph(const SubGraph& graph,
const size_t numTensor) const
{
auto subGraph = std::make_unique<mindspore::schema::SubGraphT>();
subGraph->name = graph.name;
subGraph->inputIndices = graph.inputIndices;
subGraph->outputIndices = graph.outputIndices;
subGraph->nodeIndices = graph.nodeIndices;
subGraph->tensorIndices.reserve(numTensor);
for (auto i = 0; i < numTensor; i++) {
subGraph->tensorIndices.emplace_back(static_cast<uint32_t>(i));
}
return subGraph;
}
std::shared_ptr<mindspore::Context> NnrtDeviceService::TransModelConfig(const ModelConfig& config) const
{
auto context = std::make_shared<mindspore::Context>();
const int cpuThreadNum = 2;
const int cpuNoAffinities = 0;
const int cpuBigCore = 1;
const int cpuLittleCore = 2;
context->SetThreadNum(cpuThreadNum);
int mode = cpuNoAffinities;
switch (config.mode) {
case PerformanceMode::PERFORMANCE_LOW:
case PerformanceMode::PERFORMANCE_MEDIUM:
mode = cpuLittleCore;
break;
case PerformanceMode::PERFORMANCE_HIGH:
case PerformanceMode::PERFORMANCE_EXTREME:
mode = cpuBigCore;
break;
default:
mode = cpuNoAffinities;
}
context->SetThreadAffinity(mode);
auto cpuInfo = std::make_shared<mindspore::CPUDeviceInfo>();
cpuInfo->SetEnableFP16(config.enableFloat16);
auto& deviceInfos = context->MutableDeviceInfo();
deviceInfos.emplace_back(cpuInfo);
return context;
}
} // V1_0
} // Nnrt
} // HDI
} // OHOS
@@ -0,0 +1,373 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "node_functions.h"
#include "node_registry.h"
#include <message_parcel.h>
#include <v1_0/node_attr_types.h>
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
PrimUniquePtr GetAddPrimitive(const std::vector<int8_t>& primitive)
{
AddFusion addAttr;
auto ret = ParsePrimitive<AddFusion>(primitive, addAttr, AddFusionBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of AddFusion operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_AddFusion;
auto attr = new (std::nothrow) mindspore::schema::AddFusionT;
if (attr == nullptr) {
HDF_LOGE("Create AddFusion primitive failed.");
return nullptr;
}
attr->activation_type = static_cast<mindspore::schema::ActivationType>(addAttr.activationType);
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetAvgPoolPrimitive(const std::vector<int8_t>& primitive)
{
AvgPoolFusion avgPoolAttr;
auto ret = ParsePrimitive<AvgPoolFusion>(primitive, avgPoolAttr, AvgPoolFusionBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of AvgPoolFusion operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_AvgPoolFusion;
auto attr = new (std::nothrow) mindspore::schema::AvgPoolFusionT;
if (attr == nullptr) {
HDF_LOGE("Create AvgPoolFusion primitive failed.");
return nullptr;
}
attr->kernel_size = avgPoolAttr.kernelSize;
attr->strides = avgPoolAttr.strides;
attr->pad = avgPoolAttr.pad;
attr->pad_mode = static_cast<mindspore::schema::PadMode>(avgPoolAttr.padMode);
attr->round_mode = static_cast<mindspore::schema::RoundMode>(avgPoolAttr.roundMode);
attr->format = static_cast<mindspore::schema::Format>(avgPoolAttr.format);
attr->global = avgPoolAttr.global;
attr->activation_type = static_cast<mindspore::schema::ActivationType>(avgPoolAttr.activationType);
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetConcatPrimitive(const std::vector<int8_t>& primitive)
{
Concat concatAttr;
auto ret = ParsePrimitive<Concat>(primitive, concatAttr, ConcatBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of Concat operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_Concat;
auto attr = new (std::nothrow) mindspore::schema::ConcatT;
if (attr == nullptr) {
HDF_LOGE("Create concat primitive failed.");
return nullptr;
}
attr->axis = concatAttr.axis;
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetConv2dPrimitive(const std::vector<int8_t>& primitive)
{
Conv2DFusion conv2dAttr;
auto ret = ParsePrimitive<Conv2DFusion>(primitive, conv2dAttr, Conv2DFusionBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of Conv2DFusion operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_Conv2DFusion;
auto attr = new (std::nothrow) mindspore::schema::Conv2DFusionT;
if (attr == nullptr) {
HDF_LOGE("Create Conv2DFusion primitive failed.");
return nullptr;
}
attr->kernel_size = conv2dAttr.kernelSize;
attr->stride = conv2dAttr.stride;
attr->dilation = conv2dAttr.dilation;
attr->pad_mode = static_cast<mindspore::schema::PadMode>(conv2dAttr.padMode);
attr->pad_list = conv2dAttr.padList;
attr->group = conv2dAttr.group;
attr->in_channel = conv2dAttr.inChannel;
attr->out_channel = conv2dAttr.outChannel;
attr->activation_type = static_cast<mindspore::schema::ActivationType>(conv2dAttr.activationType);
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetFullConnectionPrimitive(const std::vector<int8_t>& primitive)
{
FullConnection fullConnAttr;
auto ret = ParsePrimitive<FullConnection>(primitive, fullConnAttr, FullConnectionBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of FullConnection operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_FullConnection;
auto attr = new (std::nothrow) mindspore::schema::FullConnectionT;
if (attr == nullptr) {
HDF_LOGE("Create FullConnection primitive failed.");
return nullptr;
}
attr->has_bias = fullConnAttr.hasBias;
attr->use_axis = fullConnAttr.useAxis;
attr->axis = fullConnAttr.axis;
attr->activation_type = static_cast<mindspore::schema::ActivationType>(fullConnAttr.activationType);
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetMaxPoolFusionPrimitive(const std::vector<int8_t>& primitive)
{
MaxPoolFusion maxPoolAttr;
auto ret = ParsePrimitive<MaxPoolFusion>(primitive, maxPoolAttr, MaxPoolFusionBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of MaxPoolFusion operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_MaxPoolFusion;
auto attr = new (std::nothrow) mindspore::schema::MaxPoolFusionT;
if (attr == nullptr) {
HDF_LOGE("Create MaxPoolFusion primitive failed.");
return nullptr;
}
attr->kernel_size = maxPoolAttr.kernelSize;
attr->strides = maxPoolAttr.strides;
attr->pad = maxPoolAttr.pad;
attr->pad_mode = static_cast<mindspore::schema::PadMode>(maxPoolAttr.padMode);
attr->format = static_cast<mindspore::schema::Format>(maxPoolAttr.format);
attr->global = maxPoolAttr.global;
attr->activation_type = static_cast<mindspore::schema::ActivationType>(maxPoolAttr.activationType);
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetMatMulFusionPrimitive(const std::vector<int8_t>& primitive)
{
MatMulFusion matmulAttr;
auto ret = ParsePrimitive<MatMulFusion>(primitive, matmulAttr, MatMulFusionBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of MatMulFusion operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_MatMulFusion;
auto attr = new (std::nothrow) mindspore::schema::MatMulFusionT;
if (attr == nullptr) {
HDF_LOGE("Create MatMulFusion primitive failed.");
return nullptr;
}
attr->transpose_a = matmulAttr.transposeA;
attr->transpose_b = matmulAttr.transposeB;
attr->activation_type = static_cast<mindspore::schema::ActivationType>(matmulAttr.activationType);
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetSoftmaxPrimitive(const std::vector<int8_t>& primitive)
{
Softmax softmaxAttr;
auto ret = ParsePrimitive<Softmax>(primitive, softmaxAttr, SoftmaxBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of Softmax operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_Softmax;
auto attr = new (std::nothrow) mindspore::schema::SoftmaxT;
if (attr == nullptr) {
HDF_LOGE("Create Softmax primitive failed.");
return nullptr;
}
attr->axis = softmaxAttr.axis;
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetReshapePrimitive(const std::vector<int8_t>& primitive)
{
Reshape reshapeAttr;
auto ret = ParsePrimitive<Reshape>(primitive, reshapeAttr, ReshapeBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of Reshape operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_Reshape;
auto attr = new (std::nothrow) mindspore::schema::ReshapeT;
if (attr == nullptr) {
HDF_LOGE("Create Reshape primitive failed.");
return nullptr;
}
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetScaleFusionPrimitive(const std::vector<int8_t>& primitive)
{
ScaleFusion scaleAttr;
auto ret = ParsePrimitive<ScaleFusion>(primitive, scaleAttr, ScaleFusionBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of ScaleFusion operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_ScaleFusion;
auto attr = new (std::nothrow) mindspore::schema::ScaleFusionT;
if (attr == nullptr) {
HDF_LOGE("Create ScaleFusion primitive failed.");
return nullptr;
}
attr->axis = scaleAttr.axis;
attr->activation_type = static_cast<mindspore::schema::ActivationType>(scaleAttr.activationType);
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetActivationPrimitive(const std::vector<int8_t>& primitive)
{
Activation actAttr;
auto ret = ParsePrimitive<Activation>(primitive, actAttr, ActivationBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of Activation operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_Activation;
auto attr = new (std::nothrow) mindspore::schema::ActivationT;
if (attr == nullptr) {
HDF_LOGE("Create Activation primitive failed.");
return nullptr;
}
attr->alpha = actAttr.alpha;
attr->min_val = actAttr.minVal;
attr->max_val = actAttr.maxVal;
attr->approximate = actAttr.approximate;
attr->activation_type = static_cast<mindspore::schema::ActivationType>(actAttr.activationType);
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetQuantDTypeCastPrimitive(const std::vector<int8_t>& primitive)
{
QuantDTypeCast quantAttr;
auto ret = ParsePrimitive<QuantDTypeCast>(primitive, quantAttr, QuantDTypeCastBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of QuantDTypeCast operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_QuantDTypeCast;
auto attr = new (std::nothrow) mindspore::schema::QuantDTypeCastT;
if (attr == nullptr) {
HDF_LOGE("Create QuantDTypeCast primitive failed.");
return nullptr;
}
attr->src_t = quantAttr.srcT;
attr->dst_t = quantAttr.dstT;
prim->value.value = attr;
return prim;
}
PrimUniquePtr GetMulFusionPrimitive(const std::vector<int8_t>& primitive)
{
MulFusion mulAttr;
auto ret = ParsePrimitive<MulFusion>(primitive, mulAttr, MulFusionBlockUnmarshalling);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse primitive data of MulFusion operator failed.");
return nullptr;
}
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
prim->value.type = mindspore::schema::PrimitiveType_MulFusion;
auto attr = new (std::nothrow) mindspore::schema::MulFusionT;
if (attr == nullptr) {
HDF_LOGE("Create MulFusion primitive failed.");
return nullptr;
}
attr->activation_type = static_cast<mindspore::schema::ActivationType>(mulAttr.activationType);
prim->value.value = attr;
return prim;
}
REGISTER_NODE(Activation, NodeType::NODE_TYPE_ACTIVATION, GetActivationPrimitive);
REGISTER_NODE(AddFusion, NodeType::NODE_TYPE_ADD_FUSION, GetAddPrimitive);
REGISTER_NODE(AvgPoolFusion, NodeType::NODE_TYPE_AVGPOOL_FUSION, GetAvgPoolPrimitive);
REGISTER_NODE(Concat, NodeType::NODE_TYPE_CONCAT, GetConcatPrimitive);
REGISTER_NODE(Conv2DFusion, NodeType::NODE_TYPE_CONV2D_FUSION, GetConv2dPrimitive);
REGISTER_NODE(FullConnection, NodeType::NODE_TYPE_FULL_CONNECTION, GetFullConnectionPrimitive);
REGISTER_NODE(MaxPoolFusion, NodeType::NODE_TYPE_MAX_POOL_FUSION, GetMaxPoolFusionPrimitive);
REGISTER_NODE(MatMulFusion, NodeType::NODE_TYPE_MATMUL_FUSION, GetMatMulFusionPrimitive);
REGISTER_NODE(Reshape, NodeType::NODE_TYPE_RESHAPE, GetReshapePrimitive);
REGISTER_NODE(Softmax, NodeType::NODE_TYPE_SOFTMAX, GetSoftmaxPrimitive);
REGISTER_NODE(ScaleFusion, NodeType::NODE_TYPE_SCALE_FUSION, GetScaleFusionPrimitive);
REGISTER_NODE(QuantDTypeCast, NodeType::NODE_TYPE_QUANT_DTYPE_CAST, GetQuantDTypeCastPrimitive);
REGISTER_NODE(MulFusion, NodeType::NODE_TYPE_MUL_FUSION, GetMulFusionPrimitive);
} // namespace V1_0
} // namespace Nnrt
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "node_registry.h"
#include "utils/hdf_log.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
NodeRegistry& NodeRegistry::GetSingleton()
{
static NodeRegistry registry;
return registry;
}
NodeRegistry::Registrar::Registrar(NodeType type, std::function<PrimUniquePtr(const std::vector<int8_t>&)> nodeFunc)
{
auto& registry = NodeRegistry::GetSingleton();
if (registry.m_nodeRegs.find(type) != registry.m_nodeRegs.end()) {
HDF_LOGW("Node has been registered. nodeType=%d", type);
} else {
registry.m_nodeRegs[type] = nodeFunc;
}
}
std::function<PrimUniquePtr(const std::vector<int8_t>&)> NodeRegistry::GetNodeFunc(NodeType type) const
{
if (m_nodeRegs.find(type) == m_nodeRegs.end()) {
HDF_LOGW("Node type is not found. nodeType=%d", type);
return nullptr;
}
return m_nodeRegs.at(type);
}
bool NodeRegistry::IsNodeTypeExist(NodeType type) const
{
if (m_nodeRegs.find(type) == m_nodeRegs.end()) {
return false;
}
return true;
}
} // namespace V1_0
} // namespace Nnrt
} // namespace HDI
} // namespace OHOS
@@ -0,0 +1,412 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "prepared_model_service.h"
#include <hdf_base.h>
#include "securec.h"
#include "utils/hdf_log.h"
#include "shared_buffer_parser.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
PreparedModelService::PreparedModelService(std::shared_ptr<mindspore::Context> context)
: m_context(context) {}
PreparedModelService::~PreparedModelService()
{
if (m_cacheBuffer != nullptr) {
m_cacheBuffer->CloseAshmem();
}
for (auto& inputAsh : m_inputAshmems) {
inputAsh->UnmapAshmem();
inputAsh->CloseAshmem();
}
for (auto& outputAsh : m_outputAshmems) {
outputAsh->UnmapAshmem();
outputAsh->CloseAshmem();
}
}
int32_t PreparedModelService::ExportModelCache(std::vector<SharedBuffer>& modelCache)
{
if (!modelCache.empty()) {
HDF_LOGE("The parameters of ExportModelCache should be an empty vector.");
return HDF_ERR_INVALID_PARAM;
}
if (m_cacheBuffer != nullptr) {
auto fd = m_cacheBuffer->GetAshmemFd();
auto size = m_cacheBuffer->GetAshmemSize();
// SharedBuffer: fd, bufferSize, offset, dataSize
modelCache.emplace_back(SharedBuffer{fd, size, 0, size});
return HDF_SUCCESS;
}
auto size = m_builder.GetSize();
auto buffer = m_builder.GetBufferPointer();
const char* name = m_graph != nullptr ? m_graph->name.c_str() : "CacheModel";
sptr<Ashmem> cache = Ashmem::CreateAshmem(name, size);
if (cache == nullptr) {
HDF_LOGE("Create shared memory failed.");
return HDF_ERR_MALLOC_FAIL;
}
bool ret = cache->MapReadAndWriteAshmem();
if (!ret) {
HDF_LOGE("Map fd to write cache failed.");
return HDF_FAILURE;
}
ret = cache->WriteToAshmem(buffer, size, 0);
cache->UnmapAshmem();
if (!ret) {
HDF_LOGE("Write cache failed.");
return HDF_FAILURE;
}
m_cacheBuffer = cache;
// SharedBuffer: fd, bufferSize, offset, dataSize
modelCache.emplace_back(SharedBuffer {cache->GetAshmemFd(), cache->GetAshmemSize(), 0, cache->GetAshmemSize()});
return HDF_SUCCESS;
}
int32_t PreparedModelService::Run(const std::vector<IOTensor>& inputs, const std::vector<IOTensor>& outputs,
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough)
{
auto ret = SetInputs(inputs);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Inputs tensor is invalid.");
return ret;
}
if (!m_isDynamicShape) {
ret = SetOutputs(outputs);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Output tensor is invalid.");
ResetInputAndOutput();
return ret;
}
}
auto msRet = m_model->Predict(m_inputs, &m_outputs);
if (msRet != mindspore::kSuccess) {
HDF_LOGE("Run model failed.");
ResetInputAndOutput();
return HDF_FAILURE;
}
ret = UpdateOutput(outputs, outputsDims, isOutputBufferEnough);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Update output dimension or data failed.");
ResetInputAndOutput();
return ret;
}
ResetInputAndOutput();
return HDF_SUCCESS;
}
int32_t PreparedModelService::UpdateOutput(const std::vector<IOTensor>& outputs,
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough)
{
bool isEnough {true};
size_t outputSize = m_outputs.size();
isOutputBufferEnough.resize(outputSize, true);
for (size_t i = 0; i < outputSize; i++) {
auto& msOutput = m_outputs[i];
auto& output = outputs[i];
auto msShape = msOutput.Shape();
outputsDims.emplace_back(msShape.begin(), msShape.end());
auto dataSize = msOutput.DataSize();
if (dataSize > output.data.bufferSize) {
HDF_LOGE("Output buffer is not enough. actual size %{public}zu, buffer size %{public}u",
dataSize, output.data.bufferSize);
isOutputBufferEnough[i] = false;
isEnough= false;
}
if (isEnough && m_isDynamicShape) {
auto msData = msOutput.MutableData();
SharedBufferParser parser;
auto ret = parser.Init(output.data);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Parse %zu th output data failed.", i);
return HDF_ERR_INVALID_PARAM;
}
auto data = parser.GetBufferPtr();
auto memRet = memcpy_s(data, dataSize, msData, dataSize);
if (memRet != EOK) {
HDF_LOGE("Copy output memory failed.");
return HDF_FAILURE;
}
}
}
return HDF_SUCCESS;
}
void PreparedModelService::ResetInputAndOutput()
{
for (auto& msInput : m_inputs) {
msInput.SetData(nullptr);
}
if (!m_isDynamicShape) {
for (auto& msOutput : m_outputs) {
msOutput.SetData(nullptr);
}
}
}
int32_t PreparedModelService::Compile(std::shared_ptr<mindspore::schema::MetaGraphT> graph)
{
if (graph == nullptr) {
HDF_LOGE("Graph cannot be nullptr");
return HDF_ERR_INVALID_PARAM;
}
for (auto i : graph->inputIndex) {
auto inputShape = graph->allTensors[i]->dims;
auto iter = std::find(inputShape.begin(), inputShape.end(), DYNAMIC_SHAPE_FLAG);
if (iter != inputShape.end()) {
m_isDynamicShape = true;
break;
}
}
auto offset = mindspore::schema::MetaGraph::Pack(m_builder, graph.get());
m_builder.Finish(offset);
mindspore::schema::FinishMetaGraphBuffer(m_builder, offset);
auto modelSize = m_builder.GetSize();
uint8_t* modelBuffer = m_builder.GetBufferPointer();
if (modelBuffer == nullptr) {
HDF_LOGE("Model is invalid.");
return HDF_FAILURE;
}
m_model = std::make_shared<mindspore::Model>();
mindspore::Status msRet = m_model->Build(modelBuffer, modelSize, mindspore::kMindIR, m_context);
if (msRet != mindspore::kSuccess) {
HDF_LOGE("Prepare model failed, please make sure model is validate.");
return HDF_FAILURE;
}
auto ret = GetMSInputsAndOutputs();
if (ret != HDF_SUCCESS) {
HDF_LOGE("Model without inputs or outputs is invalid.");
return ret;
}
return HDF_SUCCESS;
}
int32_t PreparedModelService::Compile(const void* modelBuffer, size_t length)
{
if (modelBuffer == nullptr || length == 0) {
HDF_LOGE("ModelBuffer cannot be nullptr and length cannot be zero.");
return HDF_ERR_INVALID_PARAM;
}
m_model = std::make_shared<mindspore::Model>();
mindspore::Status msRet = m_model->Build(modelBuffer, length, mindspore::kMindIR, m_context);
if (msRet != mindspore::kSuccess) {
HDF_LOGE("Prepare model from cache failed, please make sure model cache is valid.");
return HDF_FAILURE;
}
auto ret = GetMSInputsAndOutputs();
if (ret != HDF_SUCCESS) {
HDF_LOGE("Model without inputs or outputs is invalid.");
return ret;
}
for (auto input : m_inputs) {
auto shapes = input.Shape();
if (std::find(shapes.begin(), shapes.end(), DYNAMIC_SHAPE_FLAG) != shapes.end()) {
m_isDynamicShape = true;
break;
}
}
return HDF_SUCCESS;
}
int32_t PreparedModelService::SetInputs(const std::vector<IOTensor>& inputs)
{
if (inputs.size() != m_inputs.size()) {
HDF_LOGE("inputs size is invalid. expect: %zu, actual: %zu", m_inputs.size(), inputs.size());
return HDF_ERR_INVALID_PARAM;
}
for (auto& ash : m_inputAshmems) {
ash->UnmapAshmem();
ash->CloseAshmem();
}
m_inputAshmems.clear();
int32_t ret {0};
size_t inputSize = m_inputs.size();
std::vector<std::vector<int64_t>> tmpAllDims;
for (size_t i = 0; i < inputSize; i++) {
auto& input = inputs[i];
auto& msInput = m_inputs[i];
ret = CompareTensor(input, msInput);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Inputs tensor is not match that of model. Please check input tensor.");
return ret;
}
tmpAllDims.emplace_back(input.dimensions.begin(), input.dimensions.end());
}
if (m_isDynamicShape) {
auto msRet = m_model->Resize(m_inputs, tmpAllDims);
if (msRet != mindspore::kSuccess) {
HDF_LOGE("Resize for dynamic inputs failed.");
return HDF_FAILURE;
}
ret = GetMSInputsAndOutputs();
if (ret != HDF_SUCCESS) {
HDF_LOGE("Get ms inputs or outputs failed after resize.");
return ret;
}
}
for (size_t i = 0; i < inputSize; i++) {
auto& input = inputs[i];
auto& msInput = m_inputs[i];
sptr<Ashmem> ashptr = ParseBuffer(input.data);
if (ashptr == nullptr) {
HDF_LOGE("Parse %zuth input data failed.", i);
return HDF_ERR_INVALID_PARAM;
}
auto data = const_cast<void*>(ashptr->ReadFromAshmem(input.data.dataSize, 0));
msInput.SetData(data);
m_inputAshmems.emplace_back(ashptr);
}
return HDF_SUCCESS;
}
int32_t PreparedModelService::SetOutputs(const std::vector<IOTensor>& outputs)
{
HDF_LOGI("Start Set outputs, m_outputs size=%zu", m_outputs.size());
if (outputs.size() != m_outputs.size()) {
HDF_LOGE("outputs size is invalid. expect: %{public}zu, actual: %{public}zu", m_outputs.size(), outputs.size());
return HDF_ERR_INVALID_PARAM;
}
for (auto ash : m_outputAshmems) {
ash->UnmapAshmem();
ash->CloseAshmem();
}
m_outputAshmems.clear();
for (size_t i = 0; i < m_outputs.size(); i++) {
auto& output = outputs[i];
auto& msOutput = m_outputs[i];
sptr<Ashmem> ashptr = ParseBuffer(output.data);
if (ashptr == nullptr) {
HDF_LOGE("Parse %{public}zu th output data failed.", i);
return HDF_ERR_INVALID_PARAM;
}
auto data = const_cast<void*>(ashptr->ReadFromAshmem(output.data.dataSize, 0));
msOutput.SetAllocator(nullptr);
msOutput.SetData(data);
m_outputAshmems.emplace_back(ashptr);
}
return HDF_SUCCESS;
}
int32_t PreparedModelService::GetMSInputsAndOutputs()
{
m_inputs = m_model->GetInputs();
if (m_inputs.empty()) {
HDF_LOGE("Get inputs failed.");
return HDF_FAILURE;
}
m_outputs = m_model->GetOutputs();
if (m_outputs.empty()) {
HDF_LOGE("Get outputs failed.");
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
int32_t PreparedModelService::CompareTensor(const IOTensor& tensor, const mindspore::MSTensor& msTensor)
{
auto dataType = static_cast<DataType>(msTensor.DataType());
if (tensor.dataType != dataType) {
HDF_LOGE("Data type of tensor is not match that of model.");
return HDF_ERR_INVALID_PARAM;
}
auto format = static_cast<Format>(msTensor.format());
if (tensor.format != format) {
HDF_LOGE("Format of tensor is not match that of model.");
return HDF_ERR_INVALID_PARAM;
}
for (size_t i = 0; i < tensor.dimensions.size(); i++) {
if (msTensor.Shape()[i] != DYNAMIC_SHAPE_FLAG && tensor.dimensions[i] != msTensor.Shape()[i]) {
HDF_LOGE("The Shape of tensor is not match that of model.");
return HDF_ERR_INVALID_PARAM;
}
}
return HDF_SUCCESS;
}
sptr<Ashmem> PreparedModelService::ParseBuffer(const SharedBuffer& buffer)
{
if (buffer.fd == -1) {
HDF_LOGE("Invalid buffer fd, it cannot be -1.");
return nullptr;
}
HDF_LOGW("NNRT buffer fd=%{public}d, length=%{public}u", buffer.fd, buffer.dataSize);
sptr<Ashmem> ashptr = new (std::nothrow) Ashmem(buffer.fd, buffer.bufferSize);
if (ashptr == nullptr) {
HDF_LOGE("Create shared memory failed.");
return nullptr;
}
if (!ashptr->MapReadAndWriteAshmem()) {
HDF_LOGE("Map buffer fd to address failed.");
return nullptr;
}
const void* data = ashptr->ReadFromAshmem(buffer.dataSize, buffer.offset);
if (data == nullptr) {
HDF_LOGE("Get data address failed.");
ashptr->UnmapAshmem();
ashptr->CloseAshmem();
return nullptr;
}
return ashptr;
}
} // V1_0
} // Nnrt
} // HDI
} // OHOS
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OHOS_HDI_NNR_V1_0_UTILS_H
#define OHOS_HDI_NNR_V1_0_UTILS_H
#include "shared_buffer_parser.h"
#include <hdf_base.h>
#include "ashmem.h"
#include "v1_0/nnrt_types.h"
#include "utils/hdf_log.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
SharedBufferParser::~SharedBufferParser()
{
if (m_ashptr != nullptr) {
m_ashptr->UnmapAshmem();
m_ashptr->CloseAshmem();
m_bufferAddr = nullptr;
}
}
int32_t SharedBufferParser::Init(const std::string& name, int32_t size)
{
HDF_LOGI("Init SharedBufferParser from name and size.");
sptr<Ashmem> ashptr = Ashmem::CreateAshmem(name.c_str(), size);
if (ashptr == nullptr) {
HDF_LOGE("Create ashmen from size failed.");
return HDF_FAILURE;
}
SharedBuffer buffer;
buffer.fd = ashptr->GetAshmemFd();
buffer.bufferSize = ashptr->GetAshmemSize();
buffer.offset = 0;
buffer.dataSize = size;
auto ret = Init(buffer);
if (ret != HDF_SUCCESS) {
HDF_LOGE("Init SharedBufferParser failed.");
return ret;
}
return HDF_SUCCESS;
}
int32_t SharedBufferParser::Init(const SharedBuffer& buffer)
{
if (buffer.fd == INVALID_FD) {
HDF_LOGE("Invalid buffer fd, it cannot be %{public}d.", INVALID_FD);
return HDF_ERR_INVALID_PARAM;
}
m_ashptr = new (std::nothrow) Ashmem(buffer.fd, buffer.bufferSize);
if (m_ashptr == nullptr) {
HDF_LOGE("Create ashmem failed.");
return HDF_FAILURE;
}
if (!m_ashptr->MapReadAndWriteAshmem()) {
HDF_LOGE("Map buffer fd to address failed.");
return HDF_FAILURE;
}
auto bufferAddr = m_ashptr->ReadFromAshmem(buffer.dataSize, buffer.offset);
if (bufferAddr == nullptr) {
HDF_LOGE("Invalid dataSize or offset of SharedBuffer.");
return HDF_ERR_INVALID_PARAM;
}
m_bufferAddr = const_cast<void*>(bufferAddr);
m_buffer = buffer;
return HDF_SUCCESS;
}
void* SharedBufferParser::GetBufferPtr()
{
return m_bufferAddr;
}
SharedBuffer SharedBufferParser::GetBuffer()
{
return m_buffer;
}
} // V1_0
} // Nnrt
} // HDI
} // OHOS
#endif // OHOS_HDI_NNR_V1_0_UTILS_H
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "validation.h"
namespace OHOS {
namespace HDI {
namespace Nnrt {
namespace V1_0 {
int32_t ValidatePerformanceMode(PerformanceMode mode)
{
if (mode < PerformanceMode::PERFORMANCE_NONE || mode > PerformanceMode::PERFORMANCE_EXTREME) {
return false;
}
return true;
}
int32_t ValidatePriority(Priority priority)
{
if (priority < Priority::PRIORITY_NONE || priority > Priority::PRIORITY_HIGH) {
return false;
}
return true;
}
int32_t ValidateDataType(DataType dataType)
{
if (dataType < DataType::DATA_TYPE_UNKNOWN || dataType > DataType::DATA_TYPE_FLOAT64) {
return false;
}
if (dataType > DataType::DATA_TYPE_UNKNOWN && dataType < DataType::DATA_TYPE_BOOL) {
return false;
}
if (dataType > DataType::DATA_TYPE_BOOL && dataType < DataType::DATA_TYPE_INT8) {
return false;
}
if (dataType > DataType::DATA_TYPE_UINT64 && dataType < DataType::DATA_TYPE_FLOAT16) {
return false;
}
return true;
}
int32_t ValidateFormat(Format format)
{
if (format < Format::FORMAT_NONE || format > Format::FORMAT_NHWC) {
return false;
}
return true;
}
} // namespace V1_0
} // namespace Nnrt
} // namespace HDI
} // namespace OHOS
+133
View File
@@ -0,0 +1,133 @@
# Copyright (c) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/ohos.gni")
config("nnrt_config") {
cflags_cc = [ "-fexceptions" ]
}
nnrt_sources = [
"native/device_manager.cpp",
"native/device_registrar.cpp",
"native/hdi_device.cpp",
"native/hdi_prepared_model.cpp",
"native/memory_manager.cpp",
"native/transform.cpp",
"native/nn_tensor.cpp",
"native/validation.cpp",
"native/inner_model.cpp",
"native/compilation.cpp",
"native/execution_plan.cpp",
"native/executor.cpp",
"native/neural_network_runtime.cpp",
"native/ops_builder.cpp",
"native/ops_registry.cpp",
]
ops_sources = [
"native/ops/add_builder.cpp",
"native/ops/argmax_builder.cpp",
"native/ops/avgpool_builder.cpp",
"native/ops/pooling_builder.cpp",
"native/ops/batch_to_space_nd_builder.cpp",
"native/ops/bias_add_builder.cpp",
"native/ops/cast_builder.cpp",
"native/ops/concat_builder.cpp",
"native/ops/conv2d_builder.cpp",
"native/ops/conv2d_transpose_builder.cpp",
"native/ops/depthwise_conv2d_native_builder.cpp",
"native/ops/div_builder.cpp",
"native/ops/eltwise_builder.cpp",
"native/ops/expandims_builder.cpp",
"native/ops/fullconnection_builder.cpp",
"native/ops/maxpool_builder.cpp",
"native/ops/slice_builder.cpp",
"native/ops/softmax_builder.cpp",
"native/ops/space_to_batch_nd_builder.cpp",
"native/ops/split_builder.cpp",
"native/ops/sqrt_builder.cpp",
"native/ops/squared_difference_builder.cpp",
"native/ops/squeeze_builder.cpp",
"native/ops/stack_builder.cpp",
"native/ops/strided_slice_builder.cpp",
"native/ops/sub_builder.cpp",
"native/ops/tanh_builder.cpp",
"native/ops/tile_builder.cpp",
"native/ops/top_k_builder.cpp",
"native/ops/transpose_builder.cpp",
"native/ops/unsqueeze_builder.cpp",
"native/ops/batchnorm_builder.cpp",
"native/ops/fill_builder.cpp",
"native/ops/matmul_builder.cpp",
"native/ops/gather_builder.cpp",
"native/ops/gelu_builder.cpp",
"native/ops/hswish_builder.cpp",
"native/ops/layernorm_builder.cpp",
"native/ops/maximum_builder.cpp",
"native/ops/lessequal_builder.cpp",
"native/ops/mul_builder.cpp",
"native/ops/onehot_builder.cpp",
"native/ops/pad_builder.cpp",
"native/ops/pow_builder.cpp",
"native/ops/prelu_builder.cpp",
"native/ops/quant_dtype_cast_builder.cpp",
"native/ops/reduceall_builder.cpp",
"native/ops/reducemean_builder.cpp",
"native/ops/reduceprod_builder.cpp",
"native/ops/relu_builder.cpp",
"native/ops/relu6_builder.cpp",
"native/ops/reshape_builder.cpp",
"native/ops/resize_bilinear_builder.cpp",
"native/ops/rsqrt_builder.cpp",
"native/ops/scale_builder.cpp",
"native/ops/shape_builder.cpp",
"native/ops/sigmoid_builder.cpp",
]
ohos_shared_library("libneural_network_runtime") {
sources = nnrt_sources
sources += ops_sources
include_dirs = [
"//commonlibrary/c_utils/base/include",
"//drivers/hdf_core/adapter/uhdf/posix/include",
"//drivers/hdf_core/adapter/uhdf2/include/hdi",
"//drivers/hdf_core/adapter/uhdf2/ipc/include",
"//drivers/hdf_core/framework/include/core",
"//drivers/hdf_core/framework/include/utils",
"//drivers/hdf_core/framework/core/common/include/host",
"//foundation/ai/neural_network_runtime",
"//foundation/communication/ipc/interfaces/innerkits/ipc_core/include",
"//third_party/googletest/googletest/include/gtest",
"//third_party/mindspore/mindspore/lite/mindir/include"
]
install_images = [
"system",
"updater"
]
public_configs = [ ":nnrt_config" ]
external_deps = [
"hilog_native:libhilog",
"hitrace_native:libhitracechain",
"c_utils:utils",
"hdf_core:libhdf_utils",
"drivers_interface_nnrt:libnnrt_proxy_1.0",
"mindspore:mindir"
]
subsystem_name = "ai"
part_name = "neural_network_runtime"
}
+714
View File
@@ -0,0 +1,714 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "compilation.h"
#include <sys/stat.h>
#include <unistd.h>
#include <cstdio>
#include <sys/types.h>
#include <fstream>
#include <climits>
#include "common/utils.h"
#include "common/scoped_trace.h"
#include "validation.h"
#include "device_manager.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
constexpr int MAX_MODEL_SIZE = 200 * 1024 * 1024; // 200MB
constexpr int OCT_UNIT = 8;
constexpr int NULL_PTR_LENGTH = 0;
constexpr int NUMBER_CACHE_INFO_MEMBERS = 3;
// CRC16 Table is created based on the Polynomial of G(x) = x^16 + x^12 + x^15 + 1 and
// CRC register initialization value of "0" (0x0000)
static const unsigned short CRC16_TAB[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
Compilation::Compilation(const InnerModel* innerModel)
: m_liteGraph(innerModel->GetLiteGraphs()),
m_inputTensors(innerModel->GetInputTensors()),
m_outputTensors(innerModel->GetOutputTensors()) {}
OH_NN_ReturnCode Compilation::SetDevice(size_t deviceId)
{
if (m_isBuild) {
LOGE("Cannot set deviceId after compilation finish.");
return OH_NN_OPERATION_FORBIDDEN;
}
auto& deviceManager = DeviceManager::GetInstance();
std::shared_ptr<Device> availableDevice = deviceManager.GetDevice(deviceId);
if (availableDevice == nullptr) {
LOGE("[Compilation] DeviceId does not exist, deviceId=%zu", deviceId);
return OH_NN_INVALID_PARAMETER;
}
std::vector<bool> supportedList;
OH_NN_ReturnCode ret = availableDevice->GetSupportedOperation(m_liteGraph, supportedList);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] SetDevice failed, error happened when getting supported operation.");
return ret;
}
for (bool isSupport : supportedList) {
if (!isSupport) {
LOGE("[Compilation] SetDevice failed, current device not support the model, device id: %zu.", deviceId);
return OH_NN_FAILED;
}
}
bool supportDynamic;
ret = availableDevice->IsDynamicInputSupported(supportDynamic);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] SetDevice failed, error happened when checking whether device supports dynamic input.");
return ret;
}
if (IsDynamicShape() && (!supportDynamic)) {
LOGE("[Compilation] SetDevice failed."
"The device does not support dynamic shape inputs, but the model has dynamic inputs.");
return OH_NN_FAILED;
}
m_device = availableDevice;
m_deviceId = deviceId;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::SetCacheDir(const std::string& cacheModelPath, uint32_t version)
{
if (m_isBuild) {
LOGE("Cannot set cache after compilation finish.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (m_device == nullptr) {
LOGE("The parameter of m_device is nullptr, please call SetDevice function before calling SetCacheDir.");
return OH_NN_OPERATION_FORBIDDEN;
}
bool isSupportedCache {false};
OH_NN_ReturnCode ret = m_device->IsModelCacheSupported(isSupportedCache);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Fail to query whether the device is available to save cache model.");
return ret;
}
if (!isSupportedCache) {
LOGE("[Compilation] The device is unavailable to save cache model.");
return OH_NN_OPERATION_FORBIDDEN;
}
char realPathRes[PATH_MAX];
const char* filePath = realpath(cacheModelPath.c_str(), realPathRes);
if (filePath == nullptr) {
LOGE("[Compilation] The cache model path is invalid.");
return OH_NN_INVALID_PARAMETER;
}
struct stat fileInfo;
if (stat(filePath, &fileInfo) != 0) {
LOGE("[Compilation] The cache directory does not exist or cannot be accessed.");
return OH_NN_INVALID_PARAMETER;
}
if (!(fileInfo.st_mode & S_IFDIR)) {
LOGE("[Compilation] The cache model path is not a directory.");
return OH_NN_INVALID_PARAMETER;
}
m_cachePath = (std::string)filePath + "/";
m_version = version;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::SetPerformance(OH_NN_PerformanceMode performance)
{
if (m_isBuild) {
LOGE("[Compilation] Cannot set performance after compilation finish.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (m_device == nullptr) {
LOGE("Cannot set performance before set device, please set device first");
return OH_NN_OPERATION_FORBIDDEN;
}
bool isSupportedPerformance {false};
OH_NN_ReturnCode ret = m_device->IsPerformanceModeSupported(isSupportedPerformance);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Call device %zu failed.", m_deviceId);
return ret;
}
if (!isSupportedPerformance) {
LOGE("[Compilation] This device %zu is not support performance setting.", m_deviceId);
return OH_NN_OPERATION_FORBIDDEN;
}
if (!Validation::ValidatePerformanceMode(performance)) {
LOGE("[Compilation] SetPerformance passed invalid performance=%d", performance);
return OH_NN_INVALID_PARAMETER;
}
m_performance = performance;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::SetPriority(OH_NN_Priority priority)
{
if (m_isBuild) {
LOGE("[Compilation] Cannot set priority after compilation finish.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (m_device == nullptr) {
LOGE("Cannot set priority before set device, please set device first");
return OH_NN_OPERATION_FORBIDDEN;
}
bool isSupportedPriority {false};
OH_NN_ReturnCode ret = m_device->IsPrioritySupported(isSupportedPriority);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Call device %zu failed.", m_deviceId);
return ret;
}
if (!isSupportedPriority) {
LOGE("[Compilation] This device %zu is not support priority setting.", m_deviceId);
return OH_NN_OPERATION_FORBIDDEN;
}
if (!Validation::ValidatePriority(priority)) {
LOGE("[Compilation] SetPriority passed invalid priority=%d", priority);
return OH_NN_INVALID_PARAMETER;
}
m_priority = priority;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::SetEnableFp16(bool isFp16)
{
if (m_isBuild) {
LOGE("[Compilation] Cannot enable float16 after compilation finish.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (m_device == nullptr) {
LOGE("Cannot set enable fp16 before set device, please set device first");
return OH_NN_OPERATION_FORBIDDEN;
}
bool isSupportedFp16 {false};
OH_NN_ReturnCode ret = m_device->IsFloat16PrecisionSupported(isSupportedFp16);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Call device %zu failed.", m_deviceId);
return ret;
}
if (!isSupportedFp16) {
LOGE("[Compilation] This device %zu is not support float16 precision setting.", m_deviceId);
return OH_NN_OPERATION_FORBIDDEN;
}
m_enableFp16 = isFp16;
return OH_NN_SUCCESS;
}
unsigned short Compilation::GetCrc16(const unsigned char* buffer, size_t length) const
{
unsigned short crc16 = 0;
for (size_t i = 0; i < length; ++i) {
uint8_t tableIndex = ((crc16 >> OCT_UNIT) ^ *buffer++) & 0x00ff;
crc16 = (crc16 << OCT_UNIT) ^ CRC16_TAB[tableIndex];
}
return crc16;
}
OH_NN_ReturnCode Compilation::GenerateCacheInfo(uint32_t cacheSize, std::unique_ptr<uint64_t[]>& cacheInfo) const
{
std::string cacheInfoPath = m_cachePath + "cache_info.nncache";
std::ofstream cacheInfoStream(cacheInfoPath, std::ios::binary | std::ios::out | std::ios::trunc);
if (cacheInfoStream.fail()) {
LOGE("[Compilation] Model cache info file is invalid.");
return OH_NN_INVALID_FILE;
}
if (!cacheInfoStream.write(reinterpret_cast<const char*>(cacheInfo.get()), cacheSize)) {
LOGE("[Compilation] Fail to write cache info.");
cacheInfoStream.close();
return OH_NN_FAILED;
}
cacheInfoStream.close();
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::GenerateCacheModel(size_t cacheNumber, std::unique_ptr<uint64_t[]>& cacheInfo,
std::vector<ModelBuffer> modelBuffer) const
{
auto cacheInfoPtr = cacheInfo.get();
*cacheInfoPtr++ = static_cast<uint64_t>(cacheNumber);
*cacheInfoPtr++ = static_cast<uint64_t>(m_version);
*cacheInfoPtr++ = static_cast<uint64_t>(m_deviceId);
for (uint32_t i = 0; i < cacheNumber; ++i) {
std::string cacheModelFile = m_cachePath + std::to_string(i) + ".nncache";
std::ofstream cacheModelStream(cacheModelFile, std::ios::binary | std::ios::out | std::ios::trunc);
if (cacheModelStream.fail()) {
LOGE("[Compilation] Model cache file is invalid.");
return OH_NN_INVALID_FILE;
}
uint64_t checkSum = static_cast<uint64_t>(GetCrc16(static_cast<const unsigned char*>(modelBuffer[i].buffer),
modelBuffer[i].length));
*cacheInfoPtr++ = checkSum;
if (!cacheModelStream.write(static_cast<const char*>(modelBuffer[i].buffer), modelBuffer[i].length)) {
LOGE("[Compilation] Fail to write cache model.");
cacheModelStream.close();
return OH_NN_FAILED;
};
cacheModelStream.close();
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::GenerateCacheFiles(const std::vector<ModelBuffer>& modelBuffer) const
{
const size_t cacheNumber = modelBuffer.size();
uint32_t cacheSize = NUMBER_CACHE_INFO_MEMBERS + cacheNumber;
std::unique_ptr<uint64_t[]> cacheInfo = std::make_unique<uint64_t[]>(cacheSize);
if (cacheInfo == nullptr) {
LOGE("Fail to create cacheInfo instance.");
return OH_NN_MEMORY_ERROR;
}
OH_NN_ReturnCode ret = GenerateCacheModel(cacheNumber, cacheInfo, modelBuffer);
if (ret != OH_NN_SUCCESS) {
return ret;
}
uint32_t infoCharNumber = cacheSize * sizeof(uint64_t);
ret = GenerateCacheInfo(infoCharNumber, cacheInfo);
if (ret != OH_NN_SUCCESS) {
return ret;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::GetCacheFileLength(std::ifstream& ifs, int& fsize) const
{
ifs.seekg(0, std::ios::end);
if (!ifs.good()) {
LOGE("[Compilation] Fail to set the position of the next character to be extracted from the input stream.");
return OH_NN_INVALID_FILE;
}
int handleValue = ifs.tellg();
if (handleValue == -1) {
LOGE("[Compilation] Unable to get position of the input stream.");
return OH_NN_INVALID_FILE;
}
if ((handleValue > MAX_MODEL_SIZE) || (handleValue == NULL_PTR_LENGTH)) {
LOGE("[Compilation] Unable to read huge or empty input stream, get cache file size=%d", handleValue);
return OH_NN_INVALID_FILE;
}
fsize = handleValue;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::ReadCacheModelFile(const std::string& file, ModelBuffer& modelBuffer) const
{
std::ifstream ifs(file.c_str(), std::ios::in | std::ios::binary);
if (!ifs) {
LOGE("[Compilation] Fail to open cache file.");
return OH_NN_INVALID_FILE;
}
int fsize {-1};
OH_NN_ReturnCode ret = GetCacheFileLength(ifs, fsize);
if (ret != OH_NN_SUCCESS) {
ifs.close();
return ret;
}
ifs.seekg(0, std::ios::beg);
if (!ifs.good()) {
LOGE("[Compilation] Fail to set the position of the next character to be extracted"
"from the cache model stream.");
ifs.close();
return OH_NN_FAILED;
}
char* ptr = static_cast<char*>(m_device->AllocateBuffer(fsize));
if (ptr == nullptr) {
LOGE("[Compilation] Fail to create file buffer.");
ifs.close();
return OH_NN_NULL_PTR;
}
ifs.read(ptr, fsize);
if (!ifs.good()) {
LOGE("[Compilation] Fail to read the characters from the cache model stream.");
ifs.close();
m_device->ReleaseBuffer(ptr);
ptr = nullptr;
return OH_NN_FAILED;
}
ifs.close();
modelBuffer.buffer = ptr;
modelBuffer.length = fsize;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::CheckCacheInfo(ModelCacheInfo& modelCacheInfo, const std::string& cacheInfoPath) const
{
std::ifstream infoCacheFile(cacheInfoPath.c_str(), std::ios::in | std::ios::binary);
if (!infoCacheFile) {
LOGE("[Compilation] Openning cache info file failed.");
return OH_NN_INVALID_FILE;
}
int charNumber = NUMBER_CACHE_INFO_MEMBERS * sizeof(uint64_t);
if (!infoCacheFile.read((char*)&(modelCacheInfo), charNumber)) {
LOGE("[Compilation] Fail to get the content of info cache file.");
infoCacheFile.close();
return OH_NN_INVALID_FILE;
}
// modelCacheInfo.deviceId type is int64_t,
// it is transformed from size_t value, so the transform here will not truncate value.
size_t deviceId = static_cast<size_t>(modelCacheInfo.deviceId);
if (deviceId != m_deviceId) {
LOGE("[Compilation] The deviceId=%zu in the cache files is different from current deviceId=%zu,"
"please change the cache directory or current deviceId.", deviceId, m_deviceId);
infoCacheFile.close();
return OH_NN_INVALID_PARAMETER;
}
std::vector<uint64_t> modelCheckSum;
modelCheckSum.resize(modelCacheInfo.fileNumber);
modelCacheInfo.modelCheckSum.resize(modelCacheInfo.fileNumber);
if (!infoCacheFile.read((char*)&modelCheckSum[0], modelCacheInfo.fileNumber * sizeof(uint64_t))) {
LOGE("[Compilation] The info cache file has been changed.");
infoCacheFile.close();
return OH_NN_INVALID_FILE;
}
for (uint32_t i = 0; i < modelCacheInfo.fileNumber; ++i) {
modelCacheInfo.modelCheckSum[i] = static_cast<unsigned short>(modelCheckSum[i]);
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::RemoveCacheFiles(uint32_t fileNumber) const
{
std::string cacheInfoPath = m_cachePath + "cache_info.nncache";
if (remove(cacheInfoPath.c_str()) == -1) {
LOGE("[Compilation] Fail to remove the file %s, please delete the file manually.", cacheInfoPath.c_str());
return OH_NN_FAILED;
}
LOGI("[Compilation] Succeed to remove the file cache_info.nncach.");
for (uint32_t i = 0; i < fileNumber; ++i) {
std::string fileName = std::to_string(i) + ".nncache";
std::string cacheModelPath = m_cachePath + fileName;
if (access(cacheModelPath.c_str(), 0) != 0) {
LOGW("[Compilation] The file %s does not exist, no need to delete the file.", cacheModelPath.c_str());
continue;
}
if (remove(cacheModelPath.c_str()) == -1) {
LOGE("[Compilation] Fail to remove the file %s, please delete the file manually.", cacheModelPath.c_str());
return OH_NN_FAILED;
}
LOGI("[Compilation] Succeed to remove the file %s", cacheModelPath.c_str());
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::CheckCacheModel(const ModelCacheInfo& modelCacheInfo,
std::vector<ModelBuffer>& modelBuffers) const
{
for (uint32_t i = 0; i < modelCacheInfo.fileNumber; ++i) {
std::string cacheModelPath = m_cachePath + std::to_string(i) + ".nncache";
if (access(cacheModelPath.c_str(), 0) != 0) {
LOGE("[Compilation] The cache model file %s does not exist.", cacheModelPath.c_str());
return OH_NN_INVALID_FILE;
}
ModelBuffer modelBuffer;
OH_NN_ReturnCode ret = ReadCacheModelFile(cacheModelPath, modelBuffer);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Read cache model file failed.");
return ret;
}
if (GetCrc16(static_cast<const unsigned char*>(modelBuffer.buffer),
modelBuffer.length) != modelCacheInfo.modelCheckSum[i]) {
LOGE("[Compilation] The cache model file %s has been changed.", cacheModelPath.c_str());
return OH_NN_INVALID_FILE;
}
modelBuffers.emplace_back(std::move(modelBuffer));
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::NormalBuild(std::shared_ptr<PreparedModel>& preparedModel)
{
ModelConfig config {m_enableFp16, m_performance, m_priority};
OH_NN_ReturnCode ret = m_device->PrepareModel(m_liteGraph, config, preparedModel);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Preparing model failed when normally building.");
return ret;
}
m_executionPlan = CreateSharedPtr<ExecutionPlan>(preparedModel, m_device);
if (m_executionPlan == nullptr) {
LOGE("Fail to create ExecutionPlan instance.");
return OH_NN_MEMORY_ERROR;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::GenCacheBuild(std::shared_ptr<PreparedModel>& preparedModel)
{
OH_NN_ReturnCode ret = NormalBuild(preparedModel);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Preparing model failed when generating cache.");
return ret;
}
std::vector<ModelBuffer> modelBuffers;
ret = preparedModel->ExportModelCache(modelBuffers);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Export model cache failed.");
return ret;
}
ret = GenerateCacheFiles(modelBuffers);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Generate cache files failed.");
return ret;
}
LOGI("[Compilation] Export model cache successfully.");
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::ReGenCacheBuild(uint32_t fileNumber, std::shared_ptr<PreparedModel>& preparedModel)
{
OH_NN_ReturnCode ret = RemoveCacheFiles(fileNumber);
if (ret != OH_NN_SUCCESS) {
return ret;
}
ret = GenCacheBuild(preparedModel);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Generating cache building failed.");
return ret;
}
LOGI("[Compilation] Update model cache successfully.");
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::LoadCacheBuild(std::shared_ptr<PreparedModel>& preparedModel,
const ModelCacheInfo& cacheInfo)
{
std::vector<ModelBuffer> modelBuffers;
OH_NN_ReturnCode ret = CheckCacheModel(cacheInfo, modelBuffers);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Checking cache model failed.");
for (size_t i = 0; i < modelBuffers.size(); ++i) {
m_device->ReleaseBuffer(modelBuffers[i].buffer);
modelBuffers[i].buffer = nullptr;
modelBuffers[i].length = 0;
}
return ret;
}
ModelConfig config {m_enableFp16, m_performance, m_priority};
ret = m_device->PrepareModelFromModelCache(modelBuffers, config, preparedModel);
if (ret != OH_NN_SUCCESS) {
LOGE("[Compilation] Preparing model from cache failed.");
return ret;
}
LOGI("[Compilation] Load cache successfully.");
m_executionPlan = CreateSharedPtr<ExecutionPlan>(preparedModel, m_device);
if (m_executionPlan == nullptr) {
LOGE("Fail to create ExecutionPlan instance.");
return OH_NN_MEMORY_ERROR;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::InnerBuild()
{
OH_NN_ReturnCode ret;
std::shared_ptr<PreparedModel> preparedModel;
if (m_cachePath.empty()) {
ret = NormalBuild(preparedModel);
if (ret != OH_NN_SUCCESS) {
LOGE("Fail to normally build.");
return ret;
}
m_isBuild = true;
return OH_NN_SUCCESS;
}
std::string cacheInfoPath = m_cachePath + "cache_info.nncache";
if (access(cacheInfoPath.c_str(), 0) != 0) {
ret = GenCacheBuild(preparedModel);
if (ret != OH_NN_SUCCESS) {
LOGE("Fail to build in generating cache mode.");
return ret;
}
m_isBuild = true;
return OH_NN_SUCCESS;
}
ModelCacheInfo cacheInfo;
ret = CheckCacheInfo(cacheInfo, cacheInfoPath);
if (ret != OH_NN_SUCCESS) {
return ret;
}
if (m_version > cacheInfo.version) {
ret = ReGenCacheBuild(cacheInfo.fileNumber, preparedModel);
if (ret != OH_NN_SUCCESS) {
return ret;
}
m_isBuild = true;
return OH_NN_SUCCESS;
}
if (m_version < cacheInfo.version) {
LOGE("[Compilation] The current version is lower than the cache files, please set a higher version.");
return OH_NN_OPERATION_FORBIDDEN;
}
ret = LoadCacheBuild(preparedModel, cacheInfo);
if (ret != OH_NN_SUCCESS) {
return ret;
}
m_isBuild = true;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Compilation::Build()
{
NNRT_TRACE_NAME("Compilation");
if (m_isBuild) {
LOGE("[Compilation] Cannot enable float16 after compilation finish.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (m_device == nullptr) {
LOGE("The parameter of m_device is nullptr, please call SetDevice function before build model.");
return OH_NN_OPERATION_FORBIDDEN;
}
OH_NN_ReturnCode ret = InnerBuild();
if (ret != OH_NN_SUCCESS) {
return ret;
}
return OH_NN_SUCCESS;
}
std::shared_ptr<ExecutionPlan> Compilation::GetExecutionPlan() const
{
return m_executionPlan;
}
std::vector<std::shared_ptr<NNTensor>> Compilation::GetInputTensors() const
{
return m_inputTensors;
}
std::vector<std::shared_ptr<NNTensor>> Compilation::GetOutputTensors() const
{
return m_outputTensors;
}
bool Compilation::IsBuild() const
{
return m_isBuild;
}
bool Compilation::IsDynamicShape() const
{
for (size_t i = 0; i < m_inputTensors.size(); ++i) {
if (m_inputTensors[i]->IsDynamicShape()) {
return true;
}
}
return false;
}
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+86
View File
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_COMPILATION_H
#define NEURAL_NETWORK_RUNTIME_COMPILATION_H
#include "inner_model.h"
#include "execution_plan.h"
#include "interfaces/oem/cpp_api/device.h"
#include "interfaces/oem/cpp_api/cpp_type.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
struct ModelCacheInfo {
uint64_t fileNumber = 0;
uint64_t version = 0;
uint64_t deviceId = 0;
std::vector<unsigned short> modelCheckSum;
};
class Compilation {
public:
explicit Compilation(const InnerModel* innerModel);
OH_NN_ReturnCode SetDevice(size_t deviceId);
OH_NN_ReturnCode SetCacheDir(const std::string& cacheModelPath, uint32_t version);
OH_NN_ReturnCode SetPerformance(OH_NN_PerformanceMode performance);
OH_NN_ReturnCode SetPriority(OH_NN_Priority priority);
OH_NN_ReturnCode SetEnableFp16(bool isFp16);
OH_NN_ReturnCode Build();
bool IsBuild() const;
bool IsDynamicShape() const;
std::vector<std::shared_ptr<NNTensor>>GetInputTensors() const;
std::vector<std::shared_ptr<NNTensor>>GetOutputTensors() const;
std::shared_ptr<ExecutionPlan> GetExecutionPlan() const;
private:
std::shared_ptr<mindspore::lite::LiteGraph> m_liteGraph {nullptr};
OH_NN_Priority m_priority {OH_NN_PRIORITY_NONE};
OH_NN_PerformanceMode m_performance {OH_NN_PERFORMANCE_NONE};
bool m_enableFp16 {false};
std::shared_ptr<Device> m_device {nullptr};
std::string m_cachePath;
uint32_t m_version {0};
size_t m_deviceId {0};
bool m_isBuild {false};
std::shared_ptr<ExecutionPlan> m_executionPlan {nullptr};
std::vector<std::shared_ptr<NNTensor>> m_inputTensors;
std::vector<std::shared_ptr<NNTensor>> m_outputTensors;
private:
OH_NN_ReturnCode GenerateCacheFiles(const std::vector<ModelBuffer>& modelBuffer) const;
OH_NN_ReturnCode GenerateCacheModel(size_t cacheNumber, std::unique_ptr<uint64_t[]>& cacheInfo,
std::vector<ModelBuffer> modelBuffer) const;
OH_NN_ReturnCode GenerateCacheInfo(uint32_t cacheSize, std::unique_ptr<uint64_t[]>& cacheInfo) const;
OH_NN_ReturnCode CheckCacheInfo(ModelCacheInfo& modelCacheInfo, const std::string& cacheInfoPath) const;
OH_NN_ReturnCode ReadCacheModelFile(const std::string& file, ModelBuffer& modelBuffer) const;
OH_NN_ReturnCode RemoveCacheFiles(uint32_t fileNumber) const;
unsigned short GetCrc16(const unsigned char* buffer, size_t length) const;
OH_NN_ReturnCode CheckCacheModel(const ModelCacheInfo& modelCacheInfo,
std::vector<ModelBuffer>& modelBuffers) const;
OH_NN_ReturnCode NormalBuild(std::shared_ptr<PreparedModel>& preparedModel);
OH_NN_ReturnCode GenCacheBuild(std::shared_ptr<PreparedModel>& preparedModel);
OH_NN_ReturnCode ReGenCacheBuild(uint32_t fileNumber, std::shared_ptr<PreparedModel>& preparedModel);
OH_NN_ReturnCode LoadCacheBuild(std::shared_ptr<PreparedModel>& preparedModel, const ModelCacheInfo& cacheInfo);
OH_NN_ReturnCode InnerBuild();
OH_NN_ReturnCode GetCacheFileLength(std::ifstream& ifs, int& fsize) const;
};
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_COMPILATION_H
+171
View File
@@ -0,0 +1,171 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "device_manager.h"
#include "hdi_interfaces.h"
#include "hdi_device.h"
#include "common/log.h"
#include "common/utils.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
const std::vector<size_t>& DeviceManager::GetAllDeviceId()
{
m_tmpDeviceIds.clear();
std::shared_ptr<Device> device {nullptr};
for (auto iter = m_devices.begin(); iter != m_devices.end(); ++iter) {
device = iter->second;
if (!IsValidDevice(device)) {
continue;
}
m_tmpDeviceIds.emplace_back(iter->first);
}
return m_tmpDeviceIds;
}
std::shared_ptr<Device> DeviceManager::GetDevice(size_t deviceId) const
{
auto iter = m_devices.find(deviceId);
if (iter == m_devices.end()) {
LOGE("DeviceId is not found, deviceId=%zu", deviceId);
return nullptr;
}
return iter->second;
}
const std::string& DeviceManager::GetDeviceName(size_t deviceId)
{
m_tmpDeviceName.clear();
auto iter = m_devices.find(deviceId);
if (iter == m_devices.end()) {
LOGE("DeviceId is not found, deviceId=%zu", deviceId);
return m_tmpDeviceName;
}
std::string deviceName;
auto ret = iter->second->GetDeviceName(deviceName);
if (ret != OH_NN_SUCCESS) {
LOGE("Get device name failed.");
return m_tmpDeviceName;
}
std::string vendorName;
ret = iter->second->GetVendorName(vendorName);
if (ret != OH_NN_SUCCESS) {
LOGE("Get vendor name failed.");
return m_tmpDeviceName;
}
m_tmpDeviceName = GenUniqueName(deviceName, vendorName);
return m_tmpDeviceName;
}
std::string DeviceManager::GenUniqueName(const std::string& deviceName, const std::string& vendorName) const
{
return deviceName + "_" + vendorName;
}
OH_NN_ReturnCode DeviceManager::RegisterDevice(std::function<std::shared_ptr<Device>()> creator)
{
auto regDevice = creator();
if (regDevice == nullptr) {
LOGE("Cannot create device, register device failed.");
return OH_NN_INVALID_PARAMETER;
}
if (!IsValidDevice(regDevice)) {
LOGE("Device is not avaliable.");
return OH_NN_UNAVALIDABLE_DEVICE;
}
std::string deviceName;
auto ret = regDevice->GetDeviceName(deviceName);
if (ret != OH_NN_SUCCESS) {
LOGE("Get device name failed.");
return ret;
}
std::string vendorName;
ret = regDevice->GetVendorName(vendorName);
if (ret != OH_NN_SUCCESS) {
LOGE("Get vendor name failed.");
return ret;
}
const std::lock_guard<std::mutex> lock(m_mtx);
std::string uniqueName = GenUniqueName(deviceName, vendorName);
auto setResult = m_uniqueName.emplace(uniqueName);
if (!setResult.second) {
LOGE("Device already exists, cannot register again. deviceName=%s, vendorName=%s",
deviceName.c_str(), vendorName.c_str());
return OH_NN_FAILED;
}
m_devices.emplace(std::hash<std::string>{}(uniqueName), regDevice);
return OH_NN_SUCCESS;
}
void DeviceManager::DiscoverHDIDevices()
{
// only one device from HDI now.
OHOS::sptr<V1_0::INnrtDevice> iDevice = V1_0::INnrtDevice::Get();
if (iDevice == nullptr) {
LOGW("Get HDI device failed.");
return;
}
std::string deviceName;
std::string vendorName;
auto hdiRet = iDevice->GetDeviceName(deviceName);
if (hdiRet != HDF_SUCCESS) {
LOGW("Get device name failed. ErrorCode=%d", hdiRet);
return;
}
hdiRet = iDevice->GetVendorName(vendorName);
if (hdiRet != HDF_SUCCESS) {
LOGW("Get vendor name failed. ErrorCode=%d", hdiRet);
return;
}
std::string uniqueName = GenUniqueName(deviceName, vendorName);
const std::lock_guard<std::mutex> lock(m_mtx);
auto setResult = m_uniqueName.emplace(uniqueName);
if (!setResult.second) {
LOGW("Device already exists, cannot register again. deviceName=%s, vendorName=%s",
deviceName.c_str(), vendorName.c_str());
return;
}
std::shared_ptr<Device> device = CreateSharedPtr<HDIDevice>(iDevice);
if (device == nullptr) {
LOGW("Failed to register device, because fail to create device instance.");
return;
}
m_devices.emplace(std::hash<std::string>{}(uniqueName), device);
}
bool DeviceManager::IsValidDevice(std::shared_ptr<Device> device) const
{
DeviceStatus status {DeviceStatus::UNKNOWN};
auto ret = device->GetDeviceStatus(status);
if (ret != OH_NN_SUCCESS || status == DeviceStatus::UNKNOWN || status == DeviceStatus::OFFLINE) {
return false;
}
return true;
}
} // NeuralNetworkRuntime
} // OHOS
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_DEVICE_MANAGER_H
#define NEURAL_NETWORK_RUNTIME_DEVICE_MANAGER_H
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <memory>
#include <functional>
#include <mutex>
#include "interfaces/oem/cpp_api/device.h"
#include "interfaces/kits/c/neural_network_runtime_type.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
class DeviceManager {
public:
const std::vector<size_t>& GetAllDeviceId();
std::shared_ptr<Device> GetDevice(size_t deviceId) const;
const std::string& GetDeviceName(size_t deviceId);
// register device from C++ API
OH_NN_ReturnCode RegisterDevice(std::function<std::shared_ptr<Device>()> creator);
static DeviceManager& GetInstance()
{
static DeviceManager instance;
instance.DiscoverHDIDevices();
return instance;
}
private:
DeviceManager() = default;
DeviceManager(const DeviceManager&) = delete;
DeviceManager& operator=(const DeviceManager&) = delete;
void DiscoverHDIDevices();
std::string GenUniqueName(const std::string& deviceName, const std::string& vendorName) const;
bool IsValidDevice(std::shared_ptr<Device> device) const;
private:
std::unordered_set<std::string> m_uniqueName;
// key is device id, it is the unique number.
std::unordered_map<size_t, std::shared_ptr<Device>> m_devices;
std::mutex m_mtx;
std::string m_tmpDeviceName;
std::vector<size_t> m_tmpDeviceIds;
};
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_DEVICE_MANAGER_H
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "interfaces/oem/cpp_api/device_registrar.h"
#include "device_manager.h"
#include "common/log.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
DeviceRegistrar::DeviceRegistrar(const CreateDevice creator)
{
auto& deviceManager = DeviceManager::GetInstance();
auto ret = deviceManager.RegisterDevice(creator);
if (ret != OH_NN_SUCCESS) {
LOGW("Register device failed. ErrorCode=%d", ret);
}
}
} // NeuralNetworkRuntime
} // OHOS
+91
View File
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "execution_plan.h"
#include <vector>
#include "common/log.h"
#include "interfaces/oem/cpp_api/cpp_type.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
OH_NN_ReturnCode ExecutionPlan::Run(const std::vector<std::shared_ptr<NNTensor>>& inputTensors,
std::vector<std::shared_ptr<NNTensor>>& outputTensors)
{
OH_NN_ReturnCode ret {OH_NN_FAILED};
IOTensor tensor;
std::vector<IOTensor> inputIOTensors;
size_t inputSize = inputTensors.size();
size_t outputSize = outputTensors.size();
for (size_t i = 0; i < inputSize; ++i) {
inputTensors[i]->ConvertToIOTensor(tensor);
inputIOTensors.emplace_back(std::move(tensor));
}
std::vector<IOTensor> outputIOTensors;
for (size_t i = 0; i < outputSize; ++i) {
outputTensors[i]->ConvertToIOTensor(tensor);
outputIOTensors.emplace_back(std::move(tensor));
}
std::vector<std::vector<int32_t>> outputsDims;
std::vector<bool> isSufficientDataBuffer;
ret = m_preparedModel->Run(inputIOTensors, outputIOTensors, outputsDims, isSufficientDataBuffer);
if (ret != OH_NN_SUCCESS) {
LOGE("PrepardModel Run() failed.");
return ret;
}
// Check if the output buffer is sufficient
bool bufferFailed {false};
for (size_t i = 0; i < outputSize; ++i) {
if (!isSufficientDataBuffer[i]) {
// Print all output indices with insufficient buffer, don't return until traversing all outputs.
LOGE("Run failed, Output %zu does not have enough buffer to store the data.", i);
bufferFailed = true;
}
}
if (bufferFailed) {
return OH_NN_FAILED;
}
// Set the output NNTensor's dimensions from output IOTensor if it is dynamic.
// NNTensor::SetDimensions will check if the tensor buffer is enough for the new dimensions.
for (size_t i = 0; i < outputSize; ++i) {
ret = outputTensors[i]->SetDimensions(outputsDims[i]);
if (ret != OH_NN_SUCCESS) {
LOGE("Run failed, error happened when setting output tensor's dimensions, output id: %zu.", i);
return ret;
}
}
return OH_NN_SUCCESS;
}
std::shared_ptr<Device> ExecutionPlan::GetInputDevice() const
{
return m_device;
}
std::shared_ptr<Device> ExecutionPlan::GetOutputDevice() const
{
return m_device;
}
} // NeuralNetworkRuntime
} // OHOS
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_EXECUTION_PLAN_H
#define NEURAL_NETWORK_RUNTIME_EXECUTION_PLAN_H
#include "frameworks/native/nn_tensor.h"
#include "interfaces/kits/c/neural_network_runtime_type.h"
#include "interfaces/oem/cpp_api/prepared_model.h"
#include "interfaces/oem/cpp_api/device.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
class ExecutionPlan {
public:
ExecutionPlan(std::shared_ptr<PreparedModel> preparedModel, std::shared_ptr<Device> device)
: m_preparedModel(preparedModel),
m_device(device) {};
OH_NN_ReturnCode Run(const std::vector<std::shared_ptr<NNTensor>>& inputTensors,
std::vector<std::shared_ptr<NNTensor>>& outputTensors);
std::shared_ptr<Device> GetInputDevice() const;
std::shared_ptr<Device> GetOutputDevice() const;
private:
std::shared_ptr<PreparedModel> m_preparedModel {nullptr};
std::shared_ptr<Device> m_device {nullptr};
};
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif
+555
View File
@@ -0,0 +1,555 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "executor.h"
#include "securec.h"
#include "common/utils.h"
#include "common/scoped_trace.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
Executor::Executor(const Compilation* compilation)
: m_modelInputs(compilation->GetInputTensors()),
m_modelOutputs(compilation->GetOutputTensors()),
m_executionPlan(compilation->GetExecutionPlan()) {}
OH_NN_ReturnCode Executor::BuildInputTensor(uint32_t index, const OH_NN_Tensor& nnTensor,
std::shared_ptr<NNTensor> inputTensor) const
{
// Note: inputs have only shapes info.
if (index >= m_modelInputs.size()) {
LOGE("BuildInputTensor failed, input index is out of range.");
return OH_NN_INVALID_PARAMETER;
}
// Build a tensor from nnTensor.
auto ret = inputTensor->BuildFromOHNNTensor(nnTensor);
if (ret != OH_NN_SUCCESS) {
LOGE("BuildInputTensor failed, please check input nnTensor.");
return ret;
}
if (inputTensor->IsDynamicShape()) {
LOGE("BuildInputTensor failed, input nnTensor should has certain dimensions which cannot contain -1.");
return OH_NN_INVALID_PARAMETER;
}
if (!m_modelInputs[index]->CompareAttribute(*inputTensor)) {
LOGE("BuildInputTensor failed, input has different attributes from the one in the constructed model.");
return OH_NN_INVALID_PARAMETER;
}
inputTensor->SetName(m_modelInputs[index]->GetName());
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::SetInputTensorWithCurrentBuffer(uint32_t index,
std::shared_ptr<NNTensor> inputTensor,
const void* buffer,
size_t dataLength,
size_t curBufferLength)
{
void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
errno_t status = memcpy_s(curBuffer, dataLength, buffer, dataLength);
// Current buffer inside m_inputTensors is managed by executor, no need to release if memcpy failed.
if (status != EOK) {
LOGE("SetInputTensorWithCurrentBuffe failed, copy data from user buffer to device buffer failed. "
"Error code: %d.", status);
return OH_NN_MEMORY_ERROR;
}
// Set the new tensor with the buffer of current tensor
inputTensor->SetBuffer(curBuffer, curBufferLength);
// The memory is reused here. Thus, current tensor's buffer must set to nullptr, in case the memory is released
// twice.
m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
// Set to the new tensor, and release current one.
m_inputTensors[index].tensor = inputTensor;
return OH_NN_SUCCESS;
}
void Executor::SetInputTensorWithNewBuffer(uint32_t index,
std::shared_ptr<NNTensor> inputTensor,
const void* inputBuffer,
size_t length,
bool isInnerMem)
{
// Release the memory inside the tensor first, if it is allocated by Executor during SetInput().
if (m_inputTensors.find(index) != m_inputTensors.end()) {
if (m_inputTensors[index].isInnerMem) {
void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
inputDevice->ReleaseBuffer(curBuffer);
}
// Set current tensor's buffer to nullptr in case the NNTensor release the driver memory in destruction.
m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
}
// Set new input tensor data buffer
inputTensor->SetBuffer(inputBuffer, length);
// Create or update the input tensor
ExeTensor exeTensor{inputTensor, nullptr, 0, isInnerMem};
m_inputTensors[index] = exeTensor;
}
OH_NN_ReturnCode Executor::SetInput(uint32_t index, const OH_NN_Tensor& nnTensor, const void* buffer, size_t length)
{
std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
if (inputTensor == nullptr) {
LOGE("SetInput failed, error happened when creating NNTensor.");
return OH_NN_MEMORY_ERROR;
}
auto ret = BuildInputTensor(index, nnTensor, inputTensor);
if (ret != OH_NN_SUCCESS) {
LOGE("SetInput failed, please check input index or nnTensor.");
return ret;
}
// dataLength will be larger than 0 after BuildInputTensor()
size_t dataLength = inputTensor->GetDataLength();
if (length == 0 || length < dataLength) {
LOGE("SetInput failed, the given buffer length is too small to store the input nnTensor data.");
return OH_NN_INVALID_PARAMETER;
}
// Get length of current buffer if it is allocate by SetInput() before.
size_t curBufferLength = 0;
if ((m_inputTensors.find(index) != m_inputTensors.end()) && (m_inputTensors[index].isInnerMem)) {
curBufferLength = m_inputTensors[index].tensor->GetBufferLength();
}
// (dataLength <= curBufferLength) returns true if and only if current buffer is allocated by SetInput() before
// and is larger than user buffer.
if (dataLength <= curBufferLength) {
ret = SetInputTensorWithCurrentBuffer(index, inputTensor, buffer, dataLength, curBufferLength);
if (ret != OH_NN_SUCCESS) {
LOGE("SetInput failed, error happened when setting input with current buffer.");
return ret;
}
m_isRun = false;
return OH_NN_SUCCESS;
}
/**
* Buffer needs to allocated or reallocated if:
*
* - Current buffer is not enough.
* - SetInput() has not been called for the input before.
* - The buffer held in m_inputTensors is allocated and set by CreateInputMemory() and SetInputFromMemory().
*/
std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
void* inputBuffer = inputDevice->AllocateBuffer(length);
if (inputBuffer == nullptr) {
LOGE("SetInput failed, error happened when allocating input device buffer.");
return OH_NN_MEMORY_ERROR;
}
errno_t status = memcpy_s(inputBuffer, dataLength, buffer, dataLength);
if (status != EOK) {
LOGE("SetInput failed, copy data from user buffer failed. Error code: %d.", status);
inputDevice->ReleaseBuffer(inputBuffer);
return OH_NN_MEMORY_ERROR;
}
SetInputTensorWithNewBuffer(index, inputTensor, inputBuffer, length, true);
m_isRun = false;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::SetInputFromMemory(uint32_t index, const OH_NN_Tensor& nnTensor, const OH_NN_Memory& memory)
{
// Build a input tensor
std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
if (inputTensor == nullptr) {
LOGE("SetInputFromMemory failed, error happened when creating NNTensor.");
return OH_NN_MEMORY_ERROR;
}
auto ret = BuildInputTensor(index, nnTensor, inputTensor);
if (ret != OH_NN_SUCCESS) {
LOGE("SetInputFromMemory failed, please check input index or nnTensor");
return ret;
}
// check data length
size_t dataLength = inputTensor->GetDataLength();
if (memory.length == 0 || memory.length < dataLength) {
LOGE("SetInputFromMemory failed,"
" the length in the given memory is too small to store the input nnTensor data.");
return OH_NN_INVALID_PARAMETER;
}
SetInputTensorWithNewBuffer(index, inputTensor, const_cast<const void*>(memory.data), memory.length, false);
m_isRun = false;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::SetOutput(uint32_t index, void* buffer, size_t length)
{
if (index >= m_modelOutputs.size()) {
LOGE("SetOutput failed, output index is out of range.");
return OH_NN_INVALID_PARAMETER;
}
size_t dataLength = m_modelOutputs[index]->GetDataLength();
if (length == 0 || length < dataLength) {
LOGE("SetOutput failed, the given buffer length is too small to store the output tensor data.");
return OH_NN_INVALID_PARAMETER;
}
// If output tensor does not exist, or inner device buffer size is not enough,
// or device buffer is set by SetOutputFromMemory() before,
// allocate a new device buffer and set it to output tensor, and update the user buffer.
std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
if (m_outputTensors.find(index) != m_outputTensors.end()) {
if (m_outputTensors[index].isInnerMem) {
size_t curBufferLength = m_outputTensors[index].tensor->GetBufferLength();
if (length <= curBufferLength) {
// If current device buffer size is enough, only update the user buffer.
m_outputTensors[index].userBuffer = buffer;
m_outputTensors[index].userBufferLength = length;
m_isRun = false;
return OH_NN_SUCCESS;
} else {
// If current device buffer size is not enough,
// release current device buffer and then allocate a new one below.
void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
outputDevice->ReleaseBuffer(curBuffer);
}
}
} else {
// If output tensor does not exist, create a new null output tensor.
ExeTensor exeTensor;
m_outputTensors[index] = exeTensor;
m_outputTensors[index].tensor = m_modelOutputs[index];
}
void* deviceOutputBuffer = outputDevice->AllocateBuffer(length);
if (deviceOutputBuffer == nullptr) {
LOGE("SetOutput failed, allocating output device buffer failed.");
return OH_NN_MEMORY_ERROR;
}
m_outputTensors[index].tensor->SetBuffer(deviceOutputBuffer, length);
m_outputTensors[index].userBuffer = buffer;
m_outputTensors[index].userBufferLength = length;
m_outputTensors[index].isInnerMem = true;
m_isRun = false;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::SetOutputFromMemory(uint32_t index, const OH_NN_Memory& memory)
{
if (index >= m_modelOutputs.size()) {
LOGE("SetOutputFromMemory failed, output index is out of range.");
return OH_NN_INVALID_PARAMETER;
}
size_t dataLength = m_modelOutputs[index]->GetDataLength();
if (memory.length == 0 || memory.length < dataLength) {
LOGE("SetOutputFromMemory failed, the memory is too small to store the output tensor data.");
return OH_NN_INVALID_PARAMETER;
}
if (m_outputTensors.find(index) != m_outputTensors.end()) {
if (m_outputTensors[index].isInnerMem) {
// If it is inner buffer, releate it
void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
outputDevice->ReleaseBuffer(curBuffer);
}
} else {
// If output tensor does not exist, create a new null output tensor.
ExeTensor exeTensor;
m_outputTensors[index] = exeTensor;
m_outputTensors[index].tensor = m_modelOutputs[index];
}
// Set the output tensor with memory
m_outputTensors[index].tensor->SetBuffer(const_cast<const void*>(memory.data), memory.length);
m_outputTensors[index].userBuffer = nullptr;
m_outputTensors[index].userBufferLength = 0;
m_outputTensors[index].isInnerMem = false;
m_isRun = false;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::GetOutputShape(uint32_t index, int32_t** dimensions, uint32_t& dimensionCount)
{
if (!m_isRun) {
LOGE("GetOutputShape failed, cannot get output dimensions before Run.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (index >= m_modelOutputs.size()) {
LOGE("GetOutputShape failed, output index is out of range.");
return OH_NN_INVALID_PARAMETER;
}
if (m_outputTensors.find(index) == m_outputTensors.end()) {
LOGE("GetOutputShape failed, output has not been set. Output index: %u.", index);
return OH_NN_INVALID_PARAMETER;
}
m_outputDimensions[index] = m_outputTensors[index].tensor->GetDimensions();
*dimensions = m_outputDimensions[index].data();
dimensionCount = m_outputDimensions[index].size();
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::CreateInputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
{
if (index >= m_modelInputs.size()) {
LOGE("CreateInputMemory failed, input index is out of range.");
return OH_NN_INVALID_PARAMETER;
}
// Allocate device buffer
std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
void* deviceInputBuffer = inputDevice->AllocateBuffer(length);
if (deviceInputBuffer == nullptr) {
LOGE("CreateInputMemory failed, allocating intput device buffer failed.");
return OH_NN_MEMORY_ERROR;
}
*memory = new(std::nothrow) OH_NN_Memory{deviceInputBuffer, length};
if (*memory == nullptr) {
LOGE("CreateInputMemory failed, constructing OH_NN_Memory failed.");
inputDevice->ReleaseBuffer(deviceInputBuffer);
return OH_NN_MEMORY_ERROR;
}
// Save the buffer address for check when destroying it.
m_inputCreatedMem[index].emplace_back(deviceInputBuffer);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::DestroyInputMemory(uint32_t index, OH_NN_Memory** memory)
{
if (index >= m_modelInputs.size()) {
LOGE("DestroyInputMemory failed, input index is out of range.");
return OH_NN_INVALID_PARAMETER;
}
if (m_inputCreatedMem.find(index) == m_inputCreatedMem.end()) {
LOGE("DestroyInputMemory failed, the memory has not been created with the index.");
return OH_NN_INVALID_PARAMETER;
}
std::vector<void*>& inputCreatedMem = m_inputCreatedMem[index];
auto pos = std::find(inputCreatedMem.begin(), inputCreatedMem.end(), (*memory)->data);
if (pos == inputCreatedMem.end()) {
LOGE("DestroyInputMemory failed, the index does not match the memory.");
return OH_NN_INVALID_PARAMETER;
}
std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
auto ret = inputDevice->ReleaseBuffer((*memory)->data);
if (ret != OH_NN_SUCCESS) {
LOGE("Release input buffer failed.");
return ret;
}
inputCreatedMem.erase(pos);
delete *memory;
*memory = nullptr;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::CreateOutputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
{
if (index >= m_modelOutputs.size()) {
LOGE("CreateOutputMemory failed, output index is out of range.");
return OH_NN_INVALID_PARAMETER;
}
// Allocate device buffer
std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
void* deviceOutputBuffer = outputDevice->AllocateBuffer(length);
if (deviceOutputBuffer == nullptr) {
LOGE("CreateOutputMemory failed, allocating output device buffer failed.");
return OH_NN_MEMORY_ERROR;
}
*memory = new(std::nothrow) OH_NN_Memory{deviceOutputBuffer, length};
if (*memory == nullptr) {
LOGE("CreateOutputMemory failed, constructing OH_NN_Memory failed.");
outputDevice->ReleaseBuffer(deviceOutputBuffer);
return OH_NN_MEMORY_ERROR;
}
// Save the buffer address for check when destroying it.
m_outputCreatedMem[index].emplace_back(deviceOutputBuffer);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::DestroyOutputMemory(uint32_t index, OH_NN_Memory** memory)
{
if (index >= m_modelOutputs.size()) {
LOGE("DestroyOutputMemory failed, output index is out of range.");
return OH_NN_INVALID_PARAMETER;
}
if (m_outputCreatedMem.find(index) == m_outputCreatedMem.end()) {
LOGE("DestroyOutputMemory failed, the memory has not been created with the index.");
return OH_NN_INVALID_PARAMETER;
}
std::vector<void*>& outputCreatedMem = m_outputCreatedMem[index];
auto pos = std::find(outputCreatedMem.begin(), outputCreatedMem.end(), (*memory)->data);
if (pos == outputCreatedMem.end()) {
LOGE("DestroyOutputMemory failed, the index does not match the memory.");
return OH_NN_INVALID_PARAMETER;
}
std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
auto ret = outputDevice->ReleaseBuffer((*memory)->data);
if (ret != OH_NN_SUCCESS) {
LOGE("Release output buffer failed.");
return ret;
}
outputCreatedMem.erase(pos);
delete *memory;
*memory = nullptr;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Executor::Run()
{
NNRT_TRACE_NAME("Execution");
if (m_modelInputs.size() != m_inputTensors.size()) {
LOGE("Run failed, some input tensors have not been set.");
return OH_NN_INVALID_PARAMETER;
}
if (m_modelOutputs.size() != m_outputTensors.size()) {
LOGE("Run failed, some output tensors have not been set.");
return OH_NN_INVALID_PARAMETER;
}
// Build the NNTensor pointer vector: inputTensors and outputTensors
std::vector<std::shared_ptr<NNTensor>> inputTensors;
std::vector<std::shared_ptr<NNTensor>> outputTensors;
size_t inputSize = m_inputTensors.size();
size_t outputSize = m_outputTensors.size();
for (size_t i = 0; i < inputSize; ++i) {
inputTensors.emplace_back(m_inputTensors[i].tensor);
}
for (size_t i = 0; i < outputSize; ++i) {
outputTensors.emplace_back(m_outputTensors[i].tensor);
}
// Predict
auto ret = m_executionPlan->Run(inputTensors, outputTensors);
if (ret != OH_NN_SUCCESS) {
LOGE("Run failed, error happened when executing the inference.");
return ret;
}
errno_t status{EOK};
// Copy inner device buffer to user buffer if using SetOutput()
for (size_t i = 0; i < outputSize; ++i) {
if (m_outputTensors[i].isInnerMem) {
auto size = outputTensors[i]->GetDataLength();
if (size > m_outputTensors[i].userBufferLength) {
LOGE("Output buffer size is not enough. Your size=%zu, but actual output size=%zu.",
m_outputTensors[i].userBufferLength, size);
return OH_NN_INVALID_PARAMETER;
}
void* deviceBuffer = outputTensors[i]->GetBuffer();
if (deviceBuffer == nullptr) {
LOGE("Output buffer is nullptr.");
return OH_NN_FAILED;
}
status = memcpy_s(m_outputTensors[i].userBuffer, m_outputTensors[i].userBufferLength, deviceBuffer, size);
if (status != EOK) {
LOGE("Run failed, memory copy from device buffer to user buffer failed. Error code: %d.", status);
return OH_NN_MEMORY_ERROR;
}
}
}
m_isRun = true;
return OH_NN_SUCCESS;
}
Executor::~Executor()
{
std::shared_ptr<Device> inputDevice;
for (auto& it : m_inputTensors) {
inputDevice = m_executionPlan->GetInputDevice();
if ((it.second).isInnerMem) {
inputDevice->ReleaseBuffer((it.second).tensor->GetBuffer());
}
(it.second).tensor->SetBuffer(nullptr, 0);
(it.second).tensor.reset();
(it.second).userBuffer = nullptr;
}
m_inputTensors.clear();
std::shared_ptr<Device> outputDevice;
for (auto& it : m_outputTensors) {
outputDevice = m_executionPlan->GetOutputDevice();
if ((it.second).isInnerMem) {
outputDevice->ReleaseBuffer((it.second).tensor->GetBuffer());
}
(it.second).tensor->SetBuffer(nullptr, 0);
(it.second).tensor.reset();
(it.second).userBuffer = nullptr;
}
m_outputTensors.clear();
for (auto& it : m_inputCreatedMem) {
it.second.clear();
}
m_inputCreatedMem.clear();
for (auto& it : m_outputCreatedMem) {
it.second.clear();
}
m_outputCreatedMem.clear();
m_outputDimensions.clear();
m_modelInputs.clear();
m_modelOutputs.clear();
}
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_EXECUTOR_H
#define NEURAL_NETWORK_RUNTIME_EXECUTOR_H
#include "compilation.h"
#include "execution_plan.h"
#include "nn_tensor.h"
#include "interfaces/kits/c/neural_network_runtime.h"
#include "interfaces/oem/cpp_api/device.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
class Executor {
public:
explicit Executor(const Compilation* compilation);
~Executor();
OH_NN_ReturnCode SetInput(uint32_t index, const OH_NN_Tensor& nnTensor, const void* buffer, size_t length);
OH_NN_ReturnCode SetInputFromMemory(uint32_t index, const OH_NN_Tensor& nnTensor, const OH_NN_Memory& memory);
OH_NN_ReturnCode SetOutput(uint32_t index, void* buffer, size_t length);
OH_NN_ReturnCode SetOutputFromMemory(uint32_t index, const OH_NN_Memory& memory);
OH_NN_ReturnCode GetOutputShape(uint32_t index, int32_t** dimensions, uint32_t& dimensionCount);
OH_NN_ReturnCode CreateInputMemory(uint32_t index, size_t length, OH_NN_Memory** memory);
OH_NN_ReturnCode CreateOutputMemory(uint32_t index, size_t length, OH_NN_Memory** memory);
OH_NN_ReturnCode DestroyInputMemory(uint32_t index, OH_NN_Memory** memory);
OH_NN_ReturnCode DestroyOutputMemory(uint32_t index, OH_NN_Memory** memory);
OH_NN_ReturnCode Run();
private:
OH_NN_ReturnCode BuildInputTensor(uint32_t index, const OH_NN_Tensor& nnTensor,
std::shared_ptr<NNTensor> inputTensor) const;
OH_NN_ReturnCode SetInputTensorWithCurrentBuffer(uint32_t index, std::shared_ptr<NNTensor> inputTensor,
const void* buffer, size_t dataLength, size_t curBufferLength);
void SetInputTensorWithNewBuffer(uint32_t index, std::shared_ptr<NNTensor> inputTensor,
const void* inputBuffer, size_t length, bool isInnerMem);
private:
struct ExeTensor {
std::shared_ptr<NNTensor> tensor;
void* userBuffer;
size_t userBufferLength;
bool isInnerMem;
};
bool m_isRun {false};
std::vector<std::shared_ptr<NNTensor>> m_modelInputs;
std::vector<std::shared_ptr<NNTensor>> m_modelOutputs;
std::shared_ptr<ExecutionPlan> m_executionPlan {nullptr};
std::unordered_map<int, std::vector<int32_t>> m_outputDimensions;
std::unordered_map<int, ExeTensor> m_inputTensors;
std::unordered_map<int, ExeTensor> m_outputTensors;
std::unordered_map<int, std::vector<void*>> m_inputCreatedMem;
std::unordered_map<int, std::vector<void*>> m_outputCreatedMem;
};
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif
+331
View File
@@ -0,0 +1,331 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hdi_device.h"
#include "hdf_base.h"
#include "mindir.h"
#include "hdi_prepared_model.h"
#include "memory_manager.h"
#include "transform.h"
#include "common/log.h"
#include "common/utils.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
HDIDevice::HDIDevice(OHOS::sptr<V1_0::INnrtDevice> device) : m_iDevice(device)
{
device->GetVersion(m_hdiVersion.first, m_hdiVersion.second);
}
OH_NN_ReturnCode HDIDevice::GetDeviceName(std::string& name)
{
auto ret = m_iDevice->GetDeviceName(name);
if (ret != HDF_SUCCESS) {
LOGE("Get HDI device name failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::GetVendorName(std::string& name)
{
auto ret = m_iDevice->GetVendorName(name);
if (ret != HDF_SUCCESS) {
LOGE("Get HDI device vendor name failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::GetDeviceType(OH_NN_DeviceType& deviceType)
{
V1_0::DeviceType iDeviceType;
auto ret = m_iDevice->GetDeviceType(iDeviceType);
if (ret != HDF_SUCCESS) {
LOGE("Get HDI device type failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
deviceType = HDIToNN::TransHDIDeviceType(iDeviceType);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::GetDeviceStatus(DeviceStatus& status)
{
V1_0::DeviceStatus iDeviceStatus;
auto ret = m_iDevice->GetDeviceStatus(iDeviceStatus);
if (ret != HDF_SUCCESS) {
LOGE("Get HDI device status failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
status = HDIToNN::TransHDIDeviceStatus(iDeviceStatus);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::GetSupportedOperation(std::shared_ptr<const mindspore::lite::LiteGraph> model,
std::vector<bool>& ops)
{
if (model == nullptr) {
LOGE("Model is nullptr, cannot query supported operation.");
return OH_NN_NULL_PTR;
}
V1_0::SharedBuffer tensorBuffer {INVALID_FD, 0, 0, 0};
size_t tensorSize = mindspore::lite::MindIR_LiteGraph_GetConstTensorSize(model.get());
int32_t hdiRet {0};
if (tensorSize > 0) {
hdiRet = m_iDevice->AllocateBuffer(tensorSize, tensorBuffer);
if (hdiRet != HDF_SUCCESS || tensorBuffer.fd == INVALID_FD) {
LOGE("Allocate tensor buffer error when get supported operation. ErrorCode: %d", hdiRet);
return OH_NN_FAILED;
}
}
auto iModel = mindspore::lite::MindIR_LiteGraph_To_Model(model.get(), tensorBuffer);
if (iModel == nullptr) {
LOGE("Parse litegraph to hdi model failed.");
ReleaseSharedBuffer(tensorBuffer);
return OH_NN_FAILED;
}
hdiRet = m_iDevice->GetSupportedOperation(*iModel, ops);
mindspore::lite::MindIR_Model_Destroy(&iModel);
auto ret = ReleaseSharedBuffer(tensorBuffer);
if (ret != OH_NN_SUCCESS) {
LOGE("Release tensorBuffer failed.");
return OH_NN_FAILED;
}
if (hdiRet != HDF_SUCCESS) {
LOGE("Get supported operation failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::IsFloat16PrecisionSupported(bool& isSupported)
{
auto ret = m_iDevice->IsFloat16PrecisionSupported(isSupported);
if (ret != HDF_SUCCESS) {
LOGE("Query fp16 precision supported failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::IsPerformanceModeSupported(bool& isSupported)
{
auto ret = m_iDevice->IsPerformanceModeSupported(isSupported);
if (ret != HDF_SUCCESS) {
LOGE("Query performance mode supported failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::IsPrioritySupported(bool& isSupported)
{
auto ret = m_iDevice->IsPrioritySupported(isSupported);
if (ret != HDF_SUCCESS) {
LOGE("Query priority supported failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::IsDynamicInputSupported(bool& isSupported)
{
auto ret = m_iDevice->IsDynamicInputSupported(isSupported);
if (ret != HDF_SUCCESS) {
LOGE("Query dynamic input supported failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::IsModelCacheSupported(bool& isSupported)
{
auto ret = m_iDevice->IsModelCacheSupported(isSupported);
if (ret != HDF_SUCCESS) {
LOGE("Query cache model supported failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::PrepareModel(std::shared_ptr<const mindspore::lite::LiteGraph> model,
const ModelConfig& config,
std::shared_ptr<PreparedModel>& preparedModel)
{
if (model == nullptr) {
LOGE("Model is nullptr, cannot prepare model.");
return OH_NN_INVALID_PARAMETER;
}
V1_0::SharedBuffer tensorBuffer {INVALID_FD, 0, 0, 0};
size_t tensorSize = mindspore::lite::MindIR_LiteGraph_GetConstTensorSize(model.get());
int32_t hdiRet {0};
if (tensorSize > 0) {
hdiRet = m_iDevice->AllocateBuffer(tensorSize, tensorBuffer);
if (hdiRet != HDF_SUCCESS || tensorBuffer.fd == INVALID_FD) {
LOGE("Allocate tensor buffer error when prepare model. ErrorCode: %d", hdiRet);
return OH_NN_FAILED;
}
}
V1_0::Model* iModel = mindspore::lite::MindIR_LiteGraph_To_Model(model.get(), tensorBuffer);
if (iModel == nullptr) {
LOGE("Parse litegraph to hdi model failed.");
ReleaseSharedBuffer(tensorBuffer);
return OH_NN_FAILED;
}
V1_0::ModelConfig iModelConfig;
iModelConfig.enableFloat16 = config.enableFloat16;
iModelConfig.mode = NNToHDI::TransPerformanceMode(config.mode);
iModelConfig.priority = NNToHDI::TransPriority(config.priority);
OHOS::sptr<V1_0::IPreparedModel> iPreparedModel;
auto preparedRet = m_iDevice->PrepareModel(*iModel, iModelConfig, iPreparedModel);
mindspore::lite::MindIR_Model_Destroy(&iModel);
auto ret = ReleaseSharedBuffer(tensorBuffer);
if (ret != OH_NN_SUCCESS) {
LOGE("Release tensorBuffer failed.");
return OH_NN_FAILED;
}
if (preparedRet != HDF_SUCCESS || iPreparedModel == nullptr) {
LOGE("Prepare model failed. ErrorCode=%d", preparedRet);
return OH_NN_FAILED;
}
preparedModel = CreateSharedPtr<HDIPreparedModel>(iPreparedModel);
if (preparedModel == nullptr) {
LOGE("Prepare model failed, because fail to create preparedModel instance.");
return OH_NN_MEMORY_ERROR;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::PrepareModelFromModelCache(const std::vector<ModelBuffer>& modelCache,
const ModelConfig& config,
std::shared_ptr<PreparedModel>& preparedModel)
{
std::vector<V1_0::SharedBuffer> iBuffers;
auto memManager = MemoryManager::GetInstance();
Memory memory;
OH_NN_ReturnCode ret;
size_t modelCacheSize = modelCache.size();
for (size_t i = 0; i < modelCacheSize; i++) {
ret = memManager->GetMemory(modelCache[i].buffer, memory);
if (ret != OH_NN_SUCCESS) {
LOGE("The %zuth model cache is invalid. Please put valid model cache.", i + 1);
return ret;
}
iBuffers.emplace_back(V1_0::SharedBuffer {memory.fd, memory.length, 0, memory.length});
}
V1_0::ModelConfig iModelConfig;
iModelConfig.enableFloat16 = config.enableFloat16;
iModelConfig.mode = NNToHDI::TransPerformanceMode(config.mode);
iModelConfig.priority = NNToHDI::TransPriority(config.priority);
OHOS::sptr<V1_0::IPreparedModel> iPreparedModel;
auto hdiRet = m_iDevice->PrepareModelFromModelCache(iBuffers, iModelConfig, iPreparedModel);
if (hdiRet != HDF_SUCCESS) {
LOGE("Prepare model from cache failed. ErrorCode=%d", hdiRet);
return OH_NN_UNAVALIDABLE_DEVICE;
}
preparedModel = CreateSharedPtr<HDIPreparedModel>(iPreparedModel);
if (preparedModel == nullptr) {
LOGE("Prepare model from model cache failed, because fail to create preparedModel instance.");
return OH_NN_MEMORY_ERROR;
}
return OH_NN_SUCCESS;
}
void* HDIDevice::AllocateBuffer(size_t length)
{
if (length == 0) {
LOGE("The length param is invalid, length=0");
return nullptr;
}
V1_0::SharedBuffer buffer;
auto ret = m_iDevice->AllocateBuffer(length, buffer);
if (ret != HDF_SUCCESS) {
LOGE("Allocate buffer error. ErrorCode: %d", ret);
return nullptr;
}
auto memManager = MemoryManager::GetInstance();
auto addr = memManager->MapMemory(buffer.fd, length);
if (addr == nullptr) {
LOGE("Map fd to address failed.");
}
return addr;
}
OH_NN_ReturnCode HDIDevice::ReleaseBuffer(const void* buffer)
{
if (buffer == nullptr) {
LOGE("Buffer is nullptr, no need to release.");
return OH_NN_INVALID_PARAMETER;
}
auto memManager = MemoryManager::GetInstance();
Memory memory;
auto ret = memManager->GetMemory(buffer, memory);
if (ret != OH_NN_SUCCESS) {
LOGE("Invalid Buffer, it is not NNRt buffer.");
return ret;
}
V1_0::SharedBuffer hdiBuffer {memory.fd, memory.length, 0, memory.length};
auto deviceResult = m_iDevice->ReleaseBuffer(hdiBuffer);
if (deviceResult != HDF_SUCCESS) {
LOGE("Device release buffer error. ErrorCode: %d", deviceResult);
return OH_NN_FAILED;
}
ret = memManager->UnMapMemory(buffer);
if (ret != OH_NN_SUCCESS) {
LOGE("Unmap memory failed.");
return ret;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIDevice::ReleaseSharedBuffer(const V1_0::SharedBuffer& buffer)
{
if (buffer.fd == INVALID_FD) {
LOGI("No need to release. fd=%d", INVALID_FD);
return OH_NN_SUCCESS;
}
auto ret = m_iDevice->ReleaseBuffer(buffer);
if (ret != HDF_SUCCESS) {
LOGE("Device release buffer error. ErrorCode=%d", ret);
return OH_NN_FAILED;
}
return OH_NN_SUCCESS;
}
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_HDI_DEVICE_H
#define NEURAL_NETWORK_RUNTIME_HDI_DEVICE_H
#include "refbase.h"
#include "hdi_interfaces.h"
#include "interfaces/oem/cpp_api/device.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
class HDIDevice : public Device {
public:
explicit HDIDevice(OHOS::sptr<V1_0::INnrtDevice> device);
OH_NN_ReturnCode GetDeviceName(std::string& name) override;
OH_NN_ReturnCode GetVendorName(std::string& name) override;
OH_NN_ReturnCode GetDeviceType(OH_NN_DeviceType& deviceType) override;
OH_NN_ReturnCode GetDeviceStatus(DeviceStatus& status) override;
OH_NN_ReturnCode GetSupportedOperation(std::shared_ptr<const mindspore::lite::LiteGraph> model,
std::vector<bool>& ops) override;
OH_NN_ReturnCode IsFloat16PrecisionSupported(bool& isSupported) override;
OH_NN_ReturnCode IsPerformanceModeSupported(bool& isSupported) override;
OH_NN_ReturnCode IsPrioritySupported(bool& isSupported) override;
OH_NN_ReturnCode IsDynamicInputSupported(bool& isSupported) override;
OH_NN_ReturnCode IsModelCacheSupported(bool& isSupported) override;
OH_NN_ReturnCode PrepareModel(std::shared_ptr<const mindspore::lite::LiteGraph> model,
const ModelConfig& config,
std::shared_ptr<PreparedModel>& preparedModel) override;
OH_NN_ReturnCode PrepareModelFromModelCache(const std::vector<ModelBuffer>& modelCache,
const ModelConfig& config,
std::shared_ptr<PreparedModel>& preparedModel) override;
void* AllocateBuffer(size_t length) override;
OH_NN_ReturnCode ReleaseBuffer(const void* buffer) override;
private:
OH_NN_ReturnCode ReleaseSharedBuffer(const V1_0::SharedBuffer& buffer);
private:
// first: major version, second: minor version
std::pair<uint32_t, uint32_t> m_hdiVersion;
OHOS::sptr<V1_0::INnrtDevice> m_iDevice {nullptr};
};
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_HDI_DEVICE_H
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_HDI_INTERFACES_H
#define NEURAL_NETWORK_RUNTIME_HDI_INTERFACES_H
#include <v1_0/nnrt_types.h>
#include <v1_0/innrt_device.h>
#include <v1_0/iprepared_model.h>
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace V1_0 = OHOS::HDI::Nnrt::V1_0;
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_HDI_INTERFACES_H
+91
View File
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hdi_prepared_model.h"
#include "common/log.h"
#include "memory_manager.h"
#include "transform.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
HDIPreparedModel::HDIPreparedModel(OHOS::sptr<V1_0::IPreparedModel> hdiPreparedModel)
: m_hdiPreparedModel(hdiPreparedModel)
{
hdiPreparedModel->GetVersion(m_hdiVersion.first, m_hdiVersion.second);
}
OH_NN_ReturnCode HDIPreparedModel::ExportModelCache(std::vector<ModelBuffer>& modelCache)
{
if (!modelCache.empty()) {
LOGE("The vector of modelCache should be empty. size=%zu", modelCache.size());
return OH_NN_INVALID_PARAMETER;
}
std::vector<V1_0::SharedBuffer> iBuffers;
auto ret = m_hdiPreparedModel->ExportModelCache(iBuffers);
if (ret != HDF_SUCCESS) {
LOGE("Export model cache failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
auto memManager = MemoryManager::GetInstance();
for (size_t i = 0; i < iBuffers.size(); i++) {
auto addr = memManager->MapMemory(iBuffers[i].fd, iBuffers[i].bufferSize);
if (addr == nullptr) {
LOGE("Export the %zuth model cache failed, cannot not map fd to address.", i + 1);
return OH_NN_MEMORY_ERROR;
}
ModelBuffer modelbuffer {addr, iBuffers[i].bufferSize};
modelCache.emplace_back(modelbuffer);
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode HDIPreparedModel::Run(const std::vector<IOTensor>& inputs, const std::vector<IOTensor>& outputs,
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough)
{
V1_0::IOTensor iTensor;
std::vector<V1_0::IOTensor> iInputTensors;
for (auto& input: inputs) {
iTensor = NNToHDI::TransIOTensor(input);
if (iTensor.data.fd == INVALID_FD) {
LOGE("Transform inputs tensor failed, cannot find data file descriptor.");
return OH_NN_INVALID_PARAMETER;
}
iInputTensors.emplace_back(iTensor);
}
std::vector<V1_0::IOTensor> iOutputTensors;
for (auto& output: outputs) {
iTensor = NNToHDI::TransIOTensor(output);
if (iTensor.data.fd == INVALID_FD) {
LOGE("Transform outputs tensor failed, cannot find data file descriptor.");
return OH_NN_INVALID_PARAMETER;
}
iOutputTensors.emplace_back(iTensor);
}
auto ret = m_hdiPreparedModel->Run(iInputTensors, iOutputTensors, outputsDims, isOutputBufferEnough);
if (ret != HDF_SUCCESS || outputsDims.empty()) {
LOGE("Run model failed. ErrorCode=%d", ret);
return OH_NN_UNAVALIDABLE_DEVICE;
}
return OH_NN_SUCCESS;
}
} // namespace NeuralNetworkRuntime
} // OHOS
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_HDI_PREPARED_MODEL_H
#define NEURAL_NETWORK_RUNTIME_HDI_PREPARED_MODEL_H
#include <vector>
#include "refbase.h"
#include "hdi_interfaces.h"
#include "interfaces/oem/cpp_api/prepared_model.h"
#include "interfaces/oem/cpp_api/cpp_type.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
class HDIPreparedModel : public PreparedModel {
public:
explicit HDIPreparedModel(OHOS::sptr<V1_0::IPreparedModel> hdiPreparedModel);
OH_NN_ReturnCode ExportModelCache(std::vector<ModelBuffer>& modelCache) override;
OH_NN_ReturnCode Run(const std::vector<IOTensor>& inputs,
const std::vector<IOTensor>& outputs,
std::vector<std::vector<int32_t>>& outputsDims,
std::vector<bool>& isOutputBufferEnough) override;
private:
// first: major version, second: minor version
std::pair<uint32_t, uint32_t> m_hdiVersion;
OHOS::sptr<V1_0::IPreparedModel> m_hdiPreparedModel {nullptr};
};
} // namespace NeuralNetworkRuntime
} // OHOS
#endif // NEURAL_NETWORK_RUNTIME_HDI_PREPARED_MODEL_H
+546
View File
@@ -0,0 +1,546 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "inner_model.h"
#include <new>
#include <unordered_map>
#include <vector>
#include "securec.h"
#include "common/utils.h"
#include "common/scoped_trace.h"
#include "device_manager.h"
#include "hdi_device.h"
#include "validation.h"
#include "ops_builder.h"
#include "ops_registry.h"
#include "transform.h"
namespace MSLITE = mindspore::lite;
namespace OHOS {
namespace NeuralNetworkRuntime {
const std::string NNR_MODEL = "NNR_Model";
const std::string LOADED_NNR_MODEL = "Loaded_NNR_Model";
namespace {
class LiteGraphDeleter {
public:
void operator()(MSLITE::LiteGraph* liteGraph) const
{
MindIR_LiteGraph_Destroy(&liteGraph);
}
};
std::shared_ptr<NNTensor> ConstructNNTensorFromLiteGraphTensor(const MSLITE::TensorPtr msTensor)
{
MSLITE::DataType msDataType = MSLITE::MindIR_Tensor_GetDataType(msTensor);
OH_NN_DataType dataType = MSToNN::TransformDataType(msDataType);
std::vector<int32_t> msDims = MSLITE::MindIR_Tensor_GetDims(msTensor);
std::vector<MSLITE::QuantParam> msQuantParams = MSLITE::MindIR_Tensor_GetQuantParams(msTensor);
std::vector<QuantParam> nnQuantParams = MSToNN::TransformQuantParams(msQuantParams);
std::shared_ptr<NNTensor> nnTensor = CreateSharedPtr<NNTensor>();
if (nnTensor == nullptr) {
LOGE("ConstructNNTensorFromLiteGraphTensor failed, error happened when creating NNTensor.");
return nullptr;
}
OH_NN_ReturnCode ret = nnTensor->Build(dataType, msDims, nnQuantParams, OH_NN_TENSOR);
if (ret != OH_NN_SUCCESS) {
LOGE("ConstructNNTensorFromLiteGraphTensor failed, error happened when building NNTensor with attributes.");
return nullptr;
}
return nnTensor;
}
OH_NN_ReturnCode ConstructNNTensorsFromLiteGraph(const MSLITE::LiteGraph* liteGraph,
const std::vector<uint32_t>& indices,
std::vector<std::shared_ptr<NNTensor>>& nnTensors)
{
if (indices.empty()) {
LOGE("ConstructNNTensorsFromLiteGraph failed, passed empty indices list.");
return OH_NN_INVALID_PARAMETER;
}
uint32_t maximumIndex = *(std::max_element(indices.begin(), indices.end()));
if (maximumIndex >= liteGraph->all_tensors_.size()) {
LOGE("ConstructNNTensorsFromLiteGraph failed, index exceed size of all_tensors inside liteGraph.");
return OH_NN_INVALID_PARAMETER;
}
std::shared_ptr<NNTensor> nnTensor;
for (uint32_t i : indices) {
nnTensor = ConstructNNTensorFromLiteGraphTensor(liteGraph->all_tensors_[i]);
if (nnTensor == nullptr) {
LOGE("ConstructNNTensorsFromLiteGraph failed, failed to construct NNTensor from LiteGraphTensor.");
return OH_NN_NULL_PTR;
}
nnTensors.emplace_back(nnTensor);
}
return OH_NN_SUCCESS;
}
} // anonymous namespace
InnerModel::InnerModel() {}
bool InnerModel::IsBuild() const
{
return (m_liteGraph != nullptr);
}
OH_NN_ReturnCode InnerModel::BuildFromLiteGraph(const MSLITE::LiteGraph* liteGraph)
{
NNRT_TRACE_NAME("Build model from lite graph");
if (liteGraph == nullptr) {
LOGE("BuildFromLiteGraph failed, passed empty liteGraph.");
return OH_NN_INVALID_PARAMETER;
}
if (m_liteGraph != nullptr) {
LOGE("BuildFromLiteGraph failed, liteGraph has been built or loaded before.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (!m_allTensors.empty() || !m_ops.empty()) {
LOGE("BuildFromLiteGraph failed, please LoadLiteGraph without adding tensor and operations.");
return OH_NN_OPERATION_FORBIDDEN;
}
m_inputTensors.clear();
OH_NN_ReturnCode ret = ConstructNNTensorsFromLiteGraph(liteGraph, liteGraph->input_indices_, m_inputTensors);
if (ret != OH_NN_SUCCESS) {
LOGE("BuildFromLiteGraph failed, error happened when constructing input NNTensors from liteGraph.");
return ret;
}
m_outputTensors.clear();
ret = ConstructNNTensorsFromLiteGraph(liteGraph, liteGraph->output_indices_, m_outputTensors);
if (ret != OH_NN_SUCCESS) {
LOGE("BuildFromLiteGraph failed, error happened when constructing output NNTensors from liteGraph.");
return ret;
}
m_liteGraph.reset(const_cast<MSLITE::LiteGraph*>(liteGraph), LiteGraphDeleter());
m_liteGraph->name_ = LOADED_NNR_MODEL;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode InnerModel::AddTensor(const OH_NN_Tensor& nnTensor)
{
if (m_liteGraph != nullptr) {
LOGE("AddTensor failed, AddTensor is forbidden after Finish() or LoadLiteGraph() has been called.");
return OH_NN_OPERATION_FORBIDDEN;
}
std::shared_ptr<NNTensor> tensor = CreateSharedPtr<NNTensor>();
if (tensor == nullptr) {
LOGE("AddTensor failed, error happened when creating NNTensor.");
return OH_NN_MEMORY_ERROR;
}
OH_NN_ReturnCode ret = tensor->BuildFromOHNNTensor(nnTensor);
if (ret != OH_NN_SUCCESS) {
LOGE("AddTensor failed, error happened when build NNTensor from OH_NN_Tensor.");
return ret;
}
// The NNTensor is named as "Tensor: <tensor index>"".
tensor->SetName("Tensor: " + std::to_string(m_allTensors.size()));
m_allTensors.emplace_back(tensor);
return OH_NN_SUCCESS;
}
// DOTO: 圈复杂度待优化
OH_NN_ReturnCode InnerModel::SetTensorValue(uint32_t index, const void* buffer, size_t length)
{
if (m_liteGraph != nullptr) {
LOGE("SetTensorValue failed, SetTensorValue is forbidden after Finish() or LoadLiteGraph() has been called.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (index >= m_allTensors.size()) {
LOGE("SetTensorValue failed, passed index %u out of the number of added tensors.", index);
return OH_NN_INVALID_PARAMETER;
}
const std::shared_ptr<NNTensor> tensor = m_allTensors[index];
if (tensor->GetBuffer() != nullptr) {
LOGE("SetTensorValue failed, tensor has been set value twice. Tensor index: %u.", index);
return OH_NN_INVALID_PARAMETER;
}
if (buffer == nullptr) {
LOGW("SetTensorValue passed empty buffer, which makes no effect.");
return OH_NN_SUCCESS;
}
if (tensor->IsDynamicShape()) {
LOGE("SetTensorValue failed, cannot set value to tensor with dynamic shape.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (length != tensor->GetDataLength()) {
LOGE("SetTensorValue failed, get buffer length %zu different from the byte size of tensor %zu.",
length, tensor->GetDataLength());
return OH_NN_INVALID_PARAMETER;
}
// Data will be released inside NNTensor if it is set inside NNTensor using SetBuffer().
void* data = new (std::nothrow) char[length];
if (data == nullptr) {
LOGE("SetTensorValue failed, please check whether it runs out of memory.");
return OH_NN_MEMORY_ERROR;
}
errno_t ret = memcpy_s(data, length, buffer, length);
if (ret != EOK) {
LOGE("SetTensorValue failed, please the information of error number %d from memcpy_s.", ret);
delete [] reinterpret_cast<char*>(data);
return OH_NN_FAILED;
}
tensor->SetBuffer(data, length);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode InnerModel::ValidateInputAndOutput(
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices) const
{
OH_NN_ReturnCode ret = ValidateTensorArray(inputIndices);
if (ret != OH_NN_SUCCESS) {
LOGE("ValidateInputAndOutput failed, please check input indices.");
return ret;
}
ret = ValidateTensorArray(outputIndices);
if (ret != OH_NN_SUCCESS) {
LOGE("ValidateInputAndOutput failed, please check output indices.");
return ret;
}
if (inputIndices.size == 0) {
LOGE("ValidateInputAndOutput failed, passed empty input indices.");
return OH_NN_INVALID_PARAMETER;
}
if (outputIndices.size == 0) {
LOGE("ValidateInputAndOutput failed, passed empty output indices.");
return OH_NN_INVALID_PARAMETER;
}
std::shared_ptr<NNTensor> tensor{nullptr};
for (uint32_t i = 0; i < inputIndices.size; i++) {
tensor = m_allTensors[inputIndices.data[i]];
if (tensor->GetType() != OH_NN_TENSOR) {
LOGE("ValidateInputAndOutput failed, tensor set as input should has type of OH_NN_TENSOR, but receive %d."
"Tensor index: %u.", tensor->GetType(), i);
return OH_NN_INVALID_PARAMETER;
}
}
for (uint32_t i = 0; i < outputIndices.size; i++) {
tensor = m_allTensors[outputIndices.data[i]];
if (tensor->GetType() != OH_NN_TENSOR) {
LOGE("ValidateInputAndOutput failed, tensor set as output should has type of OH_NN_TENSOR, but receive %d."
"Tensor index: %u.", tensor->GetType(), i);
return OH_NN_INVALID_PARAMETER;
}
}
// The number of inputIndices and outputIndices are usually small, so O(n**2) iteration is fine.
for (uint32_t i = 0; i < inputIndices.size; i++) {
for (uint32_t j = 0; j < outputIndices.size; j++) {
if (inputIndices.data[i] == outputIndices.data[j]) {
LOGE("ValidateInputAndOutput failed, should not set an tensor as input and output at the same time, "
"input index %u, output index %u", inputIndices.data[i], outputIndices.data[j]);
return OH_NN_INVALID_PARAMETER;
}
}
}
return OH_NN_SUCCESS;
}
/* Check whether the indices exceed the number of added tensors. */
OH_NN_ReturnCode InnerModel::ValidateTensorArray(const OH_NN_UInt32Array& indices) const
{
OH_NN_ReturnCode ret = Validation::ValidateArray(indices.data, indices.size);
if (ret != OH_NN_SUCCESS) {
LOGE("ValidateTensorArray failed, please check the validity of indices.");
return ret;
}
for (uint32_t i = 0; i < indices.size; i++) {
if (indices.data[i] >= m_allTensors.size()) {
LOGE("ValidateTensors failed, index %u is out of the number of added tensors.", indices.data[i]);
return OH_NN_INVALID_PARAMETER;
}
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode InnerModel::AddOperation(OH_NN_OperationType opType, const OH_NN_UInt32Array& paramIndices,
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices)
{
if (m_liteGraph != nullptr) {
LOGE("AddOperation failed, AddOperation is forbidden after after Finish() or LoadLiteGraph() has been called.");
return OH_NN_OPERATION_FORBIDDEN;
}
OH_NN_ReturnCode ret = ValidateInputAndOutput(inputIndices, outputIndices);
if (ret != OH_NN_SUCCESS) {
LOGE("AddOperation failed, please check inputIndices and outputIndices.");
return ret;
}
std::vector<uint32_t> inputs = ConstructVectorFromArray(inputIndices.data, inputIndices.size);
std::vector<uint32_t> outputs = ConstructVectorFromArray(outputIndices.data, outputIndices.size);
ret = ValidateTensorArray(paramIndices);
if (ret != OH_NN_SUCCESS) {
LOGE("AddOperation failed, please check paramIndices.");
return ret;
}
std::vector<uint32_t> parameters = ConstructVectorFromArray(paramIndices.data, paramIndices.size);
Ops::OpsRegistry& opsRegistry = Ops::OpsRegistry::GetSingleton();
std::unique_ptr<Ops::OpsBuilder> opsBuilder = opsRegistry.GetOpsBuilder(opType);
if (opsBuilder == nullptr) {
LOGE("AddOperation failed, cannot add operation of type: %d.", opType);
return OH_NN_INVALID_PARAMETER;
}
ret = opsBuilder->Build(parameters, inputs, outputs, m_allTensors);
if (ret != OH_NN_SUCCESS) {
LOGE("AddOperation failed, error happens when build operations.");
return ret;
}
m_ops.emplace_back(std::move(opsBuilder));
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode InnerModel::SpecifyInputsAndOutputs(
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices)
{
if (m_liteGraph != nullptr) {
LOGE("SpecifyInputsAndOutputs failed, "
"SpecifyInputsAndOutputs is forbidden after Finish() or LoadLiteGraph() has been called.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (!m_inputTensors.empty()) {
LOGE("SpecifyInputsAndOutputs failed, SpecifyInputsAndOutputs should not be called twice.");
return OH_NN_OPERATION_FORBIDDEN;
}
OH_NN_ReturnCode ret = ValidateInputAndOutput(inputIndices, outputIndices);
if (ret != OH_NN_SUCCESS) {
LOGE("SpecifyInputsAndOutputs failed, please check inputIndices and outputIndices.");
return ret;
}
m_inputIndices = ConstructVectorFromArray(inputIndices.data, inputIndices.size);
m_outputIndices = ConstructVectorFromArray(outputIndices.data, outputIndices.size);
for (uint32_t i : m_inputIndices) {
m_inputTensors.emplace_back(m_allTensors[i]);
}
for (uint32_t i : m_outputIndices) {
m_outputTensors.emplace_back(m_allTensors[i]);
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode InnerModel::Build()
{
NNRT_TRACE_NAME("Build model");
if (m_liteGraph != nullptr) {
LOGE("Build failed,"
" OH_NNModel is not allowed to build again after Build() or BuildFromLiteGraph() has been called.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (m_allTensors.empty()) {
LOGE("Build failed, no OH_NN_Tensor has been added. Must call AddTensor before Build().");
return OH_NN_OPERATION_FORBIDDEN;
}
if (m_ops.empty()) {
LOGE("Build failed, no operation has beed added. Must call AddOperation before Build().");
return OH_NN_OPERATION_FORBIDDEN;
}
if ((m_inputIndices.empty()) || (m_outputIndices.empty())) {
LOGE("Build failed, inputs and outputs are unspecified. Must call SpecifyInputsAndOutputs before Build().");
return OH_NN_OPERATION_FORBIDDEN;
}
MSLITE::LiteGraph* pLiteGraph = new (std::nothrow) MSLITE::LiteGraph();
if (pLiteGraph == nullptr) {
LOGE("Build failed, error happend when creating LiteGraph.");
return OH_NN_MEMORY_ERROR;
}
m_liteGraph.reset(pLiteGraph, LiteGraphDeleter());
m_liteGraph->name_ = NNR_MODEL;
std::unordered_map<uint32_t, uint32_t> modelIDToGraphID;
AddTensorsToLiteGraph(modelIDToGraphID);
OH_NN_ReturnCode ret = AddNodesToLiteGraph(modelIDToGraphID);
if (ret != OH_NN_SUCCESS) {
return ret;
}
// subGraph will be released by LiteGraph if it is added into instance of LiteGraph.
MSLITE::LiteGraph::SubGraph* subGraph = new (std::nothrow) MSLITE::LiteGraph::SubGraph();
if (subGraph == nullptr) {
LOGE("AddNodesToLiteGraph failed, error happened when creating subgraph.");
return OH_NN_NULL_PTR;
}
subGraph->name_ = "NNRt_SubGraph"; // Name of subGraph
subGraph->input_indices_ = m_liteGraph->input_indices_;
subGraph->output_indices_ = m_liteGraph->output_indices_;
uint32_t nodeCount = static_cast<uint32_t>(m_ops.size()); // m_ops.size() smaller than UINT32_MAX
for (uint32_t i = 0; i < nodeCount; i++) {
subGraph->node_indices_.emplace_back(i);
}
m_liteGraph->sub_graphs_.emplace_back(subGraph);
return OH_NN_SUCCESS;
}
void InnerModel::AddTensorsToLiteGraph(std::unordered_map<uint32_t, uint32_t>& modelIDToGraphID)
{
uint32_t graphID = 0;
LiteGraphTensorPtr tensor(nullptr, DestroyLiteGraphTensor);
size_t tensorCount = m_allTensors.size();
for (size_t i = 0; i < tensorCount; i++) {
const std::shared_ptr<NNTensor>& nnTensor = m_allTensors[i];
// If the tensor is used as operation parameter, it will not convert to the tensor of LiteGraph.
if (nnTensor->IsOpParameter()) {
continue;
}
tensor = nnTensor->ConvertToLiteGraphTensor();
m_liteGraph->all_tensors_.emplace_back(tensor.release());
modelIDToGraphID[i] = graphID++;
}
// Note: Indices in m_inputIndices and m_outputIndices have been checked in SpecifyInputAndOutput(), there is no
// need to check twice.
std::vector<uint32_t>& inputIndices = m_liteGraph->input_indices_;
for (uint32_t index : m_inputIndices) {
inputIndices.emplace_back(modelIDToGraphID.at(index));
}
std::vector<uint32_t>& outputIndices = m_liteGraph->output_indices_;
for (uint32_t index : m_outputIndices) {
outputIndices.emplace_back(modelIDToGraphID.at(index));
}
}
OH_NN_ReturnCode InnerModel::AddNodesToLiteGraph(const std::unordered_map<uint32_t, uint32_t>& modelIDToGraphID)
{
MSLITE::LiteGraph::Node* node{nullptr};
size_t opCount = m_ops.size();
Ops::LiteGraphPrimitvePtr primitive = {nullptr, DestroyLiteGraphTensor};
for (size_t i = 0; i < opCount; i++) {
std::unique_ptr<Ops::OpsBuilder>& op = m_ops[i];
// node will be released by LiteGraph if it is added into instance of LiteGraph.
node = new(std::nothrow) MSLITE::LiteGraph::Node();
if (node == nullptr) {
LOGE("AddNodesToLiteGraph failed, error happened when creating LiteGraph tensor.");
return OH_NN_NULL_PTR;
}
node->name_ = op->GetName() + ":" + std::to_string(i);
node->quant_type_ = NNToMS::TransformQuantType(op->GetQuantType());
op->GetInputIndex(node->input_indices_, modelIDToGraphID);
op->GetOutputIndex(node->output_indices_, modelIDToGraphID);
primitive = op->GetPrimitive();
if (primitive == nullptr) {
LOGE("Build %s primitive failed.", op->GetName().c_str());
delete node;
return OH_NN_FAILED;
}
node->primitive_ = primitive.release();
m_liteGraph->all_nodes_.emplace_back(node);
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode InnerModel::GetSupportedOperations(size_t deviceID, const bool** isSupported, uint32_t& opCount)
{
if (m_liteGraph == nullptr) {
LOGE("GetSupportedOperations failed. GetSupportedOperations() must be called after Finish().");
return OH_NN_OPERATION_FORBIDDEN;
}
DeviceManager& deviceManager = DeviceManager::GetInstance();
std::shared_ptr<Device> device = deviceManager.GetDevice(deviceID);
if (device == nullptr) {
LOGE("GetSupportedOperations failed, retrieve device failed.");
return OH_NN_FAILED;
}
std::vector<bool> supportedOperations;
OH_NN_ReturnCode ret = device->GetSupportedOperation(m_liteGraph, supportedOperations);
if (ret != OH_NN_SUCCESS) {
LOGE("GetSupportedOperations failed, error happened when get supported operations from devices.");
return ret;
}
m_supportedOperations.clear();
std::copy(supportedOperations.begin(), supportedOperations.end(), std::back_inserter(m_supportedOperations));
*isSupported = reinterpret_cast<bool*>(m_supportedOperations.data());
opCount = m_supportedOperations.size();
return OH_NN_SUCCESS;
}
std::shared_ptr<MSLITE::LiteGraph> InnerModel::GetLiteGraphs() const
{
return m_liteGraph;
}
std::vector<std::shared_ptr<NNTensor>> InnerModel::GetInputTensors() const
{
return m_inputTensors;
}
std::vector<std::shared_ptr<NNTensor>> InnerModel::GetOutputTensors() const
{
return m_outputTensors;
}
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+67
View File
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_INNER_MODEL_H
#define NEURAL_NETWORK_RUNTIME_INNER_MODEL_H
#include <memory>
#include <unordered_map>
#include "mindir.h"
#include "ops_builder.h"
#include "interfaces/kits/c/neural_network_runtime.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
class InnerModel {
public:
InnerModel();
bool IsBuild() const;
OH_NN_ReturnCode BuildFromLiteGraph(const mindspore::lite::LiteGraph* liteGraph);
OH_NN_ReturnCode AddTensor(const OH_NN_Tensor& nnTensor);
OH_NN_ReturnCode SetTensorValue(uint32_t index, const void* buffer, size_t length);
OH_NN_ReturnCode AddOperation(OH_NN_OperationType opType,
const OH_NN_UInt32Array& paramIndices,
const OH_NN_UInt32Array& inputIndices,
const OH_NN_UInt32Array& outputIndices);
OH_NN_ReturnCode GetSupportedOperations(size_t deviceID, const bool** isSupported, uint32_t& opCount);
OH_NN_ReturnCode SpecifyInputsAndOutputs(
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices);
OH_NN_ReturnCode Build();
std::vector<std::shared_ptr<NNTensor>> GetInputTensors() const;
std::vector<std::shared_ptr<NNTensor>> GetOutputTensors() const;
std::shared_ptr<mindspore::lite::LiteGraph> GetLiteGraphs() const;
private:
void AddTensorsToLiteGraph(std::unordered_map<uint32_t, uint32_t>& modelIDToGraphID);
OH_NN_ReturnCode AddNodesToLiteGraph(const std::unordered_map<uint32_t, uint32_t>& modelIDToGraphID);
OH_NN_ReturnCode ValidateInputAndOutput(
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices) const;
OH_NN_ReturnCode ValidateTensorArray(const OH_NN_UInt32Array& indices) const;
private:
std::vector<char> m_supportedOperations; // std::vector<bool> not support data(), use std::vector<char> instead.
std::vector<uint32_t> m_inputIndices;
std::vector<uint32_t> m_outputIndices;
std::vector<std::unique_ptr<Ops::OpsBuilder>> m_ops;
std::vector<std::shared_ptr<NNTensor>> m_allTensors;
std::vector<std::shared_ptr<NNTensor>> m_inputTensors; // Used to pass input tensors to compilation.
std::vector<std::shared_ptr<NNTensor>> m_outputTensors; // Used to pass output tensors to compilation.
std::shared_ptr<mindspore::lite::LiteGraph> m_liteGraph {nullptr};
};
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_INNER_MODEL_H
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "memory_manager.h"
#include <sys/mman.h>
#include <unistd.h>
#include "interfaces/oem/cpp_api/cpp_type.h"
#include "common/log.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
void* MemoryManager::MapMemory(int fd, size_t length)
{
if (fd < 0) {
LOGE("Invalid fd, fd must greater than 0.");
return nullptr;
}
if (length <= 0 || length > ALLOCATE_BUFFER_LIMIT) {
LOGE("Invalid buffer size, it must greater than 0 and less than 1Gb. length=%zu", length);
return nullptr;
}
void* addr = mmap(nullptr, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
LOGE("Map fd to address failed.");
return nullptr;
}
std::lock_guard<std::mutex> lock(m_mtx);
Memory memory {fd, addr, length};
m_memorys.emplace(addr, memory);
return addr;
}
OH_NN_ReturnCode MemoryManager::UnMapMemory(const void* buffer)
{
if (buffer == nullptr) {
LOGE("Buffer is nullptr, no need to release.");
return OH_NN_INVALID_PARAMETER;
}
auto iter = m_memorys.find(buffer);
if (iter == m_memorys.end()) {
LOGE("This buffer is not found, cannot release.");
return OH_NN_INVALID_PARAMETER;
}
auto& memory = m_memorys[buffer];
auto unmapResult = munmap(const_cast<void*>(memory.data), memory.length);
if (unmapResult != 0) {
LOGE("Unmap memory failed. Please try again.");
return OH_NN_MEMORY_ERROR;
}
memory.data = nullptr;
if (close(memory.fd) != 0) {
LOGE("Close memory fd failed. fd=%d", memory.fd);
return OH_NN_MEMORY_ERROR;
}
std::lock_guard<std::mutex> lock(m_mtx);
m_memorys.erase(iter);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode MemoryManager::GetMemory(const void* buffer, Memory& memory) const
{
if (buffer == nullptr) {
LOGE("Memory is nullptr.");
return OH_NN_NULL_PTR;
}
auto iter = m_memorys.find(buffer);
if (iter == m_memorys.end()) {
LOGE("Memory is not found.");
return OH_NN_INVALID_PARAMETER;
}
memory.fd = iter->second.fd;
memory.data = buffer;
memory.length = iter->second.length;
return OH_NN_SUCCESS;
}
} // NeuralNetworkRuntime
} // OHOS
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_MEMORY_MANAGER_H
#define NEURAL_NETWORK_RUNTIME_MEMORY_MANAGER_H
#include <unordered_map>
#include <mutex>
#include "interfaces/kits/c/neural_network_runtime_type.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
const int INVALID_FD = -1;
struct Memory {
int fd;
const void* data;
size_t length;
};
class MemoryManager {
public:
~MemoryManager() = default;
void* MapMemory(int fd, size_t length);
OH_NN_ReturnCode UnMapMemory(const void* buffer);
OH_NN_ReturnCode GetMemory(const void* buffer, Memory& memory) const;
static MemoryManager* GetInstance()
{
static MemoryManager instance;
return &instance;
}
private:
MemoryManager() {};
MemoryManager(const MemoryManager&) = delete;
MemoryManager& operator=(const MemoryManager&) = delete;
private:
// key: OH_NN_Memory, value: fd
std::unordered_map<const void*, Memory> m_memorys;
std::mutex m_mtx;
};
} // namespace NeuralNetworkRuntime
} // OHOS
#endif // NEURAL_NETWORK_RUNTIME_MEMORY_MANAGER_H
@@ -0,0 +1,682 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "interfaces/innerkits/c/neural_network_runtime_inner.h"
#include "interfaces/kits/c/neural_network_runtime.h"
#include "compilation.h"
#include "device_manager.h"
#include "executor.h"
#include "inner_model.h"
#include "common/log.h"
using namespace OHOS::NeuralNetworkRuntime;
#define NNRT_API __attribute__((visibility("default")))
NNRT_API OH_NNModel *OH_NNModel_Construct(void)
{
InnerModel *innerModel = new(std::nothrow) InnerModel();
if (innerModel == nullptr) {
LOGE("OH_NNModel_Construct failed, please check whether it has enough memory.");
return nullptr;
}
OH_NNModel *nnModel = reinterpret_cast<OH_NNModel*>(innerModel);
return nnModel;
}
NNRT_API OH_NN_ReturnCode OH_NNModel_AddTensor(OH_NNModel *model, const OH_NN_Tensor *tensor)
{
if (model == nullptr) {
LOGE("OH_NNModel_AddTensor failed, passed nullptr to model.");
return OH_NN_INVALID_PARAMETER;
}
if (tensor == nullptr) {
LOGE("OH_NNModel_AddTensor failed, passed nullptr to tensor.");
return OH_NN_INVALID_PARAMETER;
}
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
return innerModel->AddTensor(*tensor);
}
NNRT_API OH_NN_ReturnCode OH_NNModel_AddOperation(OH_NNModel *model,
OH_NN_OperationType op,
const OH_NN_UInt32Array *paramIndices,
const OH_NN_UInt32Array *inputIndices,
const OH_NN_UInt32Array *outputIndices)
{
if (model == nullptr) {
LOGE("OH_NNModel_AddOperation failed, passed nullptr to model.");
return OH_NN_INVALID_PARAMETER;
}
if (paramIndices == nullptr) {
LOGE("OH_NNModel_AddOperation failed, passed nullptr to paramIndices.");
return OH_NN_INVALID_PARAMETER;
}
if (inputIndices == nullptr) {
LOGE("OH_NNModel_AddOperation failed, passed nullptr to inputIndices.");
return OH_NN_INVALID_PARAMETER;
}
if (outputIndices == nullptr) {
LOGE("OH_NNModel_AddOperation failed, passed nullptr to outputIndices.");
return OH_NN_INVALID_PARAMETER;
}
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
return innerModel->AddOperation(op, *paramIndices, *inputIndices, *outputIndices);
}
NNRT_API OH_NN_ReturnCode OH_NNModel_SetTensorData(OH_NNModel *model,
uint32_t index,
const void *dataBuffer,
size_t length)
{
if (model == nullptr) {
LOGE("OH_NNModel_SetTensorData failed, passed nullptr to model.");
return OH_NN_INVALID_PARAMETER;
}
if (dataBuffer == nullptr) {
LOGE("OH_NNModel_SetTensorData failed, passed nullptr to dataBuffer, which has no effect.");
return OH_NN_INVALID_PARAMETER;
}
if (length == 0) {
LOGE("OH_NNModel_SetTensorData failed, passed dataBuffer with length 0, which has no effect.");
return OH_NN_INVALID_PARAMETER;
}
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
return innerModel->SetTensorValue(index, dataBuffer, length);
}
NNRT_API OH_NN_ReturnCode OH_NNModel_SpecifyInputsAndOutputs(OH_NNModel *model,
const OH_NN_UInt32Array *inputIndices,
const OH_NN_UInt32Array *outputIndices)
{
if (model == nullptr) {
LOGE("OH_NNModel_SpecifyInputsAndOutputs failed, passed nullptr to model.");
return OH_NN_INVALID_PARAMETER;
}
if (inputIndices == nullptr) {
LOGE("OH_NNModel_SpecifyInputsAndOutputs failed, passed nullptr to inputIndices.");
return OH_NN_INVALID_PARAMETER;
}
if (outputIndices == nullptr) {
LOGE("OH_NNModel_SpecifyInputsAndOutputs failed, passed nullptr to outputIndices.");
return OH_NN_INVALID_PARAMETER;
}
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
return innerModel->SpecifyInputsAndOutputs(*inputIndices, *outputIndices);
}
NNRT_API OH_NN_ReturnCode OH_NNModel_Finish(OH_NNModel *model)
{
if (model == nullptr) {
LOGE("OH_NNModel_Finish failed, passed nullptr to model.");
return OH_NN_INVALID_PARAMETER;
}
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
return innerModel->Build();
}
NNRT_API OH_NN_ReturnCode OH_NNModel_BuildFromLiteGraph(OH_NNModel *model, const void *liteGraph)
{
if (model == nullptr) {
LOGE("OH_NNModel_BuildFromLiteGraph failed, passed nullptr to model.");
return OH_NN_INVALID_PARAMETER;
}
if (liteGraph == nullptr) {
LOGE("OH_NNModel_BuildFromLiteGraph failed, passed nullptr to liteGraph.");
return OH_NN_INVALID_PARAMETER;
}
auto *pLiteGraph = static_cast<const mindspore::lite::LiteGraph*>(liteGraph);
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
// Once the innerModel built from the liteGraph successfully, the innerModel
// owns the liteGraph, in which case, the invoker should not delete
// the liteGraph actively. Otherwise, the invoker still has the ownership.
return innerModel->BuildFromLiteGraph(pLiteGraph);
}
NNRT_API void OH_NNModel_Destroy(OH_NNModel **model)
{
if (model == nullptr) {
LOGW("OH_NNModel_Destroy has no effect, passed nullptr to model.");
return;
}
if (*model == nullptr) {
LOGW("OH_NNModel_Destroy has no effect, passed nullptr to *model.");
return;
}
InnerModel *innerModel = reinterpret_cast<InnerModel*>(*model);
delete innerModel;
*model = nullptr;
}
NNRT_API OH_NN_ReturnCode OH_NNModel_GetAvailableOperations(OH_NNModel *model,
size_t deviceID,
const bool **isAvailable,
uint32_t *opCount)
{
if (model == nullptr) {
LOGE("OH_NNModel_GetAvailableOperations failed, passed nullptr to model.");
return OH_NN_INVALID_PARAMETER;
}
if (isAvailable == nullptr) {
LOGE("OH_NNModel_GetAvailableOperations failed, passed nullptr to isAvailable.");
return OH_NN_INVALID_PARAMETER;
}
if (*isAvailable != nullptr) {
LOGE("OH_NNModel_GetAvailableOperations failed, *isAvailable is not nullptr.");
return OH_NN_INVALID_PARAMETER;
}
if (opCount == nullptr) {
LOGE("OH_NNModel_GetAvailableOperations failed, passed nullptr to opCount.");
return OH_NN_INVALID_PARAMETER;
}
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
return innerModel->GetSupportedOperations(deviceID, isAvailable, *opCount);
}
NNRT_API OH_NNCompilation *OH_NNCompilation_Construct(const OH_NNModel *model)
{
if (model == nullptr) {
LOGE("OH_NNCompilation_Construct failed, passed nullptr to model.");
return nullptr;
}
const InnerModel *innerModel = reinterpret_cast<const InnerModel*>(model);
if (!innerModel->IsBuild()) {
LOGE("OH_NNCompilation_Construct failed, should call OH_NNModel_Finish before creating compilation.");
return nullptr;
}
Compilation *compilation = new(std::nothrow) Compilation(innerModel);
if (compilation == nullptr) {
LOGE("OH_NNCompilation_Construct failed, please check whether it has enough memory.");
return nullptr;
}
OH_NNCompilation* nnCompilation = reinterpret_cast<OH_NNCompilation*>(compilation);
return nnCompilation;
}
NNRT_API OH_NN_ReturnCode OH_NNCompilation_SetDevice(OH_NNCompilation *compilation, size_t deviceID)
{
if (compilation == nullptr) {
LOGE("OH_NNCompilation_SetDevice failed, passed nullptr to compilation.");
return OH_NN_INVALID_PARAMETER;
}
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
return innerCompilation->SetDevice(deviceID);
}
NNRT_API OH_NN_ReturnCode OH_NNCompilation_SetCache(OH_NNCompilation *compilation,
const char *cachePath,
uint32_t version)
{
if (compilation == nullptr) {
LOGE("OH_NNCompilation_SetCache failed, passed nullptr to compilation.");
return OH_NN_INVALID_PARAMETER;
}
if (cachePath == nullptr) {
LOGE("OH_NNCompilation_SetCache failed, passed nullptr to cachePath.");
return OH_NN_INVALID_PARAMETER;
}
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
return innerCompilation->SetCacheDir(cachePath, version);
}
NNRT_API OH_NN_ReturnCode OH_NNCompilation_SetPerformanceMode(OH_NNCompilation *compilation,
OH_NN_PerformanceMode performanceMode)
{
if (compilation == nullptr) {
LOGE("OH_NNCompilation_SetPerformanceMode failed, passed nullptr to compilation.");
return OH_NN_INVALID_PARAMETER;
}
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
return innerCompilation->SetPerformance(performanceMode);
}
NNRT_API OH_NN_ReturnCode OH_NNCompilation_SetPriority(OH_NNCompilation *compilation,
OH_NN_Priority priority)
{
if (compilation == nullptr) {
LOGE("OH_NNCompilation_SetPriority failed, passed nullptr to compilation.");
return OH_NN_INVALID_PARAMETER;
}
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
return innerCompilation->SetPriority(priority);
}
NNRT_API OH_NN_ReturnCode OH_NNCompilation_EnableFloat16(OH_NNCompilation *compilation, bool enableFloat16)
{
if (compilation == nullptr) {
LOGE("OH_NNCompilation_EnableFloat16 failed, passed nullptr to compilation.");
return OH_NN_INVALID_PARAMETER;
}
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
return innerCompilation->SetEnableFp16(enableFloat16);
}
NNRT_API OH_NN_ReturnCode OH_NNCompilation_Build(OH_NNCompilation *compilation)
{
if (compilation == nullptr) {
LOGE("OH_NNCompilation_Build failed, passed nullptr to compilation.");
return OH_NN_INVALID_PARAMETER;
}
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
return innerCompilation->Build();
}
NNRT_API void OH_NNCompilation_Destroy(OH_NNCompilation **compilation)
{
if (compilation == nullptr) {
LOGW("OH_NNCompilation_Destroy has no effect, passed nullptr to compilation.");
return;
}
if (*compilation == nullptr) {
LOGW("OH_NNCompilation_Destroy has no effect, passed nullptr to *compilation.");
return;
}
Compilation *innerCompilation = reinterpret_cast<Compilation*>(*compilation);
delete innerCompilation;
*compilation = nullptr;
}
NNRT_API OH_NNExecutor *OH_NNExecutor_Construct(OH_NNCompilation *compilation)
{
if (compilation == nullptr) {
LOGE("OH_NNExecutor_Construct failed, passed nullptr to compilation.");
return nullptr;
}
Compilation *innerCompilation = reinterpret_cast<Compilation*>(compilation);
if (!innerCompilation->IsBuild()) {
LOGE("OH_NNExecutor_Construct failed, should call OH_NNCompilation_Build before creating executor.");
return nullptr;
}
Executor* executor = new(std::nothrow) Executor(innerCompilation);
if (executor == nullptr) {
LOGE("OH_NNExecutor_Construct failed, please check whether it has enough memory.");
return nullptr;
}
OH_NNExecutor* nnExecutor = reinterpret_cast<OH_NNExecutor*>(executor);
return nnExecutor;
}
NNRT_API OH_NN_ReturnCode OH_NNExecutor_SetInput(OH_NNExecutor *executor,
uint32_t inputIndex,
const OH_NN_Tensor *tensor,
const void *dataBuffer,
size_t length)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_SetInput failed, passed nullptr to executor.");
return OH_NN_INVALID_PARAMETER;
}
if (tensor == nullptr) {
LOGE("OH_NNExecutor_SetInput failed, passed nullptr to tensor.");
return OH_NN_INVALID_PARAMETER;
}
if (dataBuffer == nullptr) {
LOGE("OH_NNExecutor_SetInput failed, passed nullptr to dataBuffer.");
return OH_NN_INVALID_PARAMETER;
}
if (length == 0) {
LOGE("OH_NNExecutor_SetInput failed, dataBuffer length is 0.");
return OH_NN_INVALID_PARAMETER;
}
Executor* innerExecutor = reinterpret_cast<Executor*>(executor);
return innerExecutor->SetInput(inputIndex, *tensor, dataBuffer, length);
}
NNRT_API OH_NN_ReturnCode OH_NNExecutor_SetOutput(OH_NNExecutor *executor,
uint32_t outputIndex,
void *dataBuffer,
size_t length)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_SetOutput failed, passed nullptr to executor.");
return OH_NN_INVALID_PARAMETER;
}
if (dataBuffer == nullptr) {
LOGE("OH_NNExecutor_SetOutput failed, passed nullptr to dataBuffer.");
return OH_NN_INVALID_PARAMETER;
}
if (length == 0) {
LOGE("OH_NNExecutor_SetOutput failed, dataBuffer length is 0.");
return OH_NN_INVALID_PARAMETER;
}
Executor* innerExecutor = reinterpret_cast<Executor*>(executor);
return innerExecutor->SetOutput(outputIndex, dataBuffer, length);
}
NNRT_API OH_NN_ReturnCode OH_NNExecutor_GetOutputShape(OH_NNExecutor *executor,
uint32_t outputIndex,
int32_t **shape,
uint32_t *shapeLength)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_GetOutputShape failed, passed nullptr to executor.");
return OH_NN_INVALID_PARAMETER;
}
if (shape == nullptr) {
LOGE("OH_NNExecutor_GetOutputShape failed, passed nullptr to shape.");
return OH_NN_INVALID_PARAMETER;
}
if (*shape != nullptr) {
LOGE("OH_NNExecutor_GetOutputShape failed, *shape is not nullptr.");
return OH_NN_INVALID_PARAMETER;
}
if (shapeLength == nullptr) {
LOGE("OH_NNExecutor_GetOutputShape failed, passed nullptr to shapeLength.");
return OH_NN_INVALID_PARAMETER;
}
Executor* innerExecutor = reinterpret_cast<Executor*>(executor);
return innerExecutor->GetOutputShape(outputIndex, shape, *shapeLength);
}
NNRT_API OH_NN_ReturnCode OH_NNExecutor_Run(OH_NNExecutor *executor)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_Run failed, passed nullptr to executor.");
return OH_NN_INVALID_PARAMETER;
}
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
return innerExecutor->Run();
}
NNRT_API OH_NN_Memory *OH_NNExecutor_AllocateInputMemory(OH_NNExecutor *executor, uint32_t inputIndex, size_t length)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_AllocateInputMemory failed, passed nullptr to executor.");
return nullptr;
}
if (length == 0) {
LOGW("OH_NNExecutor_AllocateInputMemory has no effect, passed length equals 0.");
return nullptr;
}
OH_NN_Memory *nnMemory = nullptr;
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
OH_NN_ReturnCode ret = innerExecutor->CreateInputMemory(inputIndex, length, &nnMemory);
if (ret != OH_NN_SUCCESS) {
LOGE("OH_NNExecutor_AllocateInputMemory failed, error happened when creating input memory in executor.");
return nullptr;
}
return nnMemory;
}
NNRT_API OH_NN_Memory *OH_NNExecutor_AllocateOutputMemory(OH_NNExecutor *executor, uint32_t outputIndex, size_t length)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_AllocateOutputMemory failed, passed nullptr to executor.");
return nullptr;
}
if (length == 0) {
LOGW("OH_NNExecutor_AllocateOutputMemory has no effect, passed length equals 0.");
return nullptr;
}
OH_NN_Memory *nnMemory = nullptr;
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
OH_NN_ReturnCode ret = innerExecutor->CreateOutputMemory(outputIndex, length, &nnMemory);
if (ret != OH_NN_SUCCESS) {
LOGE("OH_NNExecutor_AllocateOutputMemory failed, error happened when creating output memory in executor.");
return nullptr;
}
return nnMemory;
}
NNRT_API void OH_NNExecutor_DestroyInputMemory(OH_NNExecutor *executor, uint32_t inputIndex, OH_NN_Memory **memory)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_DestroyInputMemory failed, passed nullptr to executor.");
return;
}
if (memory == nullptr) {
LOGW("OH_NNExecutor_DestroyInputMemory has no effect, passed nullptr to memory.");
return;
}
if (*memory == nullptr) {
LOGW("OH_NNExecutor_DestroyInputMemory has no effect, passed nullptr to *memory.");
return;
}
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
OH_NN_ReturnCode ret = innerExecutor->DestroyInputMemory(inputIndex, memory);
if (ret != OH_NN_SUCCESS) {
LOGE("OH_NNExecutor_DestroyInputMemory failed, error happened when destroying input memory.");
return;
}
*memory = nullptr;
}
NNRT_API void OH_NNExecutor_DestroyOutputMemory(OH_NNExecutor *executor, uint32_t outputIndex, OH_NN_Memory **memory)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_DestroyOutputMemory failed, passed nullptr to executor.");
return;
}
if (memory == nullptr) {
LOGW("OH_NNExecutor_DestroyOutputMemory has no effect, passed nullptr to memory.");
return;
}
if (*memory == nullptr) {
LOGW("OH_NNExecutor_DestroyOutputMemory has no effect, passed nullptr to *memory.");
return;
}
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
OH_NN_ReturnCode ret = innerExecutor->DestroyOutputMemory(outputIndex, memory);
if (ret != OH_NN_SUCCESS) {
LOGE("OH_NNExecutor_DestroyOutputMemory failed, error happened when destroying output memory.");
return;
}
*memory = nullptr;
}
NNRT_API OH_NN_ReturnCode OH_NNExecutor_SetInputWithMemory(OH_NNExecutor *executor,
uint32_t inputIndex,
const OH_NN_Tensor *tensor,
const OH_NN_Memory *memory)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_SetInputWithMemory failed, passed nullptr to executor.");
return OH_NN_INVALID_PARAMETER;
}
if (tensor == nullptr) {
LOGE("OH_NNExecutor_SetInputWithMemory failed, passed nullptr to tensor.");
return OH_NN_INVALID_PARAMETER;
}
if (memory == nullptr) {
LOGE("OH_NNExecutor_SetInputWithMemory failed, passed nullptr to memory.");
return OH_NN_INVALID_PARAMETER;
}
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
return innerExecutor->SetInputFromMemory(inputIndex, *tensor, *memory);
}
NNRT_API OH_NN_ReturnCode OH_NNExecutor_SetOutputWithMemory(OH_NNExecutor *executor,
uint32_t outputIndex,
const OH_NN_Memory *memory)
{
if (executor == nullptr) {
LOGE("OH_NNExecutor_SetOutputWithMemory failed, passed nullptr to executor.");
return OH_NN_INVALID_PARAMETER;
}
if (memory == nullptr) {
LOGE("OH_NNExecutor_SetOutputWithMemory failed, passed nullptr to memory.");
return OH_NN_INVALID_PARAMETER;
}
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
return innerExecutor->SetOutputFromMemory(outputIndex, *memory);
}
NNRT_API void OH_NNExecutor_Destroy(OH_NNExecutor **executor)
{
if (executor == nullptr) {
LOGW("OH_NNExecutor_Destroy has no effect, since executor is nullptr.");
return;
}
if ((*executor) == nullptr) {
LOGW("OH_NNExecutor_Destroy has no effect, since *executor is nullptr");
return;
}
Executor *innerExecutor = reinterpret_cast<Executor*>(*executor);
delete innerExecutor;
*executor = nullptr;
}
NNRT_API OH_NN_ReturnCode OH_NNDevice_GetAllDevicesID(const size_t **allDevicesID, uint32_t *deviceCount)
{
if (allDevicesID == nullptr) {
LOGE("OH_NNDevice_GetAllDevicesID failed, passed nullptr to allDevicesID.");
return OH_NN_INVALID_PARAMETER;
}
if ((*allDevicesID) != nullptr) {
LOGE("OH_NNDevice_GetAllDevicesID failed, *allDevicesID should be nullptr.");
return OH_NN_INVALID_PARAMETER;
}
if (deviceCount == nullptr) {
LOGE("OH_NNDevice_GetAllDevicesID failed, passed nullptr to deviceCount.");
return OH_NN_INVALID_PARAMETER;
}
DeviceManager& deviceManager = DeviceManager::GetInstance();
const std::vector<size_t>& allDevices = deviceManager.GetAllDeviceId();
if (allDevices.empty()) {
LOGW("OH_NNDevice_GetAllDevicesID got no device.");
*allDevicesID = nullptr;
*deviceCount = 0;
return OH_NN_SUCCESS;
}
*allDevicesID = allDevices.data();
// allDevices.size() will not exceed UINT32_MAX, it is safe to cast to uint32_t.
*deviceCount = static_cast<uint32_t>(allDevices.size());
return OH_NN_SUCCESS;
}
NNRT_API OH_NN_ReturnCode OH_NNDevice_GetName(size_t deviceID, const char **name)
{
if (name == nullptr) {
LOGE("OH_NNDevice_GetName failed, passed nullptr to name.");
return OH_NN_INVALID_PARAMETER;
}
if ((*name) != nullptr) {
LOGE("OH_NNDevice_GetName failed, *name should be nullptr.");
return OH_NN_INVALID_PARAMETER;
}
DeviceManager& deviceManager = DeviceManager::GetInstance();
const std::string& deviceName = deviceManager.GetDeviceName(deviceID);
if (deviceName.empty()) {
LOGE("OH_NNDevice_GetName failed, error happened when getting name of deviceID %zu.", deviceID);
*name = nullptr;
return OH_NN_FAILED;
}
*name = deviceName.data();
return OH_NN_SUCCESS;
}
NNRT_API OH_NN_ReturnCode OH_NNDevice_GetType(size_t deviceID, OH_NN_DeviceType* deviceType)
{
DeviceManager& deviceManager = DeviceManager::GetInstance();
std::shared_ptr<Device> device = deviceManager.GetDevice(deviceID);
if (device == nullptr) {
LOGE("OH_NNDevice_GetName failed, passed invalid deviceID.");
return OH_NN_INVALID_PARAMETER;
}
if (deviceType == nullptr) {
LOGE("OH_NNDevice_GetType failed, passed nullptr to deviceType.");
return OH_NN_INVALID_PARAMETER;
}
OH_NN_ReturnCode ret = device->GetDeviceType(*deviceType);
if (ret != OH_NN_SUCCESS) {
LOGE("OH_NNDevice_GetType failed, device id: %zu.", deviceID);
return ret;
}
return OH_NN_SUCCESS;
}
+409
View File
@@ -0,0 +1,409 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <cstdlib>
#include <new>
#include "nn_tensor.h"
#include "validation.h"
#include "transform.h"
#include "common/log.h"
#include "mindir.h"
#include "mindir_types.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
const uint32_t SUPPORT_NUM_BIT = 8; // Currently support 8-bit quantization only
const uint32_t INVALID_NUM_BIT = 0;
void DestroyLiteGraphTensor(void* tensor)
{
mindspore::lite::MindIR_Tensor_Destroy(&tensor);
}
NNTensor::~NNTensor()
{
if (m_buffer != nullptr) {
delete [] reinterpret_cast<char*>(m_buffer);
}
}
NNTensor::NNTensor(NNTensor&& tensor) noexcept
{
*this = std::move(tensor);
}
NNTensor& NNTensor::operator=(NNTensor&& tensor) noexcept
{
if (this == &tensor) {
return *this;
}
m_type = tensor.m_type;
m_dataType = tensor.m_dataType;
m_format = tensor.m_format;
m_name = std::move(tensor.m_name);
m_dimensions = std::move(tensor.m_dimensions);
m_quantParams = std::move(tensor.m_quantParams);
m_elementCount = tensor.m_elementCount;
m_isDynamicShape = tensor.m_isDynamicShape;
m_isOpParameter = tensor.m_isOpParameter;
m_buffer = tensor.m_buffer;
m_bufferLength = tensor.m_bufferLength;
m_dataLength = tensor.m_dataLength;
tensor.m_buffer = nullptr;
tensor.m_bufferLength = 0;
tensor.m_dataLength = 0;
return *this;
}
OH_NN_ReturnCode NNTensor::Build(OH_NN_DataType dataType,
const std::vector<int32_t>& dimensions,
const std::vector<QuantParam>& quantParam,
OH_NN_TensorType type)
{
m_type = type;
if (!Validation::ValidateTensorDataType(dataType)) {
LOGE("Build failed, passed invalid data type.");
return OH_NN_INVALID_PARAMETER;
}
m_dataType = dataType;
OH_NN_ReturnCode ret = ParseDimensions(dimensions);
if (ret != OH_NN_SUCCESS) {
LOGE("Build failed, passed invalid dimensions.");
return ret;
}
ret = ParseQuantParams(quantParam);
if (ret != OH_NN_SUCCESS) {
LOGE("Build failed, please check quantParam.");
return ret;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode NNTensor::BuildFromOHNNTensor(const OH_NN_Tensor& nnTensor)
{
m_type = nnTensor.type;
if (!Validation::ValidateTensorDataType(nnTensor.dataType)) {
LOGE("BuildFromOHNNTensor failed, passed invalid data type: %d.", nnTensor.dataType);
return OH_NN_INVALID_PARAMETER;
}
m_dataType = nnTensor.dataType;
if (!Validation::ValidateTensorType(nnTensor.type)) {
LOGE("BuildFromOHNNTensor failed, passed invalid nnTensor type: %d.", nnTensor.type);
return OH_NN_INVALID_PARAMETER;
}
OH_NN_ReturnCode ret = ParseDimensions(nnTensor);
if (ret != OH_NN_SUCCESS) {
LOGE("BuildFromOHNNTensor failed, passed invalid nnTensor dimensions.");
return ret;
}
ret = ParseQuantParams(nnTensor.quantParam);
if (ret != OH_NN_SUCCESS) {
LOGE("BuildFromOHNNTensor failed, please check quantParam in nnTensor.");
return ret;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode NNTensor::ParseDimensions(const std::vector<int32_t>& dimensions)
{
// Temporary variable to check overflow.
uint64_t absoluteDim {0};
uint64_t elementCount {1};
uint64_t dataLength {static_cast<uint64_t>(GetTypeSize(m_dataType))};
m_isDynamicShape = false;
for (int32_t dim : dimensions) {
if (dim < -1 || dim == 0) {
LOGE("ParseDimension failed, dimension of OH_NN_Tensor cannot be 0 or less than -1, receive %d.", dim);
return OH_NN_INVALID_PARAMETER;
}
m_isDynamicShape = m_isDynamicShape || (dim == -1);
absoluteDim = static_cast<uint64_t>(abs(dim));
elementCount *= absoluteDim;
dataLength *= absoluteDim;
if (dataLength > UINT32_MAX) {
LOGE("ParseDimension failed, expected data length of tensor exceed limit %u.", UINT32_MAX);
return OH_NN_INVALID_PARAMETER;
}
}
if (m_isDynamicShape) {
// If tensor has dynamic shape, m_elementCount and m_dataLength take 0.
m_elementCount = 0;
m_dataLength = 0;
} else {
m_elementCount = static_cast<uint32_t>(elementCount);
m_dataLength = static_cast<size_t>(dataLength);
}
m_dimensions = std::move(dimensions);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode NNTensor::ParseDimensions(const OH_NN_Tensor& nnTensor)
{
OH_NN_ReturnCode ret = Validation::ValidateArray(nnTensor.dimensions, nnTensor.dimensionCount);
if (ret != OH_NN_SUCCESS) {
LOGE("BuildFromOHNNTensor failed, please check dimension and dimensionCount in NNTensor.");
return ret;
}
std::vector<int32_t> dimensions = ConstructVectorFromArray(nnTensor.dimensions, nnTensor.dimensionCount);
ret = ParseDimensions(dimensions);
if (ret != OH_NN_SUCCESS) {
LOGE("BuildFromOHNNTensor failed, passed invalid dimension info.");
return ret;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode NNTensor::ParseQuantParams(const OH_NN_QuantParam* quantParam)
{
if (quantParam == nullptr) {
return OH_NN_SUCCESS;
}
if ((quantParam->numBits == nullptr) || (quantParam->scale == nullptr) || (quantParam->zeroPoint == nullptr)) {
LOGE("ParseQuantParams failed, scale or zeroPoint is nullptr.");
return OH_NN_INVALID_PARAMETER;
}
std::vector<QuantParam> tmpQuantParam;
uint32_t numBits{0};
double scale{0.0};
int32_t zeroPoint{0};
for (uint32_t i = 0; i < quantParam->quantCount; i++) {
numBits = quantParam->numBits[i];
scale = quantParam->scale[i];
zeroPoint = quantParam->zeroPoint[i];
tmpQuantParam.emplace_back((QuantParam){numBits, scale, zeroPoint});
}
OH_NN_ReturnCode ret = ParseQuantParams(tmpQuantParam);
if (ret != OH_NN_SUCCESS) {
LOGE("ParseQuantParams failed, please numBits in NNTensor.");
return ret;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode NNTensor::ParseQuantParams(const std::vector<QuantParam>& quantParams)
{
for (const QuantParam& param : quantParams) {
// Only support 8-bit quantization in NNR version 1.0
if ((param.numBits != SUPPORT_NUM_BIT) || (param.numBits == INVALID_NUM_BIT)) {
LOGE("ParseQuantParams failed, get invalid numBits %d.", param.numBits);
return OH_NN_INVALID_PARAMETER;
}
}
m_quantParams = quantParams;
return OH_NN_SUCCESS;
}
void NNTensor::IdentifyOpParameter()
{
m_isOpParameter = true;
}
void NNTensor::SetName(const std::string& name)
{
m_name = name;
}
// Buffer set inside NNTensor will be released during deconstruction, make sure the buffer won't be released twice.
void NNTensor::SetBuffer(const void* buffer, size_t length)
{
// copy pointer instead of memory copying
m_buffer = const_cast<void*>(buffer);
m_bufferLength = length;
}
OH_NN_ReturnCode NNTensor::SetDimensions(const std::vector<int32_t>& dimensions)
{
size_t expectedDimensionCount = m_dimensions.size();
size_t dimensionCount = dimensions.size();
if (dimensionCount != expectedDimensionCount) {
LOGE("Passed dimensions have different dimension counts from NNTensor, expected %zu, but passed %zu.",
expectedDimensionCount, dimensionCount);
return OH_NN_INVALID_PARAMETER;
}
auto ret = ParseDimensions(dimensions);
if (ret != OH_NN_SUCCESS) {
LOGE("SetDimemsions failed, passed invalid dimension info.");
return ret;
}
m_dimensions = dimensions;
return OH_NN_SUCCESS;
}
OH_NN_TensorType NNTensor::GetType() const
{
return m_type;
}
std::string NNTensor::GetName() const
{
return m_name;
}
void* NNTensor::GetBuffer() const
{
return m_buffer;
}
size_t NNTensor::GetBufferLength() const
{
return m_bufferLength;
}
size_t NNTensor::GetDataLength() const
{
return m_dataLength;
}
OH_NN_DataType NNTensor::GetDataType() const
{
return m_dataType;
}
uint32_t NNTensor::GetElementCount() const
{
return m_elementCount;
}
std::vector<int32_t> NNTensor::GetDimensions() const
{
return m_dimensions;
}
OH_NN_Format NNTensor::GetFormat() const
{
return m_format;
}
std::vector<QuantParam> NNTensor::GetQuantParam() const
{
return m_quantParams;
}
LiteGraphTensorPtr NNTensor::ConvertToLiteGraphTensor() const
{
mindspore::lite::DataType dataType = NNToMS::TransformDataType(m_dataType);
mindspore::lite::Format format = NNToMS::TransformFormat(m_format);
const uint8_t* buffer = static_cast<const uint8_t*>(m_buffer);
std::vector<uint8_t> data = ConstructVectorFromArray(buffer, m_dataLength);
std::vector<mindspore::lite::QuantParam> quantParams;
mindspore::lite::QuantParam msQuantParam;
for (const QuantParam& param : m_quantParams) {
msQuantParam = {param.zeroPoint, param.scale, param.numBits};
quantParams.emplace_back(std::move(msQuantParam));
}
mindspore::lite::TensorPtr tensor = mindspore::lite::MindIR_Tensor_Create(
m_name, dataType, m_dimensions, format, data, quantParams);
if (tensor == nullptr) {
LOGE("ConvertToLiteGraphTensor failed, please check attributes of NNTensor.");
return {nullptr, DestroyLiteGraphTensor};
}
LiteGraphTensorPtr liteGraphTensor(tensor, DestroyLiteGraphTensor);
return liteGraphTensor;
}
void NNTensor::ConvertToIOTensor(IOTensor& tensor) const
{
tensor.dataType = m_dataType;
tensor.format = m_format;
tensor.dimensions = m_dimensions;
tensor.data = const_cast<void*>(m_buffer);
tensor.length = m_bufferLength;
}
bool NNTensor::IsDynamicShape() const
{
return m_isDynamicShape;
}
bool NNTensor::IsQuantTensor() const
{
return (m_quantParams.size() > 0);
}
bool NNTensor::IsScalar() const
{
return (m_dimensions.empty());
}
bool NNTensor::IsOpParameter() const
{
return m_isOpParameter;
}
bool NNTensor::CompareAttribute(const NNTensor& tensor) const
{
if (m_dataType != tensor.GetDataType()) {
LOGI("Tensors have different data type: %d and %d.", m_dataType, tensor.GetDataType());
return false;
}
if (m_format != tensor.GetFormat()) {
LOGI("Tensors have different format: %d and %d.", m_format, tensor.GetFormat());
return false;
}
const std::vector<int32_t> dimensions = tensor.GetDimensions();
if (m_dimensions.size() != dimensions.size()) {
LOGI("Tensors have differents dimension counts: %zu and %zu.", m_dimensions.size(), dimensions.size());
return false;
}
for (auto i = 0; i < dimensions.size(); i++) {
if (m_dimensions[i] != -1 && m_dimensions[i] != dimensions[i]) {
LOGI("Tensors have different dimension: dimension index: %u, dimension value: %d and %d.",
i, m_dimensions[i], dimensions[i]);
return false;
}
}
if (m_type != tensor.GetType()) {
LOGI("Tensors have different type: %d and %d.", m_type, tensor.GetType());
return false;
}
return true;
}
} // NeuralNetworkRuntime
} // OHOS
+97
View File
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_NN_TENSOR_H
#define NEURAL_NETWORK_RUNTIME_NN_TENSOR_H
#include <string>
#include <vector>
#include "interfaces/oem/cpp_api/cpp_type.h"
#include "interfaces/kits/c/neural_network_runtime.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
using LiteGraphTensorPtr = std::unique_ptr<void, void(*)(void*)>;
void DestroyLiteGraphTensor(void* tensor);
class NNTensor {
public:
NNTensor() = default;
~NNTensor();
NNTensor(NNTensor&& tensor) noexcept;
NNTensor& operator=(NNTensor&& tensor) noexcept;
// Copy construction and assignment is not allowed in case of double-free of m_buffer
NNTensor(const NNTensor& tensor) = delete;
NNTensor& operator=(const NNTensor& tensor) = delete;
OH_NN_ReturnCode BuildFromOHNNTensor(const OH_NN_Tensor& nnTensor);
OH_NN_ReturnCode Build(OH_NN_DataType dataType,
const std::vector<int32_t>& dimensions,
const std::vector<QuantParam>& quantParam,
OH_NN_TensorType type);
void IdentifyOpParameter();
void SetName(const std::string& name);
void SetBuffer(const void* buffer, size_t length);
OH_NN_ReturnCode SetDimensions(const std::vector<int32_t>& dimensions);
std::string GetName() const;
OH_NN_TensorType GetType() const;
void* GetBuffer() const;
// Return complete buffer length
size_t GetBufferLength() const;
// Return actual data length, since the data can be store in a larger buffer
size_t GetDataLength() const;
OH_NN_DataType GetDataType() const;
uint32_t GetElementCount() const;
std::vector<int32_t> GetDimensions() const;
OH_NN_Format GetFormat() const;
std::vector<QuantParam> GetQuantParam() const;
LiteGraphTensorPtr ConvertToLiteGraphTensor() const;
void ConvertToIOTensor(IOTensor& tensor) const;
bool IsDynamicShape() const;
bool IsQuantTensor() const;
bool IsScalar() const;
bool IsOpParameter() const;
bool CompareAttribute(const NNTensor& tensor) const;
private:
// Used in BuildFromOHNNTensor()
OH_NN_ReturnCode ParseQuantParams(const OH_NN_QuantParam* quantParams);
OH_NN_ReturnCode ParseDimensions(const OH_NN_Tensor& nnTensor);
// Used in Build()
OH_NN_ReturnCode ParseQuantParams(const std::vector<QuantParam>& quantParams);
OH_NN_ReturnCode ParseDimensions(const std::vector<int32_t>& dimensions);
private:
OH_NN_TensorType m_type {OH_NN_TENSOR};
OH_NN_DataType m_dataType {OH_NN_FLOAT32};
OH_NN_Format m_format {OH_NN_FORMAT_NHWC};
std::string m_name;
std::vector<int32_t> m_dimensions;
std::vector<QuantParam> m_quantParams;
uint32_t m_elementCount {0};
bool m_isDynamicShape {false};
bool m_isOpParameter {false};
void* m_buffer {nullptr};
size_t m_bufferLength {0};
size_t m_dataLength {0};
};
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_NN_TENSOR_H
+115
View File
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "add_builder.h"
#include "frameworks/native/transform.h"
#include "frameworks/native/validation.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static const int INPUT_NUM = 2;
static const int OUTPUT_NUM = 1;
static const std::string OP_NAME = "Add";
AddBuilder::AddBuilder() {}
AddBuilder::~AddBuilder() {}
OH_NN_ReturnCode AddBuilder::SetActivation(std::shared_ptr<NNTensor>& tensor)
{
tensor->IdentifyOpParameter();
if (tensor->GetDataType() != OH_NN_INT8) {
LOGE("[Add] SetActivation failed, the activationType should be type OH_NN_INT8.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Add] SetActivation GetBuffer return nullptr.");
return OH_NN_INVALID_PARAMETER;
}
int8_t* fuseData = static_cast<int8_t*>(buffer);
if (!Validation::ValidateFuseType(static_cast<OH_NN_FuseType>(*fuseData))) {
LOGE("[Add] SetActivation failed, fuse activation type is invalid.");
return OH_NN_INVALID_PARAMETER;
}
m_activationType = NNToMS::TransfromFusionType(static_cast<OH_NN_FuseType>(*fuseData));
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode AddBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
if (m_isBuild) {
LOGE("[Add] Build failed, operation has been build, cannot build again.");
return OH_NN_OPERATION_FORBIDDEN;
}
auto ret = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
if (ret != OH_NN_SUCCESS) {
LOGE("[Add] Build failed, the input or output index of Add operation is invalid.");
return ret;
}
m_inputsIndex = inputsIndex;
m_outputsIndex = outputsIndex;
for (uint32_t i : paramsIndex) {
std::shared_ptr<NNTensor> tensor = allTensors[i];
switch (tensor->GetType()) {
case OH_NN_ADD_ACTIVATIONTYPE:
ret = SetActivation(tensor);
break;
default:
LOGE("[Add] Build failed, param invalid, type = %d.", tensor->GetType());
return OH_NN_INVALID_PARAMETER;
}
if (ret != OH_NN_SUCCESS) {
LOGE("[Add] Build failed, passed invalid param.");
return ret;
}
}
// The quantization type of the first output determinies that of the operator.
SetQuantType(outputsIndex, allTensors);
m_name = OP_NAME;
m_isBuild = true;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr AddBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[Add] GetPrimitive failed, cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
void* primitive = mindspore::lite::MindIR_AddFusion_CreatePrimitive(m_activationType);
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive) ;
return graphPrimitivePtr;
}
REGISTER_OPS(AddBuilder, OH_NN_OPS_ADD);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_ADD_BUILDER_H
#define NEURAL_NETWORK_RUNTIME_ADD_BUILDER_H
#include "mindir.h"
#include "frameworks/native/ops_builder.h"
#include "frameworks/native/ops_registry.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class AddBuilder : public OpsBuilder {
public:
AddBuilder();
~AddBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
private:
OH_NN_ReturnCode SetActivation(std::shared_ptr<NNTensor>& tensor);
private:
mindspore::lite::ActivationType m_activationType{mindspore::lite::ACTIVATION_TYPE_NO_ACTIVATION};
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_ADD_BUILDER_H
+131
View File
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "argmax_builder.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static const int INPUT_NUM = 1;
static const int OUTPUT_NUM = 1;
static const std::string OP_NAME = "ArgMax";
ArgMaxBuilder::ArgMaxBuilder() {}
ArgMaxBuilder::~ArgMaxBuilder() {}
OH_NN_ReturnCode ArgMaxBuilder::SetAxis(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[ArgMax] SetAxis failed, the axis should be type HNN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[ArgMax] SetAxis GetBuffer return nullptr.");
return OH_NN_INVALID_PARAMETER;
}
m_axis = *(static_cast<int64_t*>(buffer));
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode ArgMaxBuilder::SetKeepdims(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
if (tensor->GetDataType() != OH_NN_BOOL) {
LOGE("[ArgMax] SetKeepdims failed, the keep_dims should be type HNN_BOOL.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[ArgMax] SetKeepdims GetBuffer return nullptr.");
return OH_NN_INVALID_PARAMETER;
}
m_keepDims = *(static_cast<bool*>(buffer));
return OH_NN_SUCCESS;
}
/**
* Build method.
* 1.build primitive of ops.
* 2.build inputIndex of ops.
* 3.build outputIndex of ops.
*/
OH_NN_ReturnCode ArgMaxBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex, const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
if (m_isBuild) {
LOGE("[ArgMax] Build failed, build operation has been completed, cannot build again.");
return OH_NN_OPERATION_FORBIDDEN;
}
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
if (returnCode != OH_NN_SUCCESS) {
LOGE("[ArgMax] Build failed, passed invalid input or output index.");
return returnCode;
}
m_inputsIndex = inputsIndex;
m_outputsIndex = outputsIndex;
for (int i : paramsIndex) {
const std::shared_ptr<NNTensor> tensor = allTensors[i];
switch (tensor->GetType()) {
case OH_NN_ARG_MAX_AXIS:
returnCode = SetAxis(tensor);
break;
case OH_NN_ARG_MAX_KEEPDIMS:
returnCode = SetKeepdims(tensor);
break;
default:
LOGE("[ArgMax] Build failed, param invalid, type = %d.", tensor->GetType());
return OH_NN_INVALID_PARAMETER;
}
if (returnCode != OH_NN_SUCCESS) {
LOGE("[ArgMax] Build failed, passed invalid param.");
return returnCode;
}
}
// The quantization type of the first output determinies that of the operator.
SetQuantType(outputsIndex, allTensors);
m_name = OP_NAME;
m_isBuild = true;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr ArgMaxBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[ArgMax] GetPrimitive failed, cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
void* primitive = mindspore::lite::MindIR_ArgMaxFusion_CreatePrimitive(m_axis, m_topK, m_keepDims, m_outMaxValue);
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
return graphPrimitivePtr;
}
REGISTER_OPS(ArgMaxBuilder, OH_NN_OPS_ARG_MAX);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+51
View File
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_ARGMAX_BUILDER_H
#define NEURAL_NETWORK_RUNTIME_ARGMAX_BUILDER_H
#include "mindir.h"
#include "frameworks/native/ops_builder.h"
#include "frameworks/native/ops_registry.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class ArgMaxBuilder : public OpsBuilder {
public:
ArgMaxBuilder();
~ArgMaxBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
private:
OH_NN_ReturnCode SetAxis(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetKeepdims(std::shared_ptr<NNTensor> tensor);
private:
int64_t m_axis {-1};
int64_t m_topK {1};
bool m_keepDims {false};
bool m_outMaxValue {false};
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_ARGMAX_BUILDER_H
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "avgpool_builder.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static const std::string OP_NAME = "AvgPool";
AvgPoolBuilder::AvgPoolBuilder() {}
AvgPoolBuilder::~AvgPoolBuilder() {}
OH_NN_ReturnCode AvgPoolBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
OH_NN_ReturnCode returnCode = PoolingBuild(paramsIndex, inputsIndex, outputsIndex, allTensors);
if (returnCode != OH_NN_SUCCESS) {
LOGE("[AvgPool] Build failed, the PoolingBuild failed.");
return returnCode;
}
m_isBuild = true;
m_name = OP_NAME;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr AvgPoolBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[AvgPool] GetPrimitive failed, cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
void* primitive = mindspore::lite::MindIR_AvgPoolFusion_CreatePrimitive(m_kernelSize, m_strides, m_pad,
m_padMode, m_roundMode, m_format, m_global, m_activationType);
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
return graphPrimitivePtr;
}
REGISTER_OPS(AvgPoolBuilder, OH_NN_OPS_AVG_POOL);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_AVGPOOL_BUILDER_H
#define NEURAL_NETWORK_AVGPOOL_BUILDER_H
#include "pooling_builder.h"
#include "frameworks/native/ops_registry.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class AvgPoolBuilder : public PoolingBuilder {
public:
AvgPoolBuilder();
~AvgPoolBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_AVGPOOL_BUILDER_H
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "batch_to_space_nd_builder.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static const int INPUT_NUM = 1;
static const int OUTPUT_NUM = 1;
static const int CROPS_ROWS = 2;
static const int CROPS_COLUMN = 2;
static const std::string OP_NAME = "BatchToSpaceND";
BatchToSpaceNDBuilder::BatchToSpaceNDBuilder() {}
BatchToSpaceNDBuilder::~BatchToSpaceNDBuilder() {}
OH_NN_ReturnCode BatchToSpaceNDBuilder::SetInputBlock(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[BatchToSpaceND] SetInputBlock failed, the BlockSize should be type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[BatchToSpaceND] SetInputBlock GetBuffer return nullptr.");
return OH_NN_INVALID_PARAMETER;
}
int64_t* pBlockSize = static_cast<int64_t*>(buffer);
uint32_t elementCount = tensor->GetElementCount();
for (uint32_t i = 0; i < elementCount; ++i) {
m_blockSize.emplace_back(*pBlockSize);
++pBlockSize;
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode BatchToSpaceNDBuilder::SetInputCrops(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[BatchToSpaceND] SetInputCrops failed, the Crops should be type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[BatchToSpaceND] SetInputCrops GetBuffer return nullptr.");
return OH_NN_INVALID_PARAMETER;
}
int64_t* pCropsData = static_cast<int64_t*>(buffer);
std::vector<std::vector<int64_t>> cropsData;
for (int i = 0; i < CROPS_ROWS; i++) {
std::vector<int64_t> vect_data;
vect_data.reserve(CROPS_COLUMN);
for (int j = 0; j < CROPS_COLUMN; j++) {
vect_data.push_back(*pCropsData++);
}
cropsData.push_back(vect_data);
}
m_crops = cropsData;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode BatchToSpaceNDBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex, const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
if (m_isBuild) {
LOGE("[BatchToSpaceND] Build failed, operation has been build, cannot build again.");
return OH_NN_OPERATION_FORBIDDEN;
}
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
if (returnCode != OH_NN_SUCCESS) {
LOGE("[BatchToSpaceND] Build failed, passed invalid input or output index.");
return returnCode;
}
for (int i : paramsIndex) {
std::shared_ptr<NNTensor> tensor = allTensors[i];
switch (tensor->GetType()) {
case OH_NN_BATCH_TO_SPACE_ND_BLOCKSIZE:
returnCode = SetInputBlock(tensor);
break;
case OH_NN_BATCH_TO_SPACE_ND_CROPS:
returnCode = SetInputCrops(tensor);
break;
default:
LOGE("[BatchToSpaceND] Build failed, param invalid, type = %d.", tensor->GetType());
return OH_NN_INVALID_PARAMETER;
}
if (returnCode != OH_NN_SUCCESS) {
LOGE("[BatchToSpaceND] Build failed, passed invalid param.");
return returnCode;
}
}
// The quantization type of the first output determinies that of the operator.
SetQuantType(outputsIndex, allTensors);
m_isBuild = true;
m_name = OP_NAME;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr BatchToSpaceNDBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[BatchToSpaceND] Cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
void* primitive = mindspore::lite::MindIR_BatchToSpaceND_CreatePrimitive(m_blockSize, m_crops);
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
return graphPrimitivePtr;
}
REGISTER_OPS(BatchToSpaceNDBuilder, OH_NN_OPS_BATCH_TO_SPACE_ND);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_BATCHTOSPACEND_BUILDER_H
#define NEURAL_NETWORK_BATCHTOSPACEND_BUILDER_H
#include "mindir.h"
#include "frameworks/native/ops_builder.h"
#include "frameworks/native/ops_registry.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class BatchToSpaceNDBuilder : public OpsBuilder {
public:
BatchToSpaceNDBuilder();
~BatchToSpaceNDBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
private:
OH_NN_ReturnCode SetBatchToSpaceInput(const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
OH_NN_ReturnCode SetInputBlock(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetInputCrops(std::shared_ptr<NNTensor> tensor);
private:
std::vector<int64_t> m_blockSize;
std::vector<std::vector<int64_t>> m_crops;
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_BATCHTOSPACEND_BUILDER_H
+116
View File
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "batchnorm_builder.h"
#include "mindir.h"
#include "frameworks/native/ops_registry.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static const int INPUT_NUM = 5;
static const int OUTPUT_NUM = 1;
static const int SCALAR_LENGTH = 1;
const std::string OP_NAME = "BatchNorm";
BatchNormBuilder::BatchNormBuilder() {}
BatchNormBuilder::~BatchNormBuilder() {}
OH_NN_ReturnCode BatchNormBuilder::SetEpsilon(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
if (tensor->GetDataType() != OH_NN_FLOAT32) {
LOGE("[BatchNorm] SetEpsilon failed, the Epsilon should be type OH_NN_FLOAT32.");
return OH_NN_INVALID_PARAMETER;
}
if (tensor->GetElementCount() != SCALAR_LENGTH) {
LOGE("[BatchNorm] SetEpsilon failed, the Epsilon shoule be a scalar");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[BatchNorm] SetEpsilon failed, the epsilon passed a empty buffer.");
return OH_NN_INVALID_PARAMETER;
}
m_epsilon = *static_cast<float*>(buffer);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode BatchNormBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
if (m_isBuild) {
LOGE("[BatchNorm] Build failed, batchNorm operation has been build, cannot build again.");
return OH_NN_OPERATION_FORBIDDEN;
}
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
if (returnCode != OH_NN_SUCCESS) {
LOGE("[BatchNorm] Build failed, passed invalid input or output index.");
return returnCode;
}
m_inputsIndex = inputsIndex;
m_outputsIndex = outputsIndex;
for (int i : paramsIndex) {
std::shared_ptr<NNTensor> tensor = allTensors[i];
switch (tensor->GetType()) {
case OH_NN_BATCH_NORM_EPSILON:
returnCode = SetEpsilon(tensor);
break;
default:
LOGE("[BatchNorm] Parameter Type is invalid, type=%d", tensor->GetType());
return OH_NN_INVALID_PARAMETER;
}
if (returnCode != OH_NN_SUCCESS) {
LOGE("[BatchNorm] BatchNorm Build failed,, Passed invalid param.");
return returnCode;
}
}
// The quantization type of the first output determinies that of the operator.
SetQuantType(outputsIndex, allTensors);
m_isBuild = true;
m_name = OP_NAME;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr BatchNormBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[BatchNorm] GetPrimitive failed, cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
void* primitive = mindspore::lite::MindIR_FusedBatchNorm_CreatePrimitive(m_epsilon);
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
return graphPrimitivePtr;
}
REGISTER_OPS(BatchNormBuilder, OH_NN_OPS_BATCH_NORM);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_BATHNORM_BUILDER_H
#define NEURAL_NETWORK_RUNTIME_BATHNORM_BUILDER_H
#include "frameworks/native/ops_builder.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class BatchNormBuilder : public OpsBuilder {
public:
BatchNormBuilder();
~BatchNormBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
private:
OH_NN_ReturnCode SetEpsilon(std::shared_ptr<NNTensor> tensor);
private:
float m_epsilon{1e-7};
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_BATHNORM_BUILDER_H
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bias_add_builder.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static const int INPUT_NUM = 2;
static const int OUTPUT_NUM = 1;
static const std::string OP_NAME = "BiasAdd";
BiasAddBuilder::BiasAddBuilder() {}
BiasAddBuilder::~BiasAddBuilder() {}
OH_NN_ReturnCode BiasAddBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
if (m_isBuild) {
LOGE("[BiasAdd] Build failed, biasAdd operation has been build, cannot build again.");
return OH_NN_OPERATION_FORBIDDEN;
}
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
if (returnCode != OH_NN_SUCCESS) {
LOGE("[BiasAdd] Build failed, passed invalid input or output index.");
return returnCode;
}
m_inputsIndex = inputsIndex;
m_outputsIndex = outputsIndex;
if (!paramsIndex.empty()) {
LOGE("[BiasAdd] Build failed, expects no parameters");
return OH_NN_INVALID_PARAMETER;
}
// The quantization type of the first output determinies that of the operator.
SetQuantType(outputsIndex, allTensors);
m_isBuild = true;
m_name = OP_NAME;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr BiasAddBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[BiasAdd] Build failed, cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
void* primitive = mindspore::lite::MindIR_BiasAdd_CreatePrimitive();
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
return graphPrimitivePtr;
}
REGISTER_OPS(BiasAddBuilder, OH_NN_OPS_BIAS_ADD);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_BIASADD_BUILDER_H
#define NEURAL_NETWORK_RUNTIME_BIASADD_BUILDER_H
#include "mindir.h"
#include "frameworks/native/ops_builder.h"
#include "frameworks/native/ops_registry.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class BiasAddBuilder : public OpsBuilder {
public:
BiasAddBuilder();
~BiasAddBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_BIASADD_BUILDER_H
+89
View File
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cast_builder.h"
#include "frameworks/native/transform.h"
#include "frameworks/native/validation.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static const int INPUT_NUM = 2;
static const int OUTPUT_NUM = 1;
static const int INPUT_TYPE = 1;
static const std::string OP_NAME = "Cast";
CastBuilder::CastBuilder() {}
CastBuilder::~CastBuilder() {}
OH_NN_ReturnCode CastBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
if (m_isBuild) {
LOGE("[Cast] Build failed, operation has been build, cannot build again.");
return OH_NN_OPERATION_FORBIDDEN;
}
auto ret = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
if (ret != OH_NN_SUCCESS) {
LOGE("[Cast] Build failed, the input or output index of Cast operation is invalid.");
return ret;
}
m_inputsIndex = inputsIndex;
m_outputsIndex = outputsIndex;
auto castType = allTensors[inputsIndex[INPUT_TYPE]]->GetBuffer();
if (castType == nullptr) {
LOGE("[Cast] Build castType GetBuffer return nullptr.");
return OH_NN_INVALID_PARAMETER;
}
OH_NN_DataType* castTypeInt = reinterpret_cast<OH_NN_DataType *>(castType);
if (!Validation::ValidateTensorDataType(*castTypeInt)) {
LOGE("[Cast] Type of cast operator is not validation.");
return OH_NN_INVALID_PARAMETER;
}
*castTypeInt = (OH_NN_DataType)NNToHDI::TransDataType(*castTypeInt);
if (!paramsIndex.empty()) {
LOGE("[Cast] Cast expects no parameters");
return OH_NN_INVALID_PARAMETER;
}
// The quantization type of the first output determinies that of the operator.
SetQuantType(outputsIndex, allTensors);
m_isBuild = true;
m_name = OP_NAME;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr CastBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[Cast] Cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
void* primitive = mindspore::lite::MindIR_Cast_CreatePrimitive();
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
return graphPrimitivePtr;
}
REGISTER_OPS(CastBuilder, OH_NN_OPS_CAST);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_CAST_BUILDER_H
#define NEURAL_NETWORK_RUNTIME_CAST_BUILDER_H
#include "mindir.h"
#include "frameworks/native/ops_builder.h"
#include "frameworks/native/ops_registry.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class CastBuilder : public OpsBuilder {
public:
CastBuilder();
~CastBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_CAST_BUILDER_H
+146
View File
@@ -0,0 +1,146 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "concat_builder.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static constexpr int MINIMUM_INTPUT = 2;
static constexpr int OUTPUT_NUM = 1;
static constexpr int AXIS_LENGTH = 1;
static const std::string OP_NAME = "Concat";
ConcatBuilder::ConcatBuilder() {}
ConcatBuilder::~ConcatBuilder() {}
OH_NN_ReturnCode ConcatBuilder::SetAxis(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
if (tensor->GetElementCount() != AXIS_LENGTH) {
LOGE("[Concat] SetAxis failed, the Activation shoule be a scalar");
return OH_NN_INVALID_PARAMETER;
}
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Concat] SetAxis failed, the axis should be type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Concat] SetAxis GetBuffer return nullptr.");
return OH_NN_INVALID_PARAMETER;
}
m_axis = *(static_cast<int64_t*>(buffer));
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode ConcatBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex, const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
if (m_isBuild) {
LOGE("[Concat] Build failed, operation has been build, cannot build again.");
return OH_NN_OPERATION_FORBIDDEN;
}
if (inputsIndex.size() < MINIMUM_INTPUT) {
LOGE("[Concat] Build failed, Concat need more than one inputs.");
return OH_NN_INVALID_PARAMETER;
}
if (outputsIndex.size() != OUTPUT_NUM) {
LOGE("[Concat] Build failed, The number of index of outputs not equal to 1.");
return OH_NN_INVALID_PARAMETER;
}
OH_NN_ReturnCode returnCode = SetInputsAndOutputs(inputsIndex, outputsIndex, allTensors);
if (returnCode != OH_NN_SUCCESS) {
LOGE("[Concat] Build failed, set inputs or outputs failed.");
return returnCode;
}
for (int i : paramsIndex) {
std::shared_ptr<NNTensor> tensor = allTensors[i];
switch (tensor->GetType()) {
case OH_NN_CONCAT_AXIS:
returnCode = SetAxis(tensor);
break;
default:
LOGE("[Concat] Build failed, param invalid, type = %d.", tensor->GetType());
return OH_NN_INVALID_PARAMETER;
}
if (returnCode != OH_NN_SUCCESS) {
LOGE("[Concat] Build failed, passed invalid param.");
return returnCode;
}
}
// The quantization type of the first output determinies that of the operator.
SetQuantType(outputsIndex, allTensors);
m_isBuild = true;
m_name = OP_NAME;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode ConcatBuilder::SetInputsAndOutputs(const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
size_t allTensorsSize = allTensors.size();
for (auto index : inputsIndex) {
if (index >= allTensorsSize) {
LOGE("[Concat] Invalid input index, it is out of range %zu.", allTensorsSize);
return OH_NN_INVALID_PARAMETER;
}
}
for (auto index : outputsIndex) {
if (index >= allTensorsSize) {
LOGE("[Concat] Invalid output index, it is out of range %zu.", allTensorsSize);
return OH_NN_INVALID_PARAMETER;
}
}
m_inputsIndex.clear();
m_inputsIndex = inputsIndex;
m_outputsIndex.clear();
m_outputsIndex = outputsIndex;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr ConcatBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[Concat] GetPrimitive failed, cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
void* primitive = mindspore::lite::MindIR_Concat_CreatePrimitive(m_axis);
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
return graphPrimitivePtr;
}
REGISTER_OPS(ConcatBuilder, OH_NN_OPS_CONCAT);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_CONCAT_BUILDER_H
#define NEURAL_NETWORK_RUNTIME_CONCAT_BUILDER_H
#include "mindir.h"
#include "frameworks/native/ops_builder.h"
#include "frameworks/native/ops_registry.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class ConcatBuilder : public OpsBuilder {
public:
ConcatBuilder();
~ConcatBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
private:
OH_NN_ReturnCode SetAxis(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetInputsAndOutputs(const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
private:
int64_t m_axis{0};
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_CONCAT_BUILDER_H
+298
View File
@@ -0,0 +1,298 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "conv2d_builder.h"
#include "frameworks/native/transform.h"
#include "frameworks/native/validation.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static constexpr int INPUT_NUM = 3;
static constexpr int OUTPUT_NUM = 1;
static constexpr int CONV2D_INPUT_WEIGHT = 1;
static constexpr int WEIGHT_SIZE = 4;
static constexpr int OUT_CHANNEL_INDEX = 0;
static constexpr int IN_CHANNEL_INDEX = 3;
static constexpr int KERNEL_HEIGHT_INDEX = 1;
static constexpr int KERNEL_WEIGHT_INDEX = 2;
static constexpr int PAD_MODE_GET = 1;
static constexpr int PAD_LIST_GET = 4;
static constexpr int SCALAR_LENGTH = 1;
static const std::string OP_NAME = "Conv2D";
Conv2DBuilder::Conv2DBuilder() {}
Conv2DBuilder::~Conv2DBuilder() {}
OH_NN_ReturnCode Conv2DBuilder::SetInputAndOutput(const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
if (returnCode != OH_NN_SUCCESS) {
LOGE("[Conv2d] SetInputAndOutput failed, passed invalid input or output index.");
return returnCode;
}
m_inputsIndex = inputsIndex;
m_outputsIndex = outputsIndex;
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DBuilder::SetChannel(const std::vector<uint32_t>& inputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
// set inChannel, outChannel, kernelSize
auto weightShape = allTensors[inputsIndex[CONV2D_INPUT_WEIGHT]]->GetDimensions();
if (weightShape.size() != WEIGHT_SIZE) {
LOGE("[Conv2d] SetChannel failed, the dimension of weight should be %d", WEIGHT_SIZE);
return OH_NN_INVALID_PARAMETER;
}
m_inChannel = weightShape[IN_CHANNEL_INDEX];
m_outChannel = weightShape[OUT_CHANNEL_INDEX];
return OH_NN_SUCCESS;
}
void Conv2DBuilder::SetKernelSize(const std::vector<uint32_t>& inputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
// set inChannel, outChannel, kernelSize
auto weightShape = allTensors[inputsIndex[CONV2D_INPUT_WEIGHT]]->GetDimensions();
m_kernelSize.clear();
m_kernelSize.emplace_back(weightShape[KERNEL_HEIGHT_INDEX]);
m_kernelSize.emplace_back(weightShape[KERNEL_WEIGHT_INDEX]);
}
OH_NN_ReturnCode Conv2DBuilder::SetStrides(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
// Set Strides
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Conv2d] SetStrides failed, the Strides should be type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2d] SetStrides GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
const int64_t* pStrides = reinterpret_cast<const int64_t*>(buffer);
int stridesSize = tensor->GetElementCount();
m_strides.assign(pStrides, pStrides + stridesSize);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DBuilder::SetDilation(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
// Set Dilation
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Conv2d] SetDilation failed, the Dilation should have type OH_NN_INT64");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2d] SetDilation GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
const int64_t* pDilation = reinterpret_cast<const int64_t*>(buffer);
int dilationSize = tensor->GetElementCount();
m_dilation.assign(pDilation, pDilation + dilationSize);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DBuilder::SetPad(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
bool isPadMode = false;
if (tensor->GetElementCount() == PAD_MODE_GET) {
isPadMode = true;
} else if (tensor->GetElementCount() != PAD_LIST_GET) {
LOGE("[Conv2d] SetPad failed, inputs should be 1 for padMode and 4 for padList.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2d] SetPadList GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
// Set PadMode or PadList
if (isPadMode) {
if (tensor->GetDataType() != OH_NN_INT8) {
LOGE("[Conv2d] SetPad failed, the PadMode should have type OH_NN_INT8.");
return OH_NN_INVALID_PARAMETER;
}
int8_t* pPad = static_cast<int8_t*>(buffer);
if (!OHOS::NeuralNetworkRuntime::Validation::ValidatePadMode(*pPad)) {
LOGE("[Conv2d] SetPad failed, invalid pad mode.");
return OH_NN_INVALID_PARAMETER;
}
m_padMode = NNToMS::TransformPadModeValue(*pPad);
} else {
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Conv2d] SetPad failed, the PadList should have type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
int64_t* pPadList = static_cast<int64_t*>(buffer);
int padListSize = tensor->GetElementCount();
m_pad.assign(pPadList, pPadList + padListSize);
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DBuilder::SetGroup(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
// Set Group
if (tensor->GetElementCount() != SCALAR_LENGTH) {
LOGE("[Conv2d] SetGroup failed, The Group shoule be a scalar");
return OH_NN_INVALID_PARAMETER;
}
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Conv2d] SetGroup failed, The Group should have type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2d] SetGroup GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
m_group = *static_cast<int64_t*>(buffer);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DBuilder::SetActavitation(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
if (tensor->GetElementCount() != SCALAR_LENGTH) {
LOGE("[Conv2d] SetActavitation failed, the ActivationType shoule be a scalar");
return OH_NN_INVALID_PARAMETER;
}
if (tensor->GetDataType() != OH_NN_INT8) {
LOGE("[Conv2d] SetActavitation failed, the ActivationType should have type OH_NN_INT8.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2d] SetGroup GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
int8_t* pFuseData = static_cast<int8_t*>(buffer);
if (!OHOS::NeuralNetworkRuntime::Validation::ValidateFuseType(static_cast<OH_NN_FuseType>(*pFuseData))) {
LOGE("[Conv2d] SetActavitation failed, activation input is invalid.");
return OH_NN_INVALID_PARAMETER;
}
m_activationType = NNToMS::TransfromFusionType((OH_NN_FuseType)(*pFuseData));
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex, const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
if (m_isBuild) {
LOGE("[Conv2d] Build failed, Conv2D operation has been build, cannot build again.");
return OH_NN_OPERATION_FORBIDDEN;
}
OH_NN_ReturnCode returnCode = SetInputAndOutput(inputsIndex, outputsIndex, allTensors);
if (returnCode != OH_NN_SUCCESS) {
return returnCode;
}
returnCode = SetChannel(inputsIndex, allTensors);
if (returnCode != OH_NN_SUCCESS) {
return returnCode;
}
SetKernelSize(inputsIndex, allTensors);
for (int i : paramsIndex) {
std::shared_ptr<NNTensor> tensor = allTensors[i];
switch (tensor->GetType()) {
case OH_NN_CONV2D_STRIDES:
returnCode = SetStrides(tensor);
break;
case OH_NN_CONV2D_DILATION:
returnCode = SetDilation(tensor);
break;
case OH_NN_CONV2D_PAD_MODE:
case OH_NN_CONV2D_PAD:
returnCode = SetPad(tensor);
break;
case OH_NN_CONV2D_GROUP:
returnCode = SetGroup(tensor);
break;
case OH_NN_CONV2D_ACTIVATION_TYPE:
returnCode = SetActavitation(tensor);
break;
default:
LOGE("[Conv2D] Build failed, param invalid, type = %d.", tensor->GetType());
return OH_NN_INVALID_PARAMETER;
}
if (returnCode != OH_NN_SUCCESS) {
LOGE("[Conv2D] Build failed, Passed invalid param.");
return returnCode;
}
}
// The quantization type of the first output determinies that of the operator.
SetQuantType(outputsIndex, allTensors);
m_isBuild = true;
m_name = OP_NAME;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr Conv2DBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[Conv2d] GetPrimitive failed, Cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
auto primitive = MindIR_Conv2DFusion_CreatePrimitive(m_kernelSize, m_strides,
m_dilation, m_padMode, m_pad, m_group, m_inChannel, m_outChannel, m_activationType);
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
return graphPrimitivePtr;
}
REGISTER_OPS(Conv2DBuilder, OH_NN_OPS_CONV2D);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
+65
View File
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_CONV2D_BUILDER_H
#define NEURAL_NETWORK_RUNTIME_CONV2D_BUILDER_H
#include "frameworks/native/ops_builder.h"
#include "frameworks/native/ops_registry.h"
#include "mindir.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class Conv2DBuilder : public OpsBuilder {
public:
Conv2DBuilder();
~Conv2DBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
private:
OH_NN_ReturnCode SetInputAndOutput(const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
OH_NN_ReturnCode SetChannel(const std::vector<uint32_t>& inputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
void SetKernelSize(const std::vector<uint32_t>& inputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
OH_NN_ReturnCode SetStrides(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetDilation(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetPad(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetGroup(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetActavitation(std::shared_ptr<NNTensor> tensor);
private:
int64_t m_group{1};
int64_t m_inChannel{0};
int64_t m_outChannel{0};
std::vector<int64_t> m_kernelSize;
std::vector<int64_t> m_strides;
std::vector<int64_t> m_pad;
std::vector<int64_t> m_dilation;
mindspore::lite::PadMode m_padMode{mindspore::lite::PAD_MODE_PAD};
mindspore::lite::ActivationType m_activationType{mindspore::lite::ACTIVATION_TYPE_NO_ACTIVATION};
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_CONV2D_BUILDER_H
@@ -0,0 +1,313 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "conv2d_transpose_builder.h"
#include "frameworks/native/transform.h"
#include "frameworks/native/validation.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
static constexpr int INPUT_NUM = 3;
static constexpr int OUTPUT_NUM = 1;
static constexpr int INPUT_WEIGHT = 1;
static constexpr int WEIGHT_SIZE = 4;
static constexpr int OUT_CHANNEL_INDEX = 0;
static constexpr int IN_CHANNEL_INDEX = 3;
static constexpr int KERNEL_HEIGHT_INDEX = 1;
static constexpr int KERNEL_WEIGHT_INDEX = 2;
static constexpr int PAD_MODE_PARAM_NUM = 1;
static constexpr int PAD_LIST_PARAM_NUM = 4;
static constexpr int SCALAR_LENGTH = 1;
static const std::string OP_NAME = "Conv2DTranspose";
Conv2DTransposeBuilder::Conv2DTransposeBuilder() {}
Conv2DTransposeBuilder::~Conv2DTransposeBuilder() {}
OH_NN_ReturnCode Conv2DTransposeBuilder::SetInput(const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
if (returnCode != OH_NN_SUCCESS) {
LOGE("[Conv2dTranspose] SetInput failed, Passed invalid input or output index.");
return returnCode;
}
m_inputsIndex = inputsIndex;
m_outputsIndex = outputsIndex;
// set inChannel, outChannel, kernelSize
auto weightShape = allTensors[inputsIndex[INPUT_WEIGHT]]->GetDimensions();
if (weightShape.size() != WEIGHT_SIZE) {
LOGE("[Conv2dTranspose] SetInput failed, the dimension of weight should be %d", WEIGHT_SIZE);
return OH_NN_INVALID_PARAMETER;
}
m_inChannel = weightShape[IN_CHANNEL_INDEX];
m_outChannel = weightShape[OUT_CHANNEL_INDEX];
return OH_NN_SUCCESS;
}
void Conv2DTransposeBuilder::SetKernelSize(const std::vector<uint32_t>& inputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
auto weightShape = allTensors[inputsIndex[INPUT_WEIGHT]]->GetDimensions();
m_kernelSize.clear();
m_kernelSize.emplace_back(weightShape[KERNEL_HEIGHT_INDEX]);
m_kernelSize.emplace_back(weightShape[KERNEL_WEIGHT_INDEX]);
}
OH_NN_ReturnCode Conv2DTransposeBuilder::SetStrides(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
// Set Strides
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Conv2DTranspose] SetStrides failed, the Strides should be type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2DTranspose] SetStrides GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
const int64_t* pStrides = reinterpret_cast<const int64_t*>(buffer);
int elementSize = tensor->GetElementCount();
m_strides.assign(pStrides, pStrides + elementSize);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DTransposeBuilder::SetDilation(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
// Set Dilation
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Conv2DTranspose] SetDilation failed, the Dilation should be type OH_NN_INT64");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2DTranspose] SetDilation GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
const int64_t* pDilation = reinterpret_cast<const int64_t*>(buffer);
int dilationSize = tensor->GetElementCount();
m_dilation.assign(pDilation, pDilation + dilationSize);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DTransposeBuilder::SetPad(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
bool isPadMode = false;
if (tensor->GetElementCount() == PAD_MODE_PARAM_NUM) {
isPadMode = true;
} else if (tensor->GetElementCount() != PAD_LIST_PARAM_NUM) {
LOGE("[Conv2DTranspose] SetPad failed, the inputs should be 1 if using padMode or 4 if using padList.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2DTranspose] SetPadMode GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
// Set PadMode or PadList
if (isPadMode) {
if (tensor->GetDataType() != OH_NN_INT8) {
LOGE("[Conv2DTranspose] SetPad failed, the PadMode should have type OH_NN_INT8.");
return OH_NN_INVALID_PARAMETER;
}
int8_t* pPad = static_cast<int8_t*>(buffer);
if (!OHOS::NeuralNetworkRuntime::Validation::ValidatePadMode(*pPad)) {
LOGE("[Conv2DTranspose] SetPad failed, invalid pad mode.");
return OH_NN_INVALID_PARAMETER;
}
m_padMode = NNToMS::TransformPadModeValue(*pPad);
} else {
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Conv2DTranspose] SetPad failed, the PadList should have type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
const int64_t* pPadList = reinterpret_cast<const int64_t*>(buffer);
int padListPadSize = tensor->GetElementCount();
m_padList.assign(pPadList, pPadList + padListPadSize);
}
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DTransposeBuilder::SetGroup(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
// Set Group
if (tensor->GetElementCount() != SCALAR_LENGTH) {
LOGE("[Conv2dTranspose] SetGroup failed, the Group shoule be a scalar");
return OH_NN_INVALID_PARAMETER;
}
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Conv2dTranspose] SetGroup failed, the Group should have type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2DTranspose] SetGroup GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
m_group = *reinterpret_cast<const int64_t*>(buffer);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DTransposeBuilder::SetOutPadding(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
// Set outputPadding
if (tensor->GetDataType() != OH_NN_INT64) {
LOGE("[Conv2DTranspose] SetOutPadding failed, the outputPadding should be type OH_NN_INT64.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2DTranspose] SetOutPadding GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
const int64_t* pOutputPadding = reinterpret_cast<const int64_t*>(buffer);
int outputPadSize = tensor->GetElementCount();
m_outputPaddings.assign(pOutputPadding, pOutputPadding + outputPadSize);
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DTransposeBuilder::SetActivation(std::shared_ptr<NNTensor> tensor)
{
tensor->IdentifyOpParameter();
if (tensor->GetElementCount() != SCALAR_LENGTH) {
LOGE("[Conv2DTranspose] SetActivation failed, the ActivationType shoule be a scalar");
return OH_NN_INVALID_PARAMETER;
}
if (tensor->GetDataType() != OH_NN_INT8) {
LOGE("[Conv2DTranspose] SetActivation failed, the ActivationType should have type OH_NN_INT8.");
return OH_NN_INVALID_PARAMETER;
}
void* buffer = tensor->GetBuffer();
if (buffer == nullptr) {
LOGE("[Conv2DTranspose] SetOutPadding GetBuffer return nullptr");
return OH_NN_INVALID_PARAMETER;
}
int8_t* pFuseData = static_cast<int8_t*>(buffer);
if (!OHOS::NeuralNetworkRuntime::Validation::ValidateFuseType(static_cast<OH_NN_FuseType>(*pFuseData))) {
LOGE("[Conv2DTranspose] SetActivation failed, activation input is invalid.");
return OH_NN_INVALID_PARAMETER;
}
m_activationType = NNToMS::TransfromFusionType((OH_NN_FuseType)(*pFuseData));
return OH_NN_SUCCESS;
}
OH_NN_ReturnCode Conv2DTransposeBuilder::Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
{
if (m_isBuild) {
LOGE("[Conv2DTranspose] Build failed, conv2DTranspose operation has been build, cannot build again.");
return OH_NN_OPERATION_FORBIDDEN;
}
OH_NN_ReturnCode returnCode = SetInput(inputsIndex, outputsIndex, allTensors);
if (returnCode != OH_NN_SUCCESS) {
return returnCode;
}
SetKernelSize(inputsIndex, allTensors);
for (int i : paramsIndex) {
std::shared_ptr<NNTensor> tensor = allTensors[i]; // 参数 tensor
switch (tensor->GetType()) {
case OH_NN_CONV2D_TRANSPOSE_STRIDES:
returnCode = SetStrides(tensor);
break;
case OH_NN_CONV2D_TRANSPOSE_DILATION:
returnCode = SetDilation(tensor);
break;
case OH_NN_CONV2D_TRANSPOSE_PAD_MODE:
case OH_NN_CONV2D_TRANSPOSE_PAD:
returnCode = SetPad(tensor);
break;
case OH_NN_CONV2D_TRANSPOSE_GROUP:
returnCode = SetGroup(tensor);
break;
case OH_NN_CONV2D_TRANSPOSE_OUTPUT_PADDINGS:
returnCode = SetOutPadding(tensor);
break;
case OH_NN_CONV2D_TRANSPOSE_ACTIVATION_TYPE:
returnCode = SetActivation(tensor);
break;
default:
LOGE("[Conv2DTranspose] Build failed, param invalid, type = %d.", tensor->GetType());
return OH_NN_INVALID_PARAMETER;
}
if (returnCode != OH_NN_SUCCESS) {
LOGE("[Conv2DTranspose] Build failed, passed invalid param.");
return returnCode;
}
}
// The quantization type of the first output determinies that of the operator.
SetQuantType(outputsIndex, allTensors);
m_isBuild = true;
m_name = OP_NAME;
return OH_NN_SUCCESS;
}
LiteGraphPrimitvePtr Conv2DTransposeBuilder::GetPrimitive()
{
if (!m_isBuild) {
LOGE("[Conv2DTranspose] GetPrimitive failed, cannot get primitive before call build.");
return {nullptr, DestroyLiteGraphPrimitive};
}
void* primitive = MindIR_Conv2dTransposeFusion_CreatePrimitive(m_kernelSize,
m_strides, m_dilation, m_padMode, m_padList, m_group, m_inChannel, m_outChannel,
m_activationType, m_outputPaddings);
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
return graphPrimitivePtr;
}
REGISTER_OPS(Conv2DTransposeBuilder, OH_NN_OPS_CONV2D_TRANSPOSE);
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NEURAL_NETWORK_RUNTIME_CONV2DTRANSPOSE_BUILDER_H
#define NEURAL_NETWORK_RUNTIME_CONV2DTRANSPOSE_BUILDER_H
#include "frameworks/native/ops_builder.h"
#include "frameworks/native/ops_registry.h"
#include "mindir.h"
namespace OHOS {
namespace NeuralNetworkRuntime {
namespace Ops {
class Conv2DTransposeBuilder : public OpsBuilder {
public:
Conv2DTransposeBuilder();
~Conv2DTransposeBuilder() override;
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
LiteGraphPrimitvePtr GetPrimitive() override;
private:
OH_NN_ReturnCode SetInput(const std::vector<uint32_t>& inputsIndex,
const std::vector<uint32_t>& outputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
void SetKernelSize(const std::vector<uint32_t>& inputsIndex,
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
OH_NN_ReturnCode SetStrides(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetDilation(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetPad(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetGroup(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetOutPadding(std::shared_ptr<NNTensor> tensor);
OH_NN_ReturnCode SetActivation(std::shared_ptr<NNTensor> tensor);
private:
int64_t m_group{1};
int64_t m_inChannel{0};
int64_t m_outChannel{0};
std::vector<int64_t> m_kernelSize;
std::vector<int64_t> m_strides;
std::vector<int64_t> m_padList;
std::vector<int64_t> m_dilation;
std::vector<int64_t> m_outputPaddings;
mindspore::lite::PadMode m_padMode{mindspore::lite::PAD_MODE_PAD};
mindspore::lite::ActivationType m_activationType{mindspore::lite::ACTIVATION_TYPE_NO_ACTIVATION};
};
} // namespace Ops
} // namespace NeuralNetworkRuntime
} // namespace OHOS
#endif // NEURAL_NETWORK_RUNTIME_CONV2DTRANSPOSE_BUILDER_H

Some files were not shown because too many files have changed in this diff Show More