mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-29 20:14:01 +00:00
Add ArmNN Execution Provider (#3714)
* Add ArmNN Execution Provider Add a new execution provider targeting Arm architecture based on ArmNN. Validated on NXP i.MX8QM CPU with ResNet50, MobileNetv2 and VGG models. reviewed-by: mike.caraman@nxp.com * Minor fixes - renamed onnxruntime_ARMNN_RELU_USECPU to onnxruntime_ARMNN_RELU_USE_CPU - fixed acl typo * remove extra includes. added exception for ArmNN in test * fix indentation * Separated the activation implementation from the cpu and fixed the blockage from the endif Co-authored-by: Andrei-Alexandru <andrei-alexandru.avram@nxp.com>
This commit is contained in:
parent
62af8da3f6
commit
62b44527e5
33 changed files with 1530 additions and 6 deletions
26
BUILD.md
26
BUILD.md
|
|
@ -19,6 +19,7 @@
|
|||
* [Nuphar Model Compiler](#Nuphar)
|
||||
* [DirectML](#DirectML)
|
||||
* [ARM Compute Library](#ARM-Compute-Library)
|
||||
* [ArmNN](#ArmNN)
|
||||
* [Rockchip RKNPU](#RKNPU)
|
||||
* [Xilinx Vitis-AI](#Vitis-AI)
|
||||
* Options
|
||||
|
|
@ -491,6 +492,31 @@ export LD_LIBRARY_PATH=~/ComputeLibrary/build/
|
|||
|
||||
---
|
||||
|
||||
### ArmNN
|
||||
See more information on the ArmNN Execution Provider [here](./docs/execution_providers/ArmNN-ExecutionProvider.md).
|
||||
|
||||
#### Prerequisites
|
||||
* Supported backend: i.MX8QM Armv8 CPUs
|
||||
* Supported BSP: i.MX8QM BSP
|
||||
* Install i.MX8QM BSP: `source fsl-imx-xwayland-glibc-x86_64-fsl-image-qt5-aarch64-toolchain-4*.sh`
|
||||
* Set up the build environment
|
||||
```
|
||||
source /opt/fsl-imx-xwayland/4.*/environment-setup-aarch64-poky-linux
|
||||
alias cmake="/usr/bin/cmake -DCMAKE_TOOLCHAIN_FILE=$OECORE_NATIVE_SYSROOT/usr/share/cmake/OEToolchainConfig.cmake"
|
||||
```
|
||||
* See [Build ARM](#ARM) below for information on building for ARM devices
|
||||
|
||||
#### Build Instructions
|
||||
```
|
||||
./build.sh --use_armnn
|
||||
```
|
||||
The Relu operator is set by default to use the CPU execution provider for better performance. To use the ArmNN implementation build with --armnn_relu flag
|
||||
```
|
||||
./build.sh --use_armnn --armnn_relu
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### RKNPU
|
||||
See more information on the RKNPU Execution Provider [here](./docs/execution_providers/RKNPU-ExecutionProvider.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ option(onnxruntime_USE_ACL "Build with ACL support" OFF)
|
|||
option(onnxruntime_USE_ACL_1902 "Build with ACL version 1902 support" OFF)
|
||||
option(onnxruntime_USE_ACL_1905 "Build with ACL version 1905 support" OFF)
|
||||
option(onnxruntime_USE_ACL_1908 "Build with ACL version 1908 support" OFF)
|
||||
option(onnxruntime_USE_ARMNN "Build with ArmNN support" OFF)
|
||||
option(onnxruntime_ARMNN_RELU_USE_CPU "Use the CPU implementation for the Relu operator for the ArmNN EP" ON)
|
||||
option(onnxruntime_ENABLE_INSTRUMENT "Enable Instrument with Event Tracing for Windows (ETW)" OFF)
|
||||
option(onnxruntime_USE_TELEMETRY "Build with Telemetry" OFF)
|
||||
#The onnxruntime_PREFER_SYSTEM_LIB is mainly designed for package managers like apt/yum/vcpkg.
|
||||
|
|
@ -513,6 +515,15 @@ if (onnxruntime_USE_ACL OR onnxruntime_USE_ACL_1902 OR onnxruntime_USE_ACL_1905
|
|||
list(APPEND onnxruntime_EXTERNAL_LIBRARIES arm_compute arm_compute_graph arm_compute_core)
|
||||
endif()
|
||||
|
||||
# ArmNN
|
||||
if (onnxruntime_USE_ARMNN)
|
||||
if (NOT onnxruntime_ARMNN_RELU_USE_CPU)
|
||||
add_definitions(-DRELU_ARMNN=1)
|
||||
endif()
|
||||
|
||||
list(APPEND onnxruntime_EXTERNAL_LIBRARIES armnn pthread arm_compute_core arm_compute arm_compute_graph)
|
||||
endif()
|
||||
|
||||
# MKLML
|
||||
if (onnxruntime_USE_DNNL OR onnxruntime_USE_MKLML)
|
||||
include(dnnl)
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ target_link_libraries(onnxruntime PRIVATE
|
|||
${PROVIDERS_VITISAI}
|
||||
${PROVIDERS_DML}
|
||||
${PROVIDERS_ACL}
|
||||
${PROVIDERS_ARMNN}
|
||||
${onnxruntime_winml}
|
||||
onnxruntime_optimizer
|
||||
onnxruntime_providers
|
||||
|
|
|
|||
|
|
@ -83,6 +83,10 @@ if(onnxruntime_USE_ACL)
|
|||
set(PROVIDERS_ACL onnxruntime_providers_acl)
|
||||
list(APPEND ONNXRUNTIME_PROVIDER_NAMES acl)
|
||||
endif()
|
||||
if(onnxruntime_USE_ARMNN)
|
||||
set(PROVIDERS_ARMNN onnxruntime_providers_armnn)
|
||||
list(APPEND ONNXRUNTIME_PROVIDER_NAMES armnn)
|
||||
endif()
|
||||
source_group(TREE ${ONNXRUNTIME_ROOT}/core FILES ${onnxruntime_providers_common_srcs} ${onnxruntime_providers_srcs})
|
||||
|
||||
set(onnxruntime_providers_src ${onnxruntime_providers_common_srcs} ${onnxruntime_providers_srcs})
|
||||
|
|
@ -654,3 +658,20 @@ if (onnxruntime_USE_ACL)
|
|||
install(DIRECTORY ${PROJECT_SOURCE_DIR}/../include/onnxruntime/core/providers/acl DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/onnxruntime/core/providers)
|
||||
set_target_properties(onnxruntime_providers_acl PROPERTIES LINKER_LANGUAGE CXX)
|
||||
endif()
|
||||
|
||||
if (onnxruntime_USE_ARMNN)
|
||||
add_definitions(-DUSE_ARMNN=1)
|
||||
file(GLOB_RECURSE onnxruntime_providers_armnn_cc_srcs
|
||||
"${ONNXRUNTIME_ROOT}/core/providers/armnn/*.h"
|
||||
"${ONNXRUNTIME_ROOT}/core/providers/armnn/*.cc"
|
||||
)
|
||||
|
||||
source_group(TREE ${ONNXRUNTIME_ROOT}/core FILES ${onnxruntime_providers_armnn_cc_srcs})
|
||||
add_library(onnxruntime_providers_armnn ${onnxruntime_providers_armnn_cc_srcs})
|
||||
onnxruntime_add_include_to_target(onnxruntime_providers_armnn onnxruntime_common onnxruntime_framework onnx onnx_proto protobuf::libprotobuf)
|
||||
add_dependencies(onnxruntime_providers_armnn ${onnxruntime_EXTERNAL_DEPENDENCIES})
|
||||
set_target_properties(onnxruntime_providers_armnn PROPERTIES FOLDER "ONNXRuntime")
|
||||
target_include_directories(onnxruntime_providers_armnn PRIVATE ${ONNXRUNTIME_ROOT} ${eigen_INCLUDE_DIRS} ${ARMNN_INCLUDE_DIR})
|
||||
install(DIRECTORY ${PROJECT_SOURCE_DIR}/../include/onnxruntime/core/providers/armnn DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/onnxruntime/core/providers)
|
||||
set_target_properties(onnxruntime_providers_armnn PROPERTIES LINKER_LANGUAGE CXX)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -336,6 +336,10 @@ if(onnxruntime_USE_ACL)
|
|||
list(APPEND onnxruntime_test_providers_dependencies onnxruntime_providers_acl)
|
||||
endif()
|
||||
|
||||
if(onnxruntime_USE_ARMNN)
|
||||
list(APPEND onnxruntime_test_providers_dependencies onnxruntime_providers_armnn)
|
||||
endif()
|
||||
|
||||
if (onnxruntime_ENABLE_LANGUAGE_INTEROP_OPS)
|
||||
set(ONNXRUNTIME_INTEROP_TEST_LIBS PRIVATE onnxruntime_language_interop onnxruntime_pyop)
|
||||
endif()
|
||||
|
|
@ -355,6 +359,7 @@ set(ONNXRUNTIME_TEST_LIBS
|
|||
${PROVIDERS_RKNPU}
|
||||
${PROVIDERS_DML}
|
||||
${PROVIDERS_ACL}
|
||||
${PROVIDERS_ARMNN}
|
||||
onnxruntime_optimizer
|
||||
onnxruntime_providers
|
||||
onnxruntime_util
|
||||
|
|
|
|||
29
docs/execution_providers/ArmNN-ExecutionProvider.md
Normal file
29
docs/execution_providers/ArmNN-ExecutionProvider.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
## ArmNN Execution Provider
|
||||
|
||||
[ArmNN](https://github.com/ARM-software/armnn) is an open source inference engine maintained by Arm and Linaro companies. The integration of ArmNN as an execution provider (EP) into ONNX Runtime accelerates performance of ONNX model workloads across Armv8 cores.
|
||||
|
||||
### Build ArmNN execution provider
|
||||
For build instructions, please see the [BUILD page](../../BUILD.md#ArmNN).
|
||||
|
||||
### Using the ArmNN execution provider
|
||||
#### C/C++
|
||||
To use ArmNN as execution provider for inferencing, please register it as below.
|
||||
```
|
||||
string log_id = "Foo";
|
||||
auto logging_manager = std::make_unique<LoggingManager>
|
||||
(std::unique_ptr<ISink>{new CLogSink{}},
|
||||
static_cast<Severity>(lm_info.default_warning_level),
|
||||
false,
|
||||
LoggingManager::InstanceType::Default,
|
||||
&log_id)
|
||||
Environment::Create(std::move(logging_manager), env)
|
||||
InferenceSession session_object{so, env};
|
||||
session_object.RegisterExecutionProvider(std::make_unique<::onnxruntime::ArmNNExecutionProvider>());
|
||||
status = session_object.Load(model_file_name);
|
||||
```
|
||||
The C API details are [here](../C_API.md#c-api).
|
||||
|
||||
### Performance Tuning
|
||||
For performance tuning, please see guidance on this page: [ONNX Runtime Perf Tuning](../ONNX_Runtime_Perf_Tuning.md)
|
||||
|
||||
When/if using [onnxruntime_perf_test](../../onnxruntime/test/perftest), use the flag -e armnn
|
||||
|
|
@ -37,4 +37,5 @@ constexpr const char* kRknpuExecutionProvider = "RknpuExecutionProvider";
|
|||
constexpr const char* kDmlExecutionProvider = "DmlExecutionProvider";
|
||||
constexpr const char* kMIGraphXExecutionProvider = "MIGraphXExecutionProvider";
|
||||
constexpr const char* kAclExecutionProvider = "ACLExecutionProvider";
|
||||
constexpr const char* kArmNNExecutionProvider = "ArmNNExecutionProvider";
|
||||
} // namespace onnxruntime
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "onnxruntime_c_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \param use_arena zero: false. non-zero: true.
|
||||
*/
|
||||
ORT_API_STATUS(OrtSessionOptionsAppendExecutionProvider_ArmNN, _In_ OrtSessionOptions* options, int use_arena)
|
||||
ORT_ALL_ARGS_NONNULL;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -76,7 +76,8 @@ common::Status MemcpyTransformer::ApplyImpl(Graph& graph, bool& modified, int gr
|
|||
provider != onnxruntime::kVitisAIExecutionProvider &&
|
||||
provider != onnxruntime::kOpenVINOExecutionProvider &&
|
||||
provider != onnxruntime::kNnapiExecutionProvider &&
|
||||
provider != onnxruntime::kAclExecutionProvider) {
|
||||
provider != onnxruntime::kAclExecutionProvider &&
|
||||
provider != onnxruntime::kArmNNExecutionProvider) {
|
||||
TransformerMemcpyImpl copy_impl(graph, provider);
|
||||
auto current_modified = copy_impl.ModifyGraph(registry_manager_);
|
||||
modified = modified || current_modified;
|
||||
|
|
|
|||
100
onnxruntime/core/providers/armnn/activation/activations.cc
Normal file
100
onnxruntime/core/providers/armnn/activation/activations.cc
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License
|
||||
|
||||
#ifdef RELU_ARMNN
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
|
||||
#include "core/providers/armnn/armnn_common.h"
|
||||
#include "core/providers/armnn/activation/activations.h"
|
||||
#include "core/providers/armnn/armnn_fwd.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
|
||||
template <typename T>
|
||||
thread_local std::map<OpKernel*, armnn::NetworkId> Relu<T>::reluLayers;
|
||||
|
||||
template <typename T>
|
||||
armnn::IRuntimePtr Relu<T>::run = Relu<T>::initRuntime();
|
||||
|
||||
template <typename T>
|
||||
Status Relu<T>::Compute(OpKernelContext* context) const {
|
||||
|
||||
const Tensor* X = context->Input<Tensor>(0);
|
||||
Tensor* Y = context->Output(0, X->Shape());
|
||||
|
||||
const T* src_data = X->template Data<T>();
|
||||
T* dst_data = Y->template MutableData<T>();
|
||||
|
||||
armnn::NetworkId* pNetworkId;
|
||||
ReluLayersIterator it = Relu::reluLayers.find((OpKernel*)this);
|
||||
if (it == Relu::reluLayers.end()) {
|
||||
|
||||
armnn::NetworkId networkId;
|
||||
armnn::INetworkPtr myNetwork = armnn::INetwork::Create();
|
||||
|
||||
armnn::TensorShape inputShape = ArmNNTensorShape(X->Shape());
|
||||
armnn::TensorShape outputShape = ArmNNTensorShape(Y->Shape());
|
||||
|
||||
armnn::ActivationDescriptor desc;
|
||||
desc.m_Function = armnn::ActivationFunction::ReLu;
|
||||
|
||||
armnn::IConnectableLayer* activation = myNetwork->AddActivationLayer(desc, "relu_armnn");
|
||||
|
||||
armnn::IConnectableLayer *InputLayer = myNetwork->AddInputLayer(0);
|
||||
armnn::IConnectableLayer *OutputLayer = myNetwork->AddOutputLayer(0);
|
||||
|
||||
InputLayer->GetOutputSlot(0).Connect(activation->GetInputSlot(0));
|
||||
activation->GetOutputSlot(0).Connect(OutputLayer->GetInputSlot(0));
|
||||
|
||||
//Set the tensors in the network.
|
||||
armnn::TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32);
|
||||
InputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
|
||||
|
||||
armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32);
|
||||
activation->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
|
||||
|
||||
// Optimise ArmNN network
|
||||
armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*myNetwork, {armnn::Compute::CpuAcc}, Relu::run->GetDeviceSpec());
|
||||
|
||||
if (optNet == nullptr) {
|
||||
ORT_NOT_IMPLEMENTED("Something went wrong when creating the layer");
|
||||
}
|
||||
|
||||
// Load graph into runtime
|
||||
Relu::run->LoadNetwork(networkId, std::move(optNet));
|
||||
|
||||
std::pair<ReluLayersIterator, bool> ret;
|
||||
ret = Relu::reluLayers.insert(std::pair<OpKernel*, armnn::NetworkId>((OpKernel*)this, networkId));
|
||||
pNetworkId = &ret.first->second;
|
||||
|
||||
} else {
|
||||
pNetworkId = &it->second;
|
||||
}
|
||||
|
||||
armnn::InputTensors inputTensors{{0, armnn::ConstTensor(Relu::run->GetInputTensorInfo(*pNetworkId, 0),
|
||||
src_data)}};
|
||||
armnn::OutputTensors outputTensors{{0, armnn::Tensor(Relu::run->GetOutputTensorInfo(*pNetworkId, 0),
|
||||
dst_data)}};
|
||||
|
||||
Relu::run->EnqueueWorkload(*pNetworkId, inputTensors, outputTensors);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ONNX_OPERATOR_KERNEL_EX(
|
||||
Relu,
|
||||
kOnnxDomain,
|
||||
6,
|
||||
kArmNNExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
Relu<float>);
|
||||
|
||||
} // namespace armnn_ep
|
||||
} // namespace onnxruntime
|
||||
|
||||
#endif
|
||||
52
onnxruntime/core/providers/armnn/activation/activations.h
Normal file
52
onnxruntime/core/providers/armnn/activation/activations.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License
|
||||
|
||||
#ifdef RELU_ARMNN
|
||||
|
||||
#pragma once
|
||||
#include "core/framework/op_kernel.h"
|
||||
#include "core/providers/cpu/activation/activations.h"
|
||||
#include "core/providers/armnn/armnn_execution_provider.h"
|
||||
|
||||
#include "armnn/ArmNN.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
|
||||
typedef std::map<OpKernel*, armnn::NetworkId>::iterator ReluLayersIterator;
|
||||
|
||||
template <typename T>
|
||||
class Relu : public OpKernel {
|
||||
public:
|
||||
explicit Relu(const OpKernelInfo& info) : OpKernel(info) {
|
||||
provider_ = (const_cast<ArmNNExecutionProvider*>(
|
||||
dynamic_cast<const ArmNNExecutionProvider*>(info.GetExecutionProvider())));
|
||||
}
|
||||
|
||||
~Relu() {
|
||||
Relu::reluLayers.erase(this);
|
||||
}
|
||||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
|
||||
static armnn::IRuntimePtr initRuntime(){
|
||||
if (Relu::run)
|
||||
return std::move(Relu::run);
|
||||
armnn::IRuntime::CreationOptions options;
|
||||
return std::move(armnn::IRuntime::Create(options));
|
||||
}
|
||||
|
||||
private:
|
||||
static thread_local std::map<OpKernel*, armnn::NetworkId> reluLayers;
|
||||
ArmNNExecutionProvider* provider_;
|
||||
static armnn::IRuntimePtr run;
|
||||
};
|
||||
|
||||
} // namespace armnn_ep
|
||||
} // namespace onnxruntime
|
||||
|
||||
#endif
|
||||
25
onnxruntime/core/providers/armnn/armnn_common.cc
Normal file
25
onnxruntime/core/providers/armnn/armnn_common.cc
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright(C) 2018 Intel Corporation
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
|
||||
#include "core/providers/armnn/armnn_common.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
|
||||
armnn::TensorShape ArmNNTensorShape(const TensorShape& tensorShape) {
|
||||
std::vector<unsigned int> dims;
|
||||
unsigned int inDim = tensorShape.NumDimensions();
|
||||
|
||||
for (unsigned int i = 0; i < inDim; ++i)
|
||||
dims.push_back(tensorShape.GetDims()[i]);
|
||||
|
||||
return armnn::TensorShape{static_cast<unsigned int>(dims.size()), dims.data()};
|
||||
}
|
||||
|
||||
} // namespace armnn_ep
|
||||
} // namespace onnxruntime
|
||||
18
onnxruntime/core/providers/armnn/armnn_common.h
Normal file
18
onnxruntime/core/providers/armnn/armnn_common.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "core/common/common.h"
|
||||
#include "core/framework/op_kernel.h"
|
||||
|
||||
#include "armnn/ArmNN.hpp"
|
||||
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
|
||||
armnn::TensorShape ArmNNTensorShape(const TensorShape& tensorShape);
|
||||
|
||||
} // namespace armnn_ep
|
||||
} // namespace onnxruntime
|
||||
119
onnxruntime/core/providers/armnn/armnn_execution_provider.cc
Normal file
119
onnxruntime/core/providers/armnn/armnn_execution_provider.cc
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "armnn_execution_provider.h"
|
||||
#include "core/framework/allocator.h"
|
||||
#include "core/framework/op_kernel.h"
|
||||
#include "core/framework/kernel_registry.h"
|
||||
#include "core/framework/compute_capability.h"
|
||||
#include "contrib_ops/cpu/cpu_contrib_kernels.h"
|
||||
#include "armnn_fwd.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
|
||||
constexpr const char* ArmNN = "ArmNN";
|
||||
constexpr const char* ArmNN_CPU = "ArmNNCpu";
|
||||
|
||||
namespace armnn_ep {
|
||||
|
||||
// Forward declarations of op kernels
|
||||
#ifdef RELU_ARMNN
|
||||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 6, Relu);
|
||||
#endif
|
||||
class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 1, 10, Conv);
|
||||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 11, Conv);
|
||||
|
||||
class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 7, 8, Gemm);
|
||||
class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 9, 10, Gemm);
|
||||
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 11, Gemm);
|
||||
|
||||
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 7, 9, float, AveragePool);
|
||||
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 10, 10, float, AveragePool);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 11, float, AveragePool);
|
||||
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 1, 7, float, MaxPool);
|
||||
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 8, 11, float, MaxPool);
|
||||
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 1, float, GlobalAveragePool);
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 1, float, GlobalMaxPool);
|
||||
|
||||
static void RegisterArmNNKernels(KernelRegistry& kernel_registry) {
|
||||
|
||||
#ifdef RELU_ARMNN
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 6, Relu)>());
|
||||
#endif
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 1, 10, Conv)>());
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 11, Conv)>());
|
||||
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 7, 8, Gemm)>());
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 9, 10, Gemm)>());
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 11, Gemm)>());
|
||||
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 7, 9, float, AveragePool)>());
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 10, 10, float, AveragePool)>());
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 11, float, AveragePool)>());
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 1, 7, float, MaxPool)>());
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 8, 11, float, MaxPool)>());
|
||||
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 1, float, GlobalAveragePool)>());
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kArmNNExecutionProvider, kOnnxDomain, 1, float, GlobalMaxPool)>());
|
||||
|
||||
}
|
||||
|
||||
std::shared_ptr<KernelRegistry> GetArmNNKernelRegistry() {
|
||||
std::shared_ptr<KernelRegistry> kernel_registry = std::make_shared<KernelRegistry>();
|
||||
RegisterArmNNKernels(*kernel_registry);
|
||||
|
||||
return kernel_registry;
|
||||
}
|
||||
|
||||
} // namespace armnn_ep
|
||||
|
||||
ArmNNExecutionProvider::ArmNNExecutionProvider(const ArmNNExecutionProviderInfo& info)
|
||||
: IExecutionProvider{onnxruntime::kArmNNExecutionProvider} {
|
||||
ORT_UNUSED_PARAMETER(info);
|
||||
|
||||
auto default_allocator_factory = [](int) {
|
||||
auto memory_info = onnxruntime::make_unique<OrtMemoryInfo>(ArmNN, OrtAllocatorType::OrtDeviceAllocator);
|
||||
return onnxruntime::make_unique<CPUAllocator>(std::move(memory_info));
|
||||
};
|
||||
|
||||
DeviceAllocatorRegistrationInfo default_memory_info{
|
||||
OrtMemTypeDefault,
|
||||
std::move(default_allocator_factory),
|
||||
std::numeric_limits<size_t>::max()};
|
||||
|
||||
InsertAllocator(CreateAllocator(default_memory_info));
|
||||
|
||||
auto cpu_allocator_factory = [](int) {
|
||||
auto memory_info = onnxruntime::make_unique<OrtMemoryInfo>(
|
||||
ArmNN_CPU, OrtAllocatorType::OrtDeviceAllocator, OrtDevice(), 0, OrtMemTypeCPUOutput);
|
||||
return onnxruntime::make_unique<CPUAllocator>(std::move(memory_info));
|
||||
};
|
||||
|
||||
DeviceAllocatorRegistrationInfo cpu_memory_info{
|
||||
OrtMemTypeCPUOutput,
|
||||
std::move(cpu_allocator_factory),
|
||||
std::numeric_limits<size_t>::max()};
|
||||
|
||||
InsertAllocator(CreateAllocator(cpu_memory_info));
|
||||
}
|
||||
|
||||
ArmNNExecutionProvider::~ArmNNExecutionProvider() {
|
||||
}
|
||||
|
||||
std::shared_ptr<KernelRegistry> ArmNNExecutionProvider::GetKernelRegistry() const {
|
||||
static std::shared_ptr<KernelRegistry> kernel_registry = onnxruntime::armnn_ep::GetArmNNKernelRegistry();
|
||||
return kernel_registry;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ComputeCapability>>
|
||||
ArmNNExecutionProvider::GetCapability(const onnxruntime::GraphViewer& graph,
|
||||
const std::vector<const KernelRegistry*>& kernel_registries) const {
|
||||
std::vector<std::unique_ptr<ComputeCapability>>
|
||||
result = IExecutionProvider::GetCapability(graph, kernel_registries);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace onnxruntime
|
||||
41
onnxruntime/core/providers/armnn/armnn_execution_provider.h
Executable file
41
onnxruntime/core/providers/armnn/armnn_execution_provider.h
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/framework/allocatormgr.h"
|
||||
#include "core/framework/execution_provider.h"
|
||||
#include "core/graph/constants.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
|
||||
// Information needed to construct ArmNN execution providers.
|
||||
struct ArmNNExecutionProviderInfo {
|
||||
bool create_arena{true};
|
||||
|
||||
explicit ArmNNExecutionProviderInfo(bool use_arena)
|
||||
: create_arena(use_arena) {}
|
||||
|
||||
ArmNNExecutionProviderInfo() = default;
|
||||
};
|
||||
|
||||
// Logical device representation.
|
||||
class ArmNNExecutionProvider : public IExecutionProvider {
|
||||
public:
|
||||
explicit ArmNNExecutionProvider(const ArmNNExecutionProviderInfo& info);
|
||||
virtual ~ArmNNExecutionProvider();
|
||||
|
||||
std::vector<std::unique_ptr<ComputeCapability>> GetCapability(
|
||||
const onnxruntime::GraphViewer& graph,
|
||||
const std::vector<const KernelRegistry*>& kernel_registries) const override;
|
||||
|
||||
const void* GetExecutionHandle() const noexcept override {
|
||||
// The ArmNN interface does not return anything interesting.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<KernelRegistry> GetKernelRegistry() const override;
|
||||
};
|
||||
|
||||
} // namespace onnxruntime
|
||||
12
onnxruntime/core/providers/armnn/armnn_fwd.h
Executable file
12
onnxruntime/core/providers/armnn/armnn_fwd.h
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
template <typename T>
|
||||
KernelCreateInfo BuildKernelCreateInfo();
|
||||
}
|
||||
}
|
||||
35
onnxruntime/core/providers/armnn/armnn_provider_factory.cc
Executable file
35
onnxruntime/core/providers/armnn/armnn_provider_factory.cc
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "core/providers/armnn/armnn_provider_factory.h"
|
||||
#include "armnn_execution_provider.h"
|
||||
#include "core/session/abi_session_options_impl.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
|
||||
struct ArmNNProviderFactory : IExecutionProviderFactory {
|
||||
ArmNNProviderFactory(bool create_arena) : create_arena_(create_arena) {}
|
||||
~ArmNNProviderFactory() override {}
|
||||
std::unique_ptr<IExecutionProvider> CreateProvider() override;
|
||||
|
||||
private:
|
||||
bool create_arena_;
|
||||
};
|
||||
|
||||
std::unique_ptr<IExecutionProvider> ArmNNProviderFactory::CreateProvider() {
|
||||
ArmNNExecutionProviderInfo info;
|
||||
info.create_arena = create_arena_;
|
||||
return std::make_unique<ArmNNExecutionProvider>(info);
|
||||
}
|
||||
|
||||
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_ArmNN(int use_arena) {
|
||||
return std::make_shared<onnxruntime::ArmNNProviderFactory>(use_arena != 0);
|
||||
}
|
||||
|
||||
} // namespace onnxruntime
|
||||
|
||||
ORT_API_STATUS_IMPL(OrtSessionOptionsAppendExecutionProvider_ArmNN, _In_ OrtSessionOptions* options, int use_arena) {
|
||||
options->provider_factories.push_back(onnxruntime::CreateExecutionProviderFactory_ArmNN(use_arena));
|
||||
return nullptr;
|
||||
}
|
||||
39
onnxruntime/core/providers/armnn/math/gemm.cc
Executable file
39
onnxruntime/core/providers/armnn/math/gemm.cc
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "core/providers/armnn/armnn_common.h"
|
||||
#include "core/providers/armnn/math/gemm.h"
|
||||
#include "core/providers/armnn/armnn_fwd.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
|
||||
ONNX_OPERATOR_VERSIONED_KERNEL_EX(
|
||||
Gemm,
|
||||
kOnnxDomain,
|
||||
7,
|
||||
8,
|
||||
kArmNNExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
Gemm<float>);
|
||||
|
||||
ONNX_OPERATOR_VERSIONED_KERNEL_EX(
|
||||
Gemm,
|
||||
kOnnxDomain,
|
||||
9,
|
||||
10,
|
||||
kArmNNExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
Gemm<float>);
|
||||
|
||||
ONNX_OPERATOR_KERNEL_EX(
|
||||
Gemm,
|
||||
kOnnxDomain,
|
||||
11,
|
||||
kArmNNExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
Gemm<float>);
|
||||
|
||||
} // namespace armnn_ep
|
||||
} // namespace onnxruntime
|
||||
186
onnxruntime/core/providers/armnn/math/gemm.h
Normal file
186
onnxruntime/core/providers/armnn/math/gemm.h
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/framework/op_kernel.h"
|
||||
#include "core/util/math.h"
|
||||
#include "core/util/math_cpuonly.h"
|
||||
#include "core/providers/cpu/math/gemm.h"
|
||||
#include "core/providers/armnn/armnn_execution_provider.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
|
||||
typedef std::map<OpKernel*, armnn::NetworkId>::iterator GEMMLayersIterator;
|
||||
|
||||
template <typename T>
|
||||
class Gemm : public onnxruntime::Gemm<T> {
|
||||
public:
|
||||
Gemm(const OpKernelInfo& info) : onnxruntime::Gemm<T>(info) {
|
||||
int64_t temp;
|
||||
|
||||
ORT_ENFORCE(info.GetAttr<int64_t>("transA", &temp).IsOK());
|
||||
trans_A_ = temp == 0 ? CblasNoTrans : CblasTrans;
|
||||
ORT_ENFORCE(info.GetAttr<int64_t>("transB", &temp).IsOK());
|
||||
trans_B_ = temp == 0 ? CblasNoTrans : CblasTrans;
|
||||
|
||||
ORT_ENFORCE(info.GetAttr<float>("alpha", &alpha_).IsOK());
|
||||
ORT_ENFORCE(info.GetAttr<float>("beta", &beta_).IsOK());
|
||||
}
|
||||
|
||||
Status Compute(OpKernelContext* context) const override {
|
||||
const auto X = context->Input<Tensor>(0);
|
||||
const auto W = context->Input<Tensor>(1);
|
||||
const auto B = context->Input<Tensor>(2);
|
||||
|
||||
bool useBias = B != nullptr && beta_ != 0;
|
||||
bool FC = alpha_ == 1 && (beta_ == 1 || beta_ == 0);
|
||||
if (!FC) {
|
||||
return onnxruntime::Gemm<T>::Compute(context);
|
||||
}
|
||||
|
||||
GemmHelper helper(X->Shape(), trans_A_ != CblasNoTrans, W->Shape(), trans_B_ != CblasNoTrans, useBias ? B->Shape() : TensorShape({}));
|
||||
|
||||
if (!helper.State().IsOK())
|
||||
return helper.State();
|
||||
|
||||
int64_t M = helper.M();
|
||||
int64_t N = helper.N();
|
||||
auto Y = context->Output(0, TensorShape({M, N}));
|
||||
|
||||
if (trans_A_ == CblasTrans) { // transpose input
|
||||
return onnxruntime::Gemm<T>::Compute(context);
|
||||
}
|
||||
|
||||
int64_t K = helper.K();
|
||||
LOGS_DEFAULT(VERBOSE) << "Gemm ArmNN:" << std::endl;
|
||||
if (X) LOGS_DEFAULT(VERBOSE) << "X " << X->Shape().ToString().c_str() << std::endl;
|
||||
if (W) LOGS_DEFAULT(VERBOSE) << "W " << W->Shape().ToString().c_str() << std::endl;
|
||||
if (B) LOGS_DEFAULT(VERBOSE) << "B " << B->Shape().ToString().c_str() << std::endl;
|
||||
LOGS_DEFAULT(VERBOSE) << "Y " << Y->Shape().ToString().c_str() << std::endl;
|
||||
LOGS_DEFAULT(VERBOSE) << "M " << (int)M << ", N " << (int)N << ", K " << (int)K << std::endl;
|
||||
LOGS_DEFAULT(VERBOSE) << "Alfa " << alpha_ << ", Beta " << beta_ << std::endl;
|
||||
LOGS_DEFAULT(VERBOSE) << "trans_A_ " << (trans_A_ == CblasTrans) << std::endl;
|
||||
LOGS_DEFAULT(VERBOSE) << "trans_B_ " << (trans_B_ == CblasTrans) << std::endl;
|
||||
LOGS_DEFAULT(VERBOSE) << std::endl;
|
||||
|
||||
const T* x_data = X->template Data<T>();
|
||||
const T* w_data = W->template Data<T>();
|
||||
const T* b_data;
|
||||
if (useBias)
|
||||
b_data = B->template Data<T>();
|
||||
T* y_data = Y->template MutableData<T>();
|
||||
|
||||
armnn::NetworkId* pNetworkId;
|
||||
GEMMLayersIterator it = Gemm::gemmLayers.find((OpKernel*)this);
|
||||
if (it == Gemm::gemmLayers.end()) {
|
||||
|
||||
armnn::NetworkId networkId;
|
||||
|
||||
armnn::INetworkPtr myNetwork = armnn::INetwork::Create();
|
||||
|
||||
armnn::TensorShape inputShape = ArmNNTensorShape(X->Shape());
|
||||
armnn::TensorShape weightShape = ArmNNTensorShape(W->Shape());
|
||||
armnn::TensorShape outputShape = ArmNNTensorShape(Y->Shape());
|
||||
|
||||
armnn::FullyConnectedDescriptor fcDescriptor;
|
||||
fcDescriptor.m_BiasEnabled = useBias;
|
||||
fcDescriptor.m_TransposeWeightMatrix = trans_B_ == CblasTrans;
|
||||
|
||||
armnn::IConnectableLayer* fc_armnn;
|
||||
|
||||
armnn::TensorInfo weightsInfo(weightShape, armnn::DataType::Float32);
|
||||
armnn::ConstTensor weights(weightsInfo, w_data);
|
||||
|
||||
if (fcDescriptor.m_BiasEnabled) {
|
||||
armnn::TensorShape biasShape = ArmNNTensorShape(B->Shape());
|
||||
if(B->Shape().NumDimensions() == 2){
|
||||
if(B->Shape().GetDims()[0] == 1 && B->Shape().GetDims()[1] > 1)
|
||||
biasShape = {B->Shape().GetDims()[1]};
|
||||
}
|
||||
armnn::TensorInfo biasDesc(biasShape, armnn::DataType::Float32);
|
||||
armnn::ConstTensor bias(biasDesc, b_data);
|
||||
fc_armnn = myNetwork->AddFullyConnectedLayer(fcDescriptor,
|
||||
weights,
|
||||
armnn::Optional<armnn::ConstTensor>(bias),
|
||||
"fc_armnn");
|
||||
} else {
|
||||
fc_armnn = myNetwork->AddFullyConnectedLayer(fcDescriptor,
|
||||
weights,
|
||||
armnn::EmptyOptional(),
|
||||
"fc_armnn");
|
||||
}
|
||||
|
||||
armnn::IConnectableLayer *InputLayer = myNetwork->AddInputLayer(0);
|
||||
armnn::IConnectableLayer *OutputLayer = myNetwork->AddOutputLayer(0);
|
||||
|
||||
InputLayer->GetOutputSlot(0).Connect(fc_armnn->GetInputSlot(0));
|
||||
fc_armnn->GetOutputSlot(0).Connect(OutputLayer->GetInputSlot(0));
|
||||
|
||||
//Set the tensors in the network.
|
||||
armnn::TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32);
|
||||
InputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
|
||||
|
||||
armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32);
|
||||
fc_armnn->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
|
||||
|
||||
// Optimise ArmNN network
|
||||
armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*myNetwork, {armnn::Compute::CpuAcc}, Gemm::run->GetDeviceSpec());
|
||||
|
||||
if (optNet == nullptr) {
|
||||
return onnxruntime::Gemm<T>::Compute(context);
|
||||
}
|
||||
|
||||
// Load graph into runtime
|
||||
Gemm::run->LoadNetwork(networkId, std::move(optNet));
|
||||
|
||||
std::pair<GEMMLayersIterator, bool> ret;
|
||||
ret = Gemm::gemmLayers.insert(std::pair<OpKernel*, armnn::NetworkId>((OpKernel*)this, networkId));
|
||||
pNetworkId = &ret.first->second;
|
||||
|
||||
} else {
|
||||
pNetworkId = &it->second;
|
||||
}
|
||||
|
||||
armnn::InputTensors inputTensors{{0, armnn::ConstTensor(Gemm::run->GetInputTensorInfo(*pNetworkId, 0),
|
||||
x_data)}};
|
||||
armnn::OutputTensors outputTensors{{0, armnn::Tensor(Gemm::run->GetOutputTensorInfo(*pNetworkId, 0),
|
||||
y_data)}};
|
||||
|
||||
Gemm::run->EnqueueWorkload(*pNetworkId, inputTensors, outputTensors);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
~Gemm() {
|
||||
gemmLayers.erase(this);
|
||||
}
|
||||
|
||||
static armnn::IRuntimePtr initRuntime(){
|
||||
if (Gemm::run)
|
||||
return std::move(Gemm::run);
|
||||
armnn::IRuntime::CreationOptions options;
|
||||
return std::move(armnn::IRuntime::Create(options));
|
||||
}
|
||||
|
||||
private:
|
||||
static thread_local std::map<OpKernel*, armnn::NetworkId> gemmLayers;
|
||||
ArmNNExecutionProvider* provider_;
|
||||
static armnn::IRuntimePtr run;
|
||||
|
||||
CBLAS_TRANSPOSE trans_A_;
|
||||
CBLAS_TRANSPOSE trans_B_;
|
||||
float alpha_;
|
||||
float beta_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
thread_local std::map<OpKernel*, armnn::NetworkId> onnxruntime::armnn_ep::Gemm<T>::gemmLayers;
|
||||
|
||||
template <typename T>
|
||||
armnn::IRuntimePtr Gemm<T>::run = Gemm<T>::initRuntime();
|
||||
|
||||
} // namespace armnn_ep
|
||||
} // namespace onnxruntime
|
||||
300
onnxruntime/core/providers/armnn/nn/conv.cc
Normal file
300
onnxruntime/core/providers/armnn/nn/conv.cc
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
|
||||
#include "core/common/common.h"
|
||||
#include "core/framework/op_kernel.h"
|
||||
#include "core/util/math.h"
|
||||
#include "core/util/math_cpuonly.h"
|
||||
|
||||
#include "core/providers/armnn/nn/conv.h"
|
||||
#include "core/providers/armnn/armnn_common.h"
|
||||
#include "core/providers/armnn/armnn_fwd.h"
|
||||
|
||||
#define PREF_DIM 4
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
|
||||
template <typename T>
|
||||
thread_local std::map<OpKernel*, armnn::NetworkId> Conv<T>::convLayers;
|
||||
|
||||
template <typename T>
|
||||
armnn::IRuntimePtr Conv<T>::run = Conv<T>::initRuntime();
|
||||
|
||||
armnn::Convolution2dDescriptor createConvDescriptor(std::vector<int64_t> pads, std::vector<int64_t> dilations, std::vector<int64_t> strides, bool biasEnabled){
|
||||
|
||||
std::vector<int64_t> armnnStrides(2);
|
||||
armnnStrides[0] = (strides.size() == 2) ? strides[1] : 1;
|
||||
armnnStrides[1] = strides[0];
|
||||
|
||||
std::vector<int64_t> armnnDilations(2);
|
||||
armnnDilations[0] = (dilations.size() == 2) ? dilations[1] : 1;
|
||||
armnnDilations[1] = dilations[0];
|
||||
|
||||
std::vector<int64_t> armnnPads(4);
|
||||
if (pads.size() == 2) {
|
||||
if (strides.size() == 1) {
|
||||
armnnPads[0] = 0;
|
||||
armnnPads[1] = 0;
|
||||
armnnPads[2] = pads[1];
|
||||
armnnPads[3] = pads[0];
|
||||
} else {
|
||||
armnnPads[0] = pads[1];
|
||||
armnnPads[1] = pads[0];
|
||||
armnnPads[2] = pads[1];
|
||||
armnnPads[3] = pads[0];
|
||||
}
|
||||
} else {
|
||||
armnnPads[0] = pads[1];
|
||||
armnnPads[1] = pads[3];
|
||||
armnnPads[2] = pads[0];
|
||||
armnnPads[3] = pads[2];
|
||||
}
|
||||
|
||||
armnn::Convolution2dDescriptor convolutionDescriptor;
|
||||
convolutionDescriptor.m_PadLeft = armnnPads[0];
|
||||
convolutionDescriptor.m_PadRight = armnnPads[1];
|
||||
convolutionDescriptor.m_PadTop = armnnPads[2];
|
||||
convolutionDescriptor.m_PadBottom = armnnPads[3];
|
||||
convolutionDescriptor.m_StrideX = armnnStrides[0];
|
||||
convolutionDescriptor.m_StrideY = armnnStrides[1];
|
||||
convolutionDescriptor.m_DilationX = armnnDilations[0];
|
||||
convolutionDescriptor.m_DilationY = armnnDilations[1];
|
||||
convolutionDescriptor.m_BiasEnabled = biasEnabled;
|
||||
convolutionDescriptor.m_DataLayout = armnn::DataLayout::NCHW;
|
||||
|
||||
return convolutionDescriptor;
|
||||
}
|
||||
|
||||
armnn::DepthwiseConvolution2dDescriptor createDepthwiseDescriptor(armnn::Convolution2dDescriptor convolutionDescriptor){
|
||||
|
||||
armnn::DepthwiseConvolution2dDescriptor depthwiseDescriptor;
|
||||
depthwiseDescriptor.m_PadLeft = convolutionDescriptor.m_PadLeft;
|
||||
depthwiseDescriptor.m_PadRight = convolutionDescriptor.m_PadRight;
|
||||
depthwiseDescriptor.m_PadTop = convolutionDescriptor.m_PadTop;
|
||||
depthwiseDescriptor.m_PadBottom = convolutionDescriptor.m_PadBottom;
|
||||
depthwiseDescriptor.m_StrideX = convolutionDescriptor.m_StrideX;
|
||||
depthwiseDescriptor.m_StrideY = convolutionDescriptor.m_StrideY;
|
||||
depthwiseDescriptor.m_DilationX = convolutionDescriptor.m_DilationX;
|
||||
depthwiseDescriptor.m_DilationY = convolutionDescriptor.m_DilationY;
|
||||
depthwiseDescriptor.m_BiasEnabled = convolutionDescriptor.m_BiasEnabled;
|
||||
depthwiseDescriptor.m_DataLayout = convolutionDescriptor.m_DataLayout;
|
||||
|
||||
return depthwiseDescriptor;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Status Conv<T>::Compute(OpKernelContext* context) const {
|
||||
size_t num_inputs = OpKernel::Node().InputDefs().size();
|
||||
const Tensor* X = context->Input<Tensor>(0);
|
||||
const Tensor* W = context->Input<Tensor>(1);
|
||||
const Tensor* B = num_inputs == 3 ? context->Input<Tensor>(2) : nullptr;
|
||||
|
||||
const int64_t N = X->Shape()[0];
|
||||
const int64_t M = W->Shape()[0];
|
||||
|
||||
if (X->Shape().NumDimensions() != PREF_DIM) {
|
||||
Status s = onnxruntime::Conv<T>::Compute(context);
|
||||
return s;
|
||||
}
|
||||
|
||||
ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X, W));
|
||||
|
||||
std::vector<int64_t> kernel_shape;
|
||||
ORT_RETURN_IF_ERROR(conv_attrs_.ComputeKernelShape(W->Shape(), kernel_shape));
|
||||
|
||||
std::vector<int64_t> pads(conv_attrs_.pads);
|
||||
if (pads.empty()) {
|
||||
pads.resize(kernel_shape.size() * 2, 0);
|
||||
}
|
||||
std::vector<int64_t> dilations(conv_attrs_.dilations);
|
||||
if (dilations.empty()) {
|
||||
dilations.resize(kernel_shape.size(), 1);
|
||||
}
|
||||
std::vector<int64_t> strides(conv_attrs_.strides);
|
||||
if (strides.empty()) {
|
||||
strides.resize(kernel_shape.size(), 1);
|
||||
}
|
||||
|
||||
std::vector<int64_t> Y_dims;
|
||||
Y_dims.insert(Y_dims.begin(), {N, M});
|
||||
TensorShape input_shape = X->Shape().Slice(2);
|
||||
ORT_RETURN_IF_ERROR(conv_attrs_.InferOutputShape(input_shape, kernel_shape, strides, dilations, &pads, &Y_dims));
|
||||
Tensor* Y = context->Output(0, TensorShape(Y_dims));
|
||||
|
||||
bool biasEnabled = B != nullptr;
|
||||
|
||||
const T* x_data = X->template Data<T>();
|
||||
const T* k_data = W->template Data<T>();
|
||||
|
||||
const T* b_data;
|
||||
if (biasEnabled) {
|
||||
b_data = B->template Data<T>();
|
||||
}
|
||||
|
||||
T* y_data = Y->template MutableData<T>();
|
||||
|
||||
armnn::NetworkId* pNetworkId;
|
||||
ConvLayersIterator it = Conv::convLayers.find((OpKernel*)this);
|
||||
if (it == Conv::convLayers.end()) {
|
||||
|
||||
armnn::NetworkId networkId;
|
||||
armnn::INetworkPtr myNetwork = armnn::INetwork::Create();
|
||||
|
||||
armnn::Convolution2dDescriptor convolutionDescriptor = createConvDescriptor(pads, dilations, strides, biasEnabled);
|
||||
|
||||
armnn::IConnectableLayer *convolution_armnn;
|
||||
armnn::TensorShape inputShape = ArmNNTensorShape(X->Shape());
|
||||
armnn::TensorShape weightShape = ArmNNTensorShape(W->Shape());
|
||||
|
||||
if (weightShape[2] == 1 && weightShape[3] == 1) {
|
||||
Status s = onnxruntime::Conv<T>::Compute(context);
|
||||
return s;
|
||||
}
|
||||
|
||||
if (conv_attrs_.group > 1) {
|
||||
|
||||
if (conv_attrs_.group == inputShape[1]) {
|
||||
// depthwise convolution
|
||||
armnn::DepthwiseConvolution2dDescriptor depthwiseDescriptor = createDepthwiseDescriptor(convolutionDescriptor);
|
||||
|
||||
weightShape[1] = weightShape[0];
|
||||
weightShape[0] = 1;
|
||||
armnn::TensorInfo weightsInfo(weightShape, armnn::DataType::Float32);
|
||||
armnn::ConstTensor weights(weightsInfo, k_data);
|
||||
|
||||
if (biasEnabled) {
|
||||
armnn::TensorInfo biasDesc(ArmNNTensorShape(B->Shape()), armnn::DataType::Float32);
|
||||
armnn::ConstTensor bias(biasDesc, b_data);
|
||||
convolution_armnn = myNetwork->AddDepthwiseConvolution2dLayer(depthwiseDescriptor,
|
||||
weights,
|
||||
armnn::Optional<armnn::ConstTensor>(bias),
|
||||
"depthwise_convolution_armnn");
|
||||
} else {
|
||||
convolution_armnn = myNetwork->AddDepthwiseConvolution2dLayer(depthwiseDescriptor,
|
||||
weights,
|
||||
armnn::EmptyOptional(),
|
||||
"depthwise_convolution_armnn");
|
||||
}
|
||||
} else {
|
||||
// NCHWc convolution
|
||||
Status s = onnxruntime::Conv<T>::Compute(context);
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
// normal convolution
|
||||
armnn::TensorInfo weightsInfo(weightShape, armnn::DataType::Float32);
|
||||
armnn::ConstTensor weights(weightsInfo, k_data);
|
||||
|
||||
if (biasEnabled) {
|
||||
armnn::TensorInfo biasDesc(ArmNNTensorShape(B->Shape()), armnn::DataType::Float32);
|
||||
armnn::ConstTensor bias(biasDesc, b_data);
|
||||
convolution_armnn = myNetwork->AddConvolution2dLayer(convolutionDescriptor,
|
||||
weights,
|
||||
armnn::Optional<armnn::ConstTensor>(bias),
|
||||
"convolution_armnn");
|
||||
} else {
|
||||
convolution_armnn = myNetwork->AddConvolution2dLayer(convolutionDescriptor,
|
||||
weights,
|
||||
armnn::EmptyOptional(),
|
||||
"convolution_armnn");
|
||||
}
|
||||
}
|
||||
|
||||
bool armnn_activ_enabled = false;
|
||||
armnn::ActivationDescriptor desc;
|
||||
desc.m_A = conv_attrs_.alpha;
|
||||
|
||||
if (activation_type == "Relu") {
|
||||
desc.m_Function = armnn::ActivationFunction::ReLu;
|
||||
armnn_activ_enabled = true;
|
||||
} else if (activation_type == "LeakyRelu") {
|
||||
desc.m_Function = armnn::ActivationFunction::LeakyReLu;
|
||||
armnn_activ_enabled = true;
|
||||
} else if (activation_type == "Tanh") {
|
||||
desc.m_Function = armnn::ActivationFunction::TanH;
|
||||
armnn_activ_enabled = true;
|
||||
} else if (activation_type == "Sigmoid") {
|
||||
desc.m_Function = armnn::ActivationFunction::Sigmoid;
|
||||
armnn_activ_enabled = true;
|
||||
} else if (!activation_type.empty()) {
|
||||
ORT_NOT_IMPLEMENTED("Not implemented fused activation: ", activation_type);
|
||||
}
|
||||
|
||||
armnn::IConnectableLayer* activation = myNetwork->AddActivationLayer(desc, "activation_armnn");
|
||||
|
||||
armnn::IConnectableLayer *InputLayer = myNetwork->AddInputLayer(0);
|
||||
armnn::IConnectableLayer *OutputLayer = myNetwork->AddOutputLayer(0);
|
||||
|
||||
InputLayer->GetOutputSlot(0).Connect(convolution_armnn->GetInputSlot(0));
|
||||
if (armnn_activ_enabled) {
|
||||
convolution_armnn->GetOutputSlot(0).Connect(activation->GetInputSlot(0));
|
||||
activation->GetOutputSlot(0).Connect(OutputLayer->GetInputSlot(0));
|
||||
}
|
||||
else {
|
||||
convolution_armnn->GetOutputSlot(0).Connect(OutputLayer->GetInputSlot(0));
|
||||
}
|
||||
|
||||
//Set the tensors in the network.
|
||||
armnn::TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32);
|
||||
InputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
|
||||
|
||||
armnn::TensorInfo outputTensorInfo(ArmNNTensorShape(Y->Shape()), armnn::DataType::Float32);
|
||||
convolution_armnn->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
|
||||
|
||||
if (armnn_activ_enabled) {
|
||||
activation->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
|
||||
}
|
||||
|
||||
// Optimise ArmNN network
|
||||
armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*myNetwork, {armnn::Compute::CpuAcc}, Conv::run->GetDeviceSpec());
|
||||
|
||||
if (optNet == nullptr) {
|
||||
return onnxruntime::Conv<T>::Compute(context);
|
||||
}
|
||||
|
||||
// Load graph into runtime
|
||||
Conv::run->LoadNetwork(networkId, std::move(optNet));
|
||||
|
||||
std::pair<ConvLayersIterator, bool> ret;
|
||||
ret = Conv::convLayers.insert(std::pair<OpKernel*, armnn::NetworkId>((OpKernel*)this, networkId));
|
||||
pNetworkId = &ret.first->second;
|
||||
|
||||
} else {
|
||||
pNetworkId = &it->second;
|
||||
}
|
||||
|
||||
armnn::InputTensors inputTensors{{0, armnn::ConstTensor(Conv::run->GetInputTensorInfo(*pNetworkId, 0),
|
||||
x_data)}};
|
||||
armnn::OutputTensors outputTensors{{0, armnn::Tensor(Conv::run->GetOutputTensorInfo(*pNetworkId, 0),
|
||||
y_data)}};
|
||||
|
||||
// Execute network
|
||||
Conv::run->EnqueueWorkload(*pNetworkId, inputTensors, outputTensors);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ONNX_OPERATOR_VERSIONED_KERNEL_EX(
|
||||
Conv,
|
||||
kOnnxDomain,
|
||||
1, 10,
|
||||
kArmNNExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
Conv<float>);
|
||||
|
||||
ONNX_OPERATOR_KERNEL_EX(
|
||||
Conv,
|
||||
kOnnxDomain,
|
||||
11,
|
||||
kArmNNExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
Conv<float>);
|
||||
|
||||
} // namespace armnn
|
||||
} // namespace onnxruntime
|
||||
51
onnxruntime/core/providers/armnn/nn/conv.h
Normal file
51
onnxruntime/core/providers/armnn/nn/conv.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "core/framework/op_kernel.h"
|
||||
#include "core/providers/cpu/nn/conv.h"
|
||||
#include "core/providers/armnn/armnn_execution_provider.h"
|
||||
|
||||
#include "armnn/ArmNN.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep{
|
||||
|
||||
typedef std::map<OpKernel*, armnn::NetworkId>::iterator ConvLayersIterator;
|
||||
|
||||
template <typename T>
|
||||
class Conv : public onnxruntime::Conv<T> {
|
||||
public:
|
||||
explicit Conv(const OpKernelInfo& info) : onnxruntime::Conv<T>(info), conv_attrs_(info) {
|
||||
provider_ = (const_cast<ArmNNExecutionProvider*>(
|
||||
dynamic_cast<const ArmNNExecutionProvider*>(info.GetExecutionProvider())));
|
||||
}
|
||||
|
||||
~Conv() {
|
||||
Conv::convLayers.erase(this);
|
||||
}
|
||||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
|
||||
static armnn::IRuntimePtr initRuntime(){
|
||||
if (Conv::run)
|
||||
return std::move(Conv::run);
|
||||
armnn::IRuntime::CreationOptions options;
|
||||
return std::move(armnn::IRuntime::Create(options));
|
||||
}
|
||||
|
||||
protected:
|
||||
static thread_local std::map<OpKernel*, armnn::NetworkId> convLayers;
|
||||
ConvAttributes conv_attrs_;
|
||||
ArmNNExecutionProvider* provider_;
|
||||
static armnn::IRuntimePtr run;
|
||||
std::string activation_type;
|
||||
|
||||
};
|
||||
|
||||
} // namespace armnn_ep
|
||||
} // namespace onnxruntime
|
||||
310
onnxruntime/core/providers/armnn/nn/pool.cc
Normal file
310
onnxruntime/core/providers/armnn/nn/pool.cc
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "core/common/common.h"
|
||||
#include "core/framework/op_kernel.h"
|
||||
#include "core/util/math.h"
|
||||
#include "core/util/math_cpuonly.h"
|
||||
|
||||
#include "core/providers/armnn/nn/pool.h"
|
||||
#include "core/providers/armnn/armnn_common.h"
|
||||
#include "core/providers/armnn/armnn_fwd.h"
|
||||
|
||||
#define PREF_DIM 4
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
|
||||
template <typename T, typename PoolType>
|
||||
thread_local std::map<OpKernel*, armnn::NetworkId> Pool<T, PoolType>::poolLayers;
|
||||
|
||||
template <typename T, typename PoolType>
|
||||
armnn::IRuntimePtr Pool<T, PoolType>::run = Pool<T, PoolType>::initRuntime();
|
||||
|
||||
template <typename T>
|
||||
thread_local std::map<OpKernel*, armnn::NetworkId> MaxPoolV8<T>::maxPoolLayers;
|
||||
|
||||
template <typename T>
|
||||
armnn::IRuntimePtr MaxPoolV8<T>::run = MaxPoolV8<T>::initRuntime();
|
||||
|
||||
armnn::Pooling2dDescriptor createDescriptor(std::vector<int64_t> pads, std::vector<int64_t> strides, std::vector<int64_t> kernel_shape, armnn::PoolingAlgorithm pool_type, onnxruntime::PoolAttributes pool_attrs){
|
||||
|
||||
std::vector<int64_t> armnnStrides(2);
|
||||
armnnStrides[0] = (strides.size() == 2) ? strides[1] : 1;
|
||||
armnnStrides[1] = strides[0];
|
||||
|
||||
std::vector<int64_t> armnnKernelShape(2);
|
||||
armnnKernelShape[0] = (kernel_shape.size() > 1) ? kernel_shape[1] : 1;
|
||||
armnnKernelShape[1] = kernel_shape[0];
|
||||
|
||||
std::vector<int64_t> armnnPads(4);
|
||||
if (pads.size() == 2) {
|
||||
if (strides.size() == 1) {
|
||||
armnnPads[0] = 0;
|
||||
armnnPads[1] = 0;
|
||||
armnnPads[2] = pads[1];
|
||||
armnnPads[3] = pads[0];
|
||||
} else {
|
||||
armnnPads[0] = pads[1];
|
||||
armnnPads[1] = pads[0];
|
||||
armnnPads[2] = pads[1];
|
||||
armnnPads[3] = pads[0];
|
||||
}
|
||||
} else {
|
||||
armnnPads[0] = pads[1];
|
||||
armnnPads[1] = pads[3];
|
||||
armnnPads[2] = pads[0];
|
||||
armnnPads[3] = pads[2];
|
||||
}
|
||||
|
||||
armnn::Pooling2dDescriptor poolDescriptor;
|
||||
poolDescriptor.m_PoolType = pool_type;
|
||||
poolDescriptor.m_PadLeft = armnnPads[0];
|
||||
poolDescriptor.m_PadRight = armnnPads[1];
|
||||
poolDescriptor.m_PadTop = armnnPads[2];
|
||||
poolDescriptor.m_PadBottom = armnnPads[3];
|
||||
poolDescriptor.m_PoolWidth = armnnKernelShape[0];
|
||||
poolDescriptor.m_PoolHeight = armnnKernelShape[1];
|
||||
poolDescriptor.m_StrideX = armnnStrides[0];
|
||||
poolDescriptor.m_StrideY = armnnStrides[1];
|
||||
poolDescriptor.m_OutputShapeRounding = pool_attrs.ceil_mode ? armnn::OutputShapeRounding::Ceiling : armnn::OutputShapeRounding::Floor;
|
||||
poolDescriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude;
|
||||
if (pool_type == armnn::PoolingAlgorithm::Average && pool_attrs.count_include_pad)
|
||||
poolDescriptor.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue;
|
||||
poolDescriptor.m_DataLayout = armnn::DataLayout::NCHW;
|
||||
|
||||
return poolDescriptor;
|
||||
}
|
||||
|
||||
template <typename T, typename PoolType>
|
||||
Status Pool<T, PoolType>::Compute(OpKernelContext* context) const {
|
||||
|
||||
const Tensor* X = context->Input<Tensor>(0);
|
||||
const TensorShape& x_shape = X->Shape();
|
||||
|
||||
std::vector<int64_t> dilations(PoolBase::pool_attrs_.dilations);
|
||||
std::vector<int64_t> armnnDilations(2);
|
||||
armnnDilations[0] = (dilations.size() == 2) ? dilations[1] : 1;
|
||||
armnnDilations[1] = (!dilations.empty()) ? dilations[0] : 1;
|
||||
|
||||
if ((X->Shape().NumDimensions() != PREF_DIM) ||
|
||||
(armnnDilations[0] * armnnDilations[1] > 1)) {
|
||||
Status s = onnxruntime::Pool<T, PoolType>::Compute(context);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<int64_t> pads = PoolBase::pool_attrs_.pads;
|
||||
std::vector<int64_t> strides = PoolBase::pool_attrs_.strides;
|
||||
std::vector<int64_t> kernel_shape = PoolBase::pool_attrs_.kernel_shape;
|
||||
|
||||
if (PoolBase::pool_attrs_.global_pooling) {
|
||||
const auto& input_dims = x_shape.GetDims();
|
||||
kernel_shape.assign(input_dims.begin() + 2, input_dims.end());
|
||||
strides.assign(kernel_shape.size(), 0);
|
||||
pads.assign(kernel_shape.size(), 0);
|
||||
}
|
||||
|
||||
std::vector<int64_t> output_dims = PoolBase::pool_attrs_.SetOutputSize(x_shape, x_shape[1], &pads);
|
||||
Tensor* Y = context->Output(0, TensorShape(output_dims));
|
||||
|
||||
const T* x_data = X->template Data<T>();
|
||||
T* y_data = Y->template MutableData<T>();
|
||||
|
||||
armnn::NetworkId* pNetworkId;
|
||||
PoolLayersIterator it = Pool::poolLayers.find((OpKernel*)this);
|
||||
if (it == Pool::poolLayers.end()) {
|
||||
|
||||
armnn::PoolingAlgorithm pool_type;
|
||||
if (PoolBase::op_name_ == "GlobalAveragePool" || PoolBase::op_name_ == "AveragePool"){
|
||||
pool_type = armnn::PoolingAlgorithm::Average;
|
||||
} else if (PoolBase::op_name_ == "GlobalMaxPool" || PoolBase::op_name_ == "MaxPool"){
|
||||
pool_type = armnn::PoolingAlgorithm::Max;
|
||||
} else
|
||||
return onnxruntime::Pool<T, PoolType>::Compute(context);
|
||||
|
||||
armnn::NetworkId networkId;
|
||||
|
||||
armnn::INetworkPtr myNetwork = armnn::INetwork::Create();
|
||||
|
||||
armnn::Pooling2dDescriptor poolDescriptor = createDescriptor(pads, strides, kernel_shape, pool_type, PoolBase::pool_attrs_);
|
||||
|
||||
armnn::IConnectableLayer *pool_armnn = myNetwork->AddPooling2dLayer(poolDescriptor, "pool_armnn");
|
||||
armnn::TensorShape inputShape = ArmNNTensorShape(X->Shape());
|
||||
armnn::TensorShape outputShape = ArmNNTensorShape(Y->Shape());
|
||||
|
||||
armnn::IConnectableLayer *InputLayer = myNetwork->AddInputLayer(0);
|
||||
armnn::IConnectableLayer *OutputLayer = myNetwork->AddOutputLayer(0);
|
||||
|
||||
InputLayer->GetOutputSlot(0).Connect(pool_armnn->GetInputSlot(0));
|
||||
pool_armnn->GetOutputSlot(0).Connect(OutputLayer->GetInputSlot(0));
|
||||
|
||||
//Set the tensors in the network.
|
||||
armnn::TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32);
|
||||
InputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
|
||||
|
||||
armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32);
|
||||
pool_armnn->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
|
||||
|
||||
// Optimise ArmNN network
|
||||
armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*myNetwork, {armnn::Compute::CpuAcc}, Pool::run->GetDeviceSpec());
|
||||
|
||||
if (optNet == nullptr) {
|
||||
return onnxruntime::Pool<T, PoolType>::Compute(context);
|
||||
}
|
||||
|
||||
// Load graph into runtime
|
||||
Pool::run->LoadNetwork(networkId, std::move(optNet));
|
||||
|
||||
std::pair<PoolLayersIterator, bool> ret;
|
||||
ret = Pool::poolLayers.insert(std::pair<OpKernel*, armnn::NetworkId>((OpKernel*)this, networkId));
|
||||
pNetworkId = &ret.first->second;
|
||||
|
||||
} else {
|
||||
pNetworkId = &it->second;
|
||||
}
|
||||
|
||||
armnn::InputTensors inputTensors{{0, armnn::ConstTensor(Pool::run->GetInputTensorInfo(*pNetworkId, 0),
|
||||
x_data)}};
|
||||
armnn::OutputTensors outputTensors{{0, armnn::Tensor(Pool::run->GetOutputTensorInfo(*pNetworkId, 0),
|
||||
y_data)}};
|
||||
|
||||
// Execute network
|
||||
Pool::run->EnqueueWorkload(*pNetworkId, inputTensors, outputTensors);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Status MaxPoolV8<T>::Compute(OpKernelContext* context) const {
|
||||
|
||||
const Tensor* X = context->Input<Tensor>(0);
|
||||
const TensorShape& x_shape = X->Shape();
|
||||
|
||||
std::vector<int64_t> dilations(PoolBase::pool_attrs_.dilations);
|
||||
std::vector<int64_t> armnnDilations(2);
|
||||
armnnDilations[0] = (dilations.size() == 2) ? dilations[1] : 1;
|
||||
armnnDilations[1] = (!dilations.empty()) ? dilations[0] : 1;
|
||||
|
||||
if ((X->Shape().NumDimensions() != PREF_DIM) ||
|
||||
(armnnDilations[0] * armnnDilations[1] > 1)) {
|
||||
Status s = onnxruntime::MaxPoolV8::Compute(context);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<int64_t> pads = PoolBase::pool_attrs_.pads;
|
||||
std::vector<int64_t> strides = PoolBase::pool_attrs_.strides;
|
||||
std::vector<int64_t> kernel_shape = PoolBase::pool_attrs_.kernel_shape;
|
||||
|
||||
if (PoolBase::pool_attrs_.global_pooling) {
|
||||
const auto& input_dims = x_shape.GetDims();
|
||||
kernel_shape.assign(input_dims.begin() + 2, input_dims.end());
|
||||
strides.assign(kernel_shape.size(), 0);
|
||||
pads.assign(kernel_shape.size(), 0);
|
||||
}
|
||||
|
||||
std::vector<int64_t> output_dims = PoolBase::pool_attrs_.SetOutputSize(x_shape, x_shape[1], &pads);
|
||||
Tensor* Y = context->Output(0, TensorShape(output_dims));
|
||||
|
||||
const T* x_data = X->template Data<T>();
|
||||
T* y_data = Y->template MutableData<T>();
|
||||
|
||||
armnn::NetworkId* pNetworkId;
|
||||
PoolLayersIterator it = MaxPoolV8::maxPoolLayers.find((OpKernel*)this);
|
||||
if (it == MaxPoolV8::maxPoolLayers.end()) {
|
||||
|
||||
armnn::NetworkId networkId;
|
||||
|
||||
armnn::INetworkPtr myNetwork = armnn::INetwork::Create();
|
||||
|
||||
armnn::Pooling2dDescriptor poolDescriptor = createDescriptor(pads, strides, kernel_shape, armnn::PoolingAlgorithm::Max, PoolBase::pool_attrs_);
|
||||
|
||||
armnn::IConnectableLayer *pool_armnn = myNetwork->AddPooling2dLayer(poolDescriptor, "pool_armnn");
|
||||
armnn::TensorShape inputShape = ArmNNTensorShape(X->Shape());
|
||||
armnn::TensorShape outputShape = ArmNNTensorShape(Y->Shape());
|
||||
|
||||
armnn::IConnectableLayer *InputLayer = myNetwork->AddInputLayer(0);
|
||||
armnn::IConnectableLayer *OutputLayer = myNetwork->AddOutputLayer(0);
|
||||
|
||||
InputLayer->GetOutputSlot(0).Connect(pool_armnn->GetInputSlot(0));
|
||||
pool_armnn->GetOutputSlot(0).Connect(OutputLayer->GetInputSlot(0));
|
||||
|
||||
//Set the tensors in the network.
|
||||
armnn::TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32);
|
||||
InputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
|
||||
|
||||
armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32);
|
||||
pool_armnn->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
|
||||
|
||||
// Optimise ArmNN network
|
||||
armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*myNetwork, {armnn::Compute::CpuAcc}, MaxPoolV8::run->GetDeviceSpec());
|
||||
|
||||
if (optNet == nullptr) {
|
||||
return onnxruntime::MaxPoolV8::Compute(context);
|
||||
}
|
||||
|
||||
// Load graph into runtime
|
||||
MaxPoolV8::run->LoadNetwork(networkId, std::move(optNet));
|
||||
|
||||
std::pair<PoolLayersIterator, bool> ret;
|
||||
ret = MaxPoolV8::maxPoolLayers.insert(std::pair<OpKernel*, armnn::NetworkId>((OpKernel*)this, networkId));
|
||||
pNetworkId = &ret.first->second;
|
||||
|
||||
} else {
|
||||
pNetworkId = &it->second;
|
||||
}
|
||||
|
||||
armnn::InputTensors inputTensors{{0, armnn::ConstTensor(MaxPoolV8::run->GetInputTensorInfo(*pNetworkId, 0),
|
||||
x_data)}};
|
||||
armnn::OutputTensors outputTensors{{0, armnn::Tensor(MaxPoolV8::run->GetOutputTensorInfo(*pNetworkId, 0),
|
||||
y_data)}};
|
||||
|
||||
// Execute network
|
||||
MaxPoolV8::run->EnqueueWorkload(*pNetworkId, inputTensors, outputTensors);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#define POOLING_KERNEL_VERSIONED(op_name, data_type, pool_type, since_version, end_version) \
|
||||
ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_EX( \
|
||||
op_name, \
|
||||
kOnnxDomain, \
|
||||
since_version, \
|
||||
end_version, \
|
||||
data_type, \
|
||||
kArmNNExecutionProvider, \
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<data_type>()), \
|
||||
Pool<data_type, pool_type>);
|
||||
|
||||
#define POOLING_KERNEL(op_name, data_type, pool_type, since_version) \
|
||||
ONNX_OPERATOR_TYPED_KERNEL_EX( \
|
||||
op_name, \
|
||||
kOnnxDomain, \
|
||||
since_version, \
|
||||
data_type, \
|
||||
kArmNNExecutionProvider, \
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<data_type>()), \
|
||||
Pool<data_type, pool_type>);
|
||||
|
||||
POOLING_KERNEL_VERSIONED(MaxPool, float, MaxPool<1>, 1, 7)
|
||||
POOLING_KERNEL_VERSIONED(AveragePool, float, AveragePool, 7, 9)
|
||||
POOLING_KERNEL_VERSIONED(AveragePool, float, AveragePool, 10, 10)
|
||||
POOLING_KERNEL(AveragePool, float, AveragePool, 11)
|
||||
POOLING_KERNEL(GlobalAveragePool, float, AveragePool, 1)
|
||||
POOLING_KERNEL(GlobalMaxPool, float, MaxPool<1>, 1)
|
||||
|
||||
ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_EX( \
|
||||
MaxPool, \
|
||||
kOnnxDomain, \
|
||||
8, \
|
||||
11, \
|
||||
float, \
|
||||
kArmNNExecutionProvider, \
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()), \
|
||||
MaxPoolV8<float>);
|
||||
|
||||
} // namespace armnn_ep
|
||||
} // namespace onnxruntime
|
||||
|
||||
75
onnxruntime/core/providers/armnn/nn/pool.h
Normal file
75
onnxruntime/core/providers/armnn/nn/pool.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "core/framework/op_kernel.h"
|
||||
#include "core/providers/cpu/nn/pool.h"
|
||||
#include "core/providers/armnn/armnn_execution_provider.h"
|
||||
|
||||
#include "armnn/ArmNN.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace armnn_ep {
|
||||
|
||||
typedef std::map<OpKernel*, armnn::NetworkId>::iterator PoolLayersIterator;
|
||||
|
||||
template <typename T, typename PoolType>
|
||||
class Pool final : public onnxruntime::Pool<T, PoolType> {
|
||||
public:
|
||||
explicit Pool(const OpKernelInfo& info) : onnxruntime::Pool<T, PoolType>(info) {
|
||||
provider_ = (const_cast<ArmNNExecutionProvider*>(
|
||||
dynamic_cast<const ArmNNExecutionProvider*>(info.GetExecutionProvider())));
|
||||
}
|
||||
|
||||
~Pool() {
|
||||
poolLayers.erase(this);
|
||||
}
|
||||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
|
||||
static armnn::IRuntimePtr initRuntime(){
|
||||
if (Pool::run)
|
||||
return std::move(Pool::run);
|
||||
armnn::IRuntime::CreationOptions options;
|
||||
return std::move(armnn::IRuntime::Create(options));
|
||||
}
|
||||
|
||||
private:
|
||||
static thread_local std::map<OpKernel*, armnn::NetworkId> poolLayers;
|
||||
ArmNNExecutionProvider* provider_;
|
||||
static armnn::IRuntimePtr run;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class MaxPoolV8 final : public onnxruntime::MaxPoolV8 {
|
||||
public:
|
||||
explicit MaxPoolV8(const OpKernelInfo& info) : onnxruntime::MaxPoolV8(info) {
|
||||
provider_ = (const_cast<ArmNNExecutionProvider*>(
|
||||
dynamic_cast<const ArmNNExecutionProvider*>(info.GetExecutionProvider())));
|
||||
}
|
||||
|
||||
~MaxPoolV8() {
|
||||
maxPoolLayers.erase(this);
|
||||
}
|
||||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
|
||||
static armnn::IRuntimePtr initRuntime(){
|
||||
if (MaxPoolV8::run)
|
||||
return std::move(MaxPoolV8::run);
|
||||
armnn::IRuntime::CreationOptions options;
|
||||
return std::move(armnn::IRuntime::Create(options));
|
||||
}
|
||||
|
||||
private:
|
||||
static thread_local std::map<OpKernel*, armnn::NetworkId> maxPoolLayers;
|
||||
ArmNNExecutionProvider* provider_;
|
||||
static armnn::IRuntimePtr run;
|
||||
};
|
||||
|
||||
} // namespace armnn_ep
|
||||
} // namespace onnxruntime
|
||||
1
onnxruntime/core/providers/armnn/symbols.txt
Executable file
1
onnxruntime/core/providers/armnn/symbols.txt
Executable file
|
|
@ -0,0 +1 @@
|
|||
OrtSessionOptionsAppendExecutionProvider_ArmNN
|
||||
|
|
@ -37,7 +37,7 @@ void usage() {
|
|||
"\t-v: verbose\n"
|
||||
"\t-n [test_case_name]: Specifies a single test case to run.\n"
|
||||
"\t-e [EXECUTION_PROVIDER]: EXECUTION_PROVIDER could be 'cpu', 'cuda', 'dnnl', 'tensorrt', 'ngraph', "
|
||||
"'openvino', 'nuphar', 'migraphx' or 'acl'. "
|
||||
"'openvino', 'nuphar', 'migraphx', 'acl' or 'armnn'. "
|
||||
"Default: 'cpu'.\n"
|
||||
"\t-x: Use parallel executor, default (without -x): sequential executor.\n"
|
||||
"\t-d [device_id]: Specifies the device id for multi-device (e.g. GPU). The value should > 0\n"
|
||||
|
|
@ -101,6 +101,7 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
|
|||
bool enable_nnapi = false;
|
||||
bool enable_dml = false;
|
||||
bool enable_acl = false;
|
||||
bool enable_armnn = false;
|
||||
bool enable_migraphx = false;
|
||||
int device_id = 0;
|
||||
GraphOptimizationLevel graph_optimization_level = ORT_ENABLE_ALL;
|
||||
|
|
@ -168,9 +169,11 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
|
|||
enable_dml = true;
|
||||
} else if (!CompareCString(optarg, ORT_TSTR("acl"))) {
|
||||
enable_acl = true;
|
||||
} else if (!CompareCString(optarg, ORT_TSTR("armnn"))) {
|
||||
enable_armnn = true;
|
||||
} else if (!CompareCString(optarg, ORT_TSTR("migraphx"))) {
|
||||
enable_migraphx = true;
|
||||
}else {
|
||||
} else {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -362,6 +365,14 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
|
|||
#else
|
||||
fprintf(stderr, "ACL is not supported in this build");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
if (enable_armnn) {
|
||||
#ifdef USE_ARMNN
|
||||
Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_ArmNN(sf, enable_cpu_mem_arena ? 1 : 0));
|
||||
#else
|
||||
fprintf(stderr, "ArmNN is not supported in this build\n");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
if (enable_migraphx) {
|
||||
|
|
|
|||
|
|
@ -98,6 +98,8 @@ namespace perftest {
|
|||
test_config.machine_config.provider_type_name = onnxruntime::kDmlExecutionProvider;
|
||||
} else if (!CompareCString(optarg, ORT_TSTR("acl"))) {
|
||||
test_config.machine_config.provider_type_name = onnxruntime::kAclExecutionProvider;
|
||||
} else if (!CompareCString(optarg, ORT_TSTR("armnn"))) {
|
||||
test_config.machine_config.provider_type_name = onnxruntime::kArmNNExecutionProvider;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,13 @@ OnnxRuntimeTestSession::OnnxRuntimeTestSession(Ort::Env& env, std::random_device
|
|||
performance_test_config.run_config.enable_cpu_mem_arena ? 1 : 0));
|
||||
#else
|
||||
ORT_THROW("Acl is not supported in this build\n");
|
||||
#endif
|
||||
} else if (provider_name == onnxruntime::kArmNNExecutionProvider) {
|
||||
#ifdef USE_ARMNN
|
||||
Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_ArmNN(session_options,
|
||||
performance_test_config.run_config.enable_cpu_mem_arena ? 1 : 0));
|
||||
#else
|
||||
ORT_THROW("ArmNN is not supported in this build\n");
|
||||
#endif
|
||||
} else if (provider_name == onnxruntime::kMIGraphXExecutionProvider) {
|
||||
#ifdef USE_MIGRAPHX
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ static void MaxPool_8_WithIndexTest(bool has_index, int64_t storage_order = 0) {
|
|||
storage_order == 0 ? test.AddOutput<int64_t>("Indices", expected_dims, expected_indices_row)
|
||||
: test.AddOutput<int64_t>("Indices", expected_dims, expected_indices_col);
|
||||
}
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kDnnlExecutionProvider, kTensorrtExecutionProvider, kAclExecutionProvider});
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kDnnlExecutionProvider, kTensorrtExecutionProvider, kAclExecutionProvider, kArmNNExecutionProvider});
|
||||
}
|
||||
|
||||
TEST(PoolTest, MaxPool_8_With_Index) {
|
||||
|
|
|
|||
|
|
@ -712,6 +712,7 @@ void OpTester::Run(
|
|||
kOpenVINOExecutionProvider,
|
||||
kDmlExecutionProvider,
|
||||
kAclExecutionProvider,
|
||||
kArmNNExecutionProvider,
|
||||
};
|
||||
|
||||
bool has_run = false;
|
||||
|
|
@ -778,6 +779,8 @@ void OpTester::Run(
|
|||
execution_provider = DefaultRknpuExecutionProvider();
|
||||
else if (provider_type == onnxruntime::kAclExecutionProvider)
|
||||
execution_provider = DefaultAclExecutionProvider();
|
||||
else if (provider_type == onnxruntime::kArmNNExecutionProvider)
|
||||
execution_provider = DefaultArmNNExecutionProvider();
|
||||
// skip if execution provider is disabled
|
||||
if (execution_provider == nullptr)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_Rknpu(
|
|||
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_Tensorrt(int device_id);
|
||||
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_MIGraphX(int device_id);
|
||||
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_ACL(int use_arena);
|
||||
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_ArmNN(int use_arena);
|
||||
|
||||
namespace test {
|
||||
|
||||
|
|
@ -113,5 +114,14 @@ std::unique_ptr<IExecutionProvider> DefaultAclExecutionProvider(bool enable_aren
|
|||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<IExecutionProvider> DefaultArmNNExecutionProvider(bool enable_arena){
|
||||
#ifdef USE_ARMNN
|
||||
return CreateExecutionProviderFactory_ArmNN(enable_arena)->CreateProvider();
|
||||
#else
|
||||
ORT_UNUSED_PARAMETER(enable_arena);
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace onnxruntime
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ std::unique_ptr<IExecutionProvider> DefaultOpenVINOExecutionProvider();
|
|||
std::unique_ptr<IExecutionProvider> DefaultNnapiExecutionProvider();
|
||||
std::unique_ptr<IExecutionProvider> DefaultRknpuExecutionProvider();
|
||||
std::unique_ptr<IExecutionProvider> DefaultAclExecutionProvider(bool enable_arena = true);
|
||||
std::unique_ptr<IExecutionProvider> DefaultArmNNExecutionProvider(bool enable_arena = true);
|
||||
|
||||
} // namespace test
|
||||
} // namespace onnxruntime
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
#ifdef USE_ACL
|
||||
#include "core/providers/acl/acl_provider_factory.h"
|
||||
#endif
|
||||
#ifdef USE_ARMNN
|
||||
#include "core/providers/armnn/armnn_provider_factory.h"
|
||||
#endif
|
||||
#ifdef USE_MIGRAPHX
|
||||
#include "core/providers/migraphx/migraphx_provider_factory.h"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -333,6 +333,12 @@ def parse_arguments():
|
|||
"--use_acl", nargs="?", const="ACL_1905",
|
||||
choices=["ACL_1902", "ACL_1905", "ACL_1908"],
|
||||
help="Build with ACL for ARM architectures.")
|
||||
parser.add_argument(
|
||||
"--use_armnn", action='store_true',
|
||||
help="Enable ArmNN Execution Provider.")
|
||||
parser.add_argument(
|
||||
"--armnn_relu", action='store_true',
|
||||
help="Use the Relu operator implementation from the ArmNN EP.")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
|
|
@ -523,7 +529,7 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home,
|
|||
"OFF" if args.skip_winml_tests else "ON"),
|
||||
"-Donnxruntime_GENERATE_TEST_REPORTS=ON",
|
||||
"-Donnxruntime_DEV_MODE=" + (
|
||||
"OFF" if args.android or args.use_acl or
|
||||
"OFF" if args.android or args.use_acl or args.use_armnn or
|
||||
(args.ios and is_macOS()) else "ON"),
|
||||
"-DPYTHON_EXECUTABLE=" + sys.executable,
|
||||
"-Donnxruntime_USE_CUDA=" + ("ON" if args.use_cuda else "OFF"),
|
||||
|
|
@ -612,6 +618,10 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home,
|
|||
"ON" if args.use_acl == "ACL_1905" else "OFF"),
|
||||
"-Donnxruntime_USE_ACL_1908=" + (
|
||||
"ON" if args.use_acl == "ACL_1908" else "OFF"),
|
||||
"-Donnxruntime_USE_ARMNN=" + (
|
||||
"ON" if args.use_armnn else "OFF"),
|
||||
"-Donnxruntime_ARMNN_RELU_USE_CPU=" + (
|
||||
"OFF" if args.armnn_relu else "ON"),
|
||||
# Training related flags
|
||||
"-Donnxruntime_ENABLE_NVTX_PROFILE=" + (
|
||||
"ON" if args.enable_nvtx_profile else "OFF"),
|
||||
|
|
@ -1659,7 +1669,7 @@ def main():
|
|||
"Only Windows ARM(64) cross-compiled builds supported "
|
||||
"currently through this script")
|
||||
install_ubuntu_deps(args)
|
||||
if not is_docker():
|
||||
if not is_docker() and not args.use_armnn:
|
||||
install_python_deps()
|
||||
if args.enable_pybind and is_windows():
|
||||
install_python_deps(args.numpy_version)
|
||||
|
|
|
|||
Loading…
Reference in a new issue