mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-10 17:37:14 +00:00
Acl improvements (#3463)
* Fixed cornercases for acl ep gemm implementation by setting fully connected as the main layer * Introduced versioned build for the acl ep. ACL versions supported are 1902, 1905 and 1908 * Added convolution-activation fusion optimization for acl ep. We see improvements of 12% for mobilenetv2 and 4% for resnet50 Co-authored-by: Andrei-Alexandru <andrei-alexandru.avram@nxp.com>
This commit is contained in:
parent
c91527235a
commit
93b957a55a
8 changed files with 167 additions and 105 deletions
2
BUILD.md
2
BUILD.md
|
|
@ -403,6 +403,8 @@ alias cmake="/usr/bin/cmake -DCMAKE_TOOLCHAIN_FILE=$OECORE_NATIVE_SYSROOT/usr/sh
|
|||
```
|
||||
cmake ../onnxruntime-arm-upstream/cmake -DONNX_CUSTOM_PROTOC_EXECUTABLE=/usr/bin/protoc -Donnxruntime_RUN_ONNX_TESTS=OFF -Donnxruntime_GENERATE_TEST_REPORTS=ON -Donnxruntime_DEV_MODE=ON -DPYTHON_EXECUTABLE=/usr/bin/python3 -Donnxruntime_USE_CUDA=OFF -Donnxruntime_USE_NSYNC=OFF -Donnxruntime_CUDNN_HOME= -Donnxruntime_USE_JEMALLOC=OFF -Donnxruntime_ENABLE_PYTHON=OFF -Donnxruntime_BUILD_CSHARP=OFF -Donnxruntime_BUILD_SHARED_LIB=ON -Donnxruntime_USE_EIGEN_FOR_BLAS=ON -Donnxruntime_USE_OPENBLAS=OFF -Donnxruntime_USE_ACL=ON -Donnxruntime_USE_DNNL=OFF -Donnxruntime_USE_MKLML=OFF -Donnxruntime_USE_OPENMP=ON -Donnxruntime_USE_TVM=OFF -Donnxruntime_USE_LLVM=OFF -Donnxruntime_ENABLE_MICROSOFT_INTERNAL=OFF -Donnxruntime_USE_BRAINSLICE=OFF -Donnxruntime_USE_NUPHAR=OFF -Donnxruntime_USE_EIGEN_THREADPOOL=OFF -Donnxruntime_BUILD_UNIT_TESTS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
```
|
||||
The ```-Donnxruntime_USE_ACL=ON``` option will use, by default, the 19.05 version of the Arm Compute Library. To set the right version you can use:
|
||||
```-Donnxruntime_USE_ACL_1902=ON```, ```-Donnxruntime_USE_ACL_1905=ON``` or ```-Donnxruntime_USE_ACL_1908=ON```;
|
||||
|
||||
2. Build ONNX Runtime library, test and performance application:
|
||||
```
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ option(onnxruntime_DEBUG_NODE_INPUTS_OUTPUTS "Dump node input shapes and output
|
|||
option(onnxruntime_USE_DML "Build with DirectML support" OFF)
|
||||
option(onnxruntime_USE_WINML "Build with WinML support" OFF)
|
||||
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_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.
|
||||
|
|
@ -472,7 +475,18 @@ endfunction()
|
|||
set(onnxruntime_EXTERNAL_DEPENDENCIES onnx_proto)
|
||||
|
||||
# ACL
|
||||
if (onnxruntime_USE_ACL)
|
||||
if (onnxruntime_USE_ACL OR onnxruntime_USE_ACL_1902 OR onnxruntime_USE_ACL_1905 OR onnxruntime_USE_ACL_1908)
|
||||
set(onnxruntime_USE_ACL ON)
|
||||
if(onnxruntime_USE_ACL_1902)
|
||||
add_definitions(-DACL_1902=1)
|
||||
else()
|
||||
if(onnxruntime_USE_ACL_1908)
|
||||
add_definitions(-DACL_1908=1)
|
||||
else()
|
||||
add_definitions(-DACL_1905=1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(APPEND onnxruntime_EXTERNAL_LIBRARIES arm_compute acl arm_compute_graph arm_compute_core)
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -122,7 +122,10 @@ std::vector<std::unique_ptr<GraphTransformer>> GenerateTransformers(TransformerL
|
|||
|
||||
#ifndef DISABLE_CONTRIB_OPS
|
||||
transformers.emplace_back(onnxruntime::make_unique<GemmActivationFusion>(cpu_execution_providers));
|
||||
transformers.emplace_back(onnxruntime::make_unique<ConvActivationFusion>(cpu_execution_providers));
|
||||
|
||||
std::unordered_set<std::string> cpu_acl_execution_providers = {onnxruntime::kCpuExecutionProvider, onnxruntime::kAclExecutionProvider};
|
||||
|
||||
transformers.emplace_back(onnxruntime::make_unique<ConvActivationFusion>(cpu_acl_execution_providers));
|
||||
|
||||
std::unordered_set<std::string> cpu_cuda_execution_providers = {onnxruntime::kCpuExecutionProvider, onnxruntime::kCudaExecutionProvider};
|
||||
transformers.emplace_back(onnxruntime::make_unique<GeluFusion>(cpu_cuda_execution_providers));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Copyright (c) 2019-2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "acl_execution_provider.h"
|
||||
|
|
@ -37,6 +37,8 @@ class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kAclExecutionProvider, kOn
|
|||
// Opset 10
|
||||
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kAclExecutionProvider, kOnnxDomain, 10, 10, float, AveragePool);
|
||||
|
||||
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kAclExecutionProvider, kMSDomain, 1, float, FusedConv);
|
||||
|
||||
static void RegisterACLKernels(KernelRegistry& kernel_registry) {
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kAclExecutionProvider, kOnnxDomain, 6, Relu)>());
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kAclExecutionProvider, kOnnxDomain, 7, 9, Gemm)>());
|
||||
|
|
@ -58,6 +60,8 @@ static void RegisterACLKernels(KernelRegistry& kernel_registry) {
|
|||
|
||||
// Opset 10
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kAclExecutionProvider, kOnnxDomain, 10, 10, float, AveragePool)>());
|
||||
|
||||
kernel_registry.Register(BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kAclExecutionProvider, kMSDomain, 1, float, FusedConv)>());
|
||||
}
|
||||
|
||||
std::shared_ptr<KernelRegistry> GetAclKernelRegistry() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Copyright (c) 2019-2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
|
@ -19,12 +19,26 @@
|
|||
#include "arm_compute/runtime/MemoryManagerOnDemand.h"
|
||||
|
||||
//NEON
|
||||
#include "arm_compute/runtime/NEON/functions/NEGEMM.h"
|
||||
#include "arm_compute/runtime/NEON/functions/NETranspose.h"
|
||||
#include "arm_compute/runtime/NEON/functions/NEFullyConnectedLayer.h"
|
||||
|
||||
#undef GEMM_ACL
|
||||
#define CACHE_TRANSPOSED_DATA
|
||||
template <typename T>
|
||||
void importDataFromTensor(arm_compute::Tensor* tensor, T* data){
|
||||
|
||||
arm_compute::Window aclInpuWindow;
|
||||
aclInpuWindow.use_tensor_dimensions(tensor->info()->tensor_shape());
|
||||
|
||||
arm_compute::Iterator aclInputIt(tensor, aclInpuWindow);
|
||||
const unsigned int aclWidth = tensor->info()->dimension(0);
|
||||
const unsigned int aclHeight = tensor->info()->dimension(1);
|
||||
|
||||
// copy input tensor into the larger buffer
|
||||
arm_compute::execute_window_loop(
|
||||
aclInpuWindow,
|
||||
[&](const arm_compute::Coordinates& co) {
|
||||
data[co.z() * (aclWidth * aclHeight) + co.y() * aclWidth + co.x()] = *reinterpret_cast<float*>(aclInputIt.ptr());
|
||||
},
|
||||
aclInputIt);
|
||||
}
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace acl {
|
||||
|
|
@ -53,27 +67,51 @@ class Gemm : public onnxruntime::Gemm<T> {
|
|||
}
|
||||
|
||||
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);
|
||||
const auto A = context->Input<Tensor>(0);
|
||||
const auto B = context->Input<Tensor>(1);
|
||||
const auto C = context->Input<Tensor>(2);
|
||||
|
||||
GemmHelper helper(X->Shape(), trans_A_ != CblasNoTrans, W->Shape(), trans_B_ != CblasNoTrans, B->Shape());
|
||||
GemmHelper helper(A->Shape(), trans_A_ != CblasNoTrans, B->Shape(), trans_B_ != CblasNoTrans, C->Shape());
|
||||
|
||||
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}));
|
||||
auto D = context->Output(0, TensorShape({M, N}));
|
||||
|
||||
bool FC = ((alpha_ == 1 && beta_ == 1) || (alpha_ == 1 && beta_ == 0));
|
||||
bool FC = alpha_ == 1 && (beta_ == 1 || beta_ == 0);
|
||||
bool useC = C != nullptr && beta_ != 0;
|
||||
|
||||
if(trans_A_ == CblasTrans){ // transpose input
|
||||
return onnxruntime::Gemm<T>::Compute(context);
|
||||
}
|
||||
|
||||
arm_compute::TensorShape cShape = ACLTensorShape(C->Shape());
|
||||
if(useC &&
|
||||
(cShape.num_dimensions() > 2 ||
|
||||
(cShape.num_dimensions() == 2 && cShape[0] > 1 && cShape[1] > 1))) { // Multi-dimensional Bias
|
||||
return onnxruntime::Gemm<T>::Compute(context);
|
||||
}
|
||||
|
||||
if(useC && (cShape.num_dimensions() == 1 && cShape[0] != (long unsigned int) N)) { // Broadcast
|
||||
return onnxruntime::Gemm<T>::Compute(context);
|
||||
}
|
||||
|
||||
if(useC && cShape.num_dimensions() == 2){
|
||||
if((cShape[0] == 1 && cShape[1] != (long unsigned int) N) ||
|
||||
(cShape[1] == 1 && cShape[0] != (long unsigned int) N)) {
|
||||
return onnxruntime::Gemm<T>::Compute(context);
|
||||
}
|
||||
cShape = arm_compute::TensorShape(1, N);
|
||||
}
|
||||
|
||||
int64_t K = helper.K();
|
||||
LOGS_DEFAULT(VERBOSE) << "Gemm ACL:" << 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 (A) LOGS_DEFAULT(VERBOSE) << "A " << A->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;
|
||||
if (C) LOGS_DEFAULT(VERBOSE) << "C " << C->Shape().ToString().c_str() << std::endl;
|
||||
LOGS_DEFAULT(VERBOSE) << "D " << D->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;
|
||||
|
|
@ -89,51 +127,22 @@ class Gemm : public onnxruntime::Gemm<T> {
|
|||
tGEMM.c = std::make_shared<arm_compute::Tensor>();
|
||||
tGEMM.d = std::make_shared<arm_compute::Tensor>();
|
||||
|
||||
tGEMM.a->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(X->Shape()), arm_compute::Format::F32));
|
||||
tGEMM.c->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(B->Shape()), arm_compute::Format::F32));
|
||||
tGEMM.a->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(A->Shape()), arm_compute::Format::F32));
|
||||
tGEMM.b->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(B->Shape()), arm_compute::Format::F32));
|
||||
tGEMM.c->allocator()->init(arm_compute::TensorInfo(cShape, arm_compute::Format::F32));
|
||||
// dimensions are stored in the opposite order to ACL's
|
||||
tGEMM.d->allocator()->init(arm_compute::TensorInfo(arm_compute::TensorShape(N, M), tGEMM.a->info()->format()));
|
||||
|
||||
// transpose
|
||||
if (!FC && trans_B_ == CblasTrans) {
|
||||
auto trans_layer = std::make_shared<arm_compute::NETranspose>();
|
||||
tGEMM.b->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32));
|
||||
|
||||
arm_compute::Tensor tmp;
|
||||
tmp.allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32));
|
||||
|
||||
trans_layer->configure(&tmp, tGEMM.b.get());
|
||||
|
||||
const T* b_data = W->template Data<T>();
|
||||
ACLImportMemory(tmp.allocator(), (void*)b_data, W->Shape().Size() * 4);
|
||||
|
||||
tGEMM.b->allocator()->allocate();
|
||||
|
||||
trans_layer->run();
|
||||
} else {
|
||||
tGEMM.b->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32));
|
||||
}
|
||||
|
||||
tGEMM.d->allocator()->init(arm_compute::TensorInfo(arm_compute::TensorShape(N, M), arm_compute::Format::F32));
|
||||
|
||||
tGEMM.mm_layer = ACLCreateMemoryManager();
|
||||
|
||||
if(FC) {
|
||||
auto layer = std::make_shared<arm_compute::NEFullyConnectedLayer>(tGEMM.mm_layer);
|
||||
layer->configure(tGEMM.a.get(), tGEMM.b.get(), (B != nullptr && beta_ != 0) ? tGEMM.c.get() : nullptr, tGEMM.d.get());
|
||||
arm_compute::FullyConnectedLayerInfo fc_info;
|
||||
fc_info.transpose_weights = trans_B_ == CblasTrans;
|
||||
layer->configure(tGEMM.a.get(), tGEMM.b.get(), useC ? tGEMM.c.get() : nullptr, tGEMM.d.get(), fc_info);
|
||||
tGEMM.layer = std::move(layer);
|
||||
} else {
|
||||
#ifdef GEMM_ACL
|
||||
auto layer = std::make_shared<arm_compute::NEGEMM>(tGEMM.mm_layer);
|
||||
layer->configure(tGEMM.a.get(), tGEMM.b.get(), (B != nullptr && beta_ != 0) ? tGEMM.c.get() : nullptr, tGEMM.d.get(), alpha_, beta_, arm_compute::GEMMInfo());
|
||||
tGEMM.layer = std::move(layer);
|
||||
#else
|
||||
return onnxruntime::Gemm<T>::Compute(context);
|
||||
#endif
|
||||
}
|
||||
|
||||
// non-transpose
|
||||
if (FC || trans_B_ != CblasTrans) {
|
||||
const T* b_data = W->template Data<T>();
|
||||
ACLImportMemory(tGEMM.b->allocator(), (void*)b_data, W->Shape().Size() * 4);
|
||||
}
|
||||
|
||||
std::pair<GEMMLayersIterator, bool> ret;
|
||||
|
|
@ -142,41 +151,24 @@ class Gemm : public onnxruntime::Gemm<T> {
|
|||
} else {
|
||||
//TODO: valildate shapes
|
||||
pGEMM = &it->second;
|
||||
|
||||
// transpose
|
||||
if (!FC && trans_B_ == CblasTrans) {
|
||||
#ifndef CACHE_TRANSPOSED_DATA
|
||||
auto trans_layer = std::make_shared<arm_compute::NETranspose>();
|
||||
pGEMM->b->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32));
|
||||
|
||||
arm_compute::Tensor tmp;
|
||||
tmp.allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32));
|
||||
|
||||
trans_layer->configure(&tmp, pGEMM->b.get());
|
||||
|
||||
const T* b_data = W->template Data<T>();
|
||||
ACLImportMemory(tmp.allocator(), (void*)b_data, W->Shape().Size() * 4);
|
||||
|
||||
// allocate memory for b
|
||||
pGEMM->b->allocator()->allocate();
|
||||
|
||||
trans_layer->run();
|
||||
#else
|
||||
LOGS_DEFAULT(VERBOSE) << "Reuse transposed data" << std::endl;
|
||||
#endif
|
||||
} else {
|
||||
const T* b_data = W->template Data<T>();
|
||||
ACLImportMemory(pGEMM->b->allocator(), (void*)b_data, W->Shape().Size() * 4);
|
||||
}
|
||||
}
|
||||
|
||||
const T* a_data = X->template Data<T>();
|
||||
const T* c_data = B->template Data<T>();
|
||||
const T* d_data = Y->template Data<T>();
|
||||
const T* a_data = A->template Data<T>();
|
||||
const T* b_data = B->template Data<T>();
|
||||
T* d_data = D->template MutableData<T>();
|
||||
|
||||
ACLImportMemory(pGEMM->a->allocator(), (void*)a_data, X->Shape().Size() * 4);
|
||||
ACLImportMemory(pGEMM->c->allocator(), (void*)c_data, B->Shape().Size() * 4);
|
||||
ACLImportMemory(pGEMM->d->allocator(), (void*)d_data, Y->Shape().Size() * 4);
|
||||
ACLImportMemory(pGEMM->a->allocator(), (void*)a_data, A->Shape().Size() * 4);
|
||||
ACLImportMemory(pGEMM->b->allocator(), (void*)b_data, B->Shape().Size() * 4);
|
||||
if(useC){
|
||||
const T* c_data = C->template Data<T>();
|
||||
ACLImportMemory(pGEMM->c->allocator(), (void*)c_data, C->Shape().Size() * 4);
|
||||
}
|
||||
|
||||
if(D->Shape().Size() != 0 && pGEMM->d->info()->has_padding() ){
|
||||
pGEMM->d.get()->allocator()->allocate();
|
||||
} else {
|
||||
ACLImportMemory(pGEMM->d->allocator(), (void*)d_data, D->Shape().Size() * 4);
|
||||
}
|
||||
|
||||
ACLPrintTensorShape("a", *pGEMM->a);
|
||||
ACLPrintTensorShape("b", *pGEMM->b);
|
||||
|
|
@ -188,13 +180,12 @@ class Gemm : public onnxruntime::Gemm<T> {
|
|||
pGEMM->layer->run();
|
||||
pGEMM->mm_layer->clear();
|
||||
|
||||
if(D->Shape().Size() != 0 && pGEMM->d->info()->has_padding() ){
|
||||
importDataFromTensor<T>(pGEMM->d.get(), d_data);
|
||||
}
|
||||
|
||||
pGEMM->a->allocator()->free();
|
||||
#ifdef CACHE_TRANSPOSED_DATA
|
||||
if (trans_B_ != CblasTrans)
|
||||
pGEMM->b->allocator()->free();
|
||||
#else
|
||||
pGEMM->b->allocator()->free();
|
||||
#endif
|
||||
pGEMM->c->allocator()->free();
|
||||
pGEMM->d->allocator()->free();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Copyright (c) 2019-2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
@ -26,7 +26,8 @@
|
|||
|
||||
#ifdef ACL_1902
|
||||
#include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayer3x3Kernel.h"
|
||||
#else
|
||||
#endif
|
||||
#if defined(ACL_1905) || defined(ACL_1908)
|
||||
#include "arm_compute/runtime/NEON/functions/assembly/NEDepthwiseConvolutionAssemblyDispatch.h"
|
||||
#endif
|
||||
|
||||
|
|
@ -109,20 +110,20 @@ Status Conv<T>::Compute(OpKernelContext* context) const {
|
|||
arm_compute::ActivationLayerInfo::ActivationFunction acl_activ_func;
|
||||
bool acl_activ_enabled = false;
|
||||
|
||||
if (conv_attrs_.activation == "Relu") {
|
||||
if (activation_type == "Relu") {
|
||||
acl_activ_func = arm_compute::ActivationLayerInfo::ActivationFunction::RELU;
|
||||
acl_activ_enabled = true;
|
||||
} else if (conv_attrs_.activation == "LeakyRelu") {
|
||||
} else if (activation_type == "LeakyRelu") {
|
||||
acl_activ_func = arm_compute::ActivationLayerInfo::ActivationFunction::LEAKY_RELU;
|
||||
acl_activ_enabled = true;
|
||||
} else if (conv_attrs_.activation == "Tanh") {
|
||||
} else if (activation_type == "Tanh") {
|
||||
acl_activ_func = arm_compute::ActivationLayerInfo::ActivationFunction::TANH;
|
||||
acl_activ_enabled = true;
|
||||
} else if (conv_attrs_.activation == "Sigmoid") {
|
||||
} else if (activation_type == "Sigmoid") {
|
||||
acl_activ_func = arm_compute::ActivationLayerInfo::ActivationFunction::LOGISTIC;
|
||||
acl_activ_enabled = true;
|
||||
} else if (!conv_attrs_.activation.empty()) {
|
||||
ORT_NOT_IMPLEMENTED("Not implemented fused activation: ", conv_attrs_.activation);
|
||||
} else if (!activation_type.empty()) {
|
||||
ORT_NOT_IMPLEMENTED("Not implemented fused activation: ", activation_type);
|
||||
}
|
||||
|
||||
if (it == Conv::convLayers.end()) {
|
||||
|
|
@ -195,7 +196,8 @@ Status Conv<T>::Compute(OpKernelContext* context) const {
|
|||
tconv.in->info()->data_type(),
|
||||
1 /* depth multiplier */,
|
||||
tconv.in->info()->data_layout());
|
||||
#else
|
||||
#endif
|
||||
#if defined(ACL_1905) || defined(ACL_1908)
|
||||
bool optimizable =
|
||||
arm_compute::NEDepthwiseConvolutionAssemblyDispatch::is_optimized_supported(tconv.in->info(),
|
||||
tconv.k->info(),
|
||||
|
|
@ -205,12 +207,18 @@ Status Conv<T>::Compute(OpKernelContext* context) const {
|
|||
#endif
|
||||
if(optimizable) {
|
||||
//optimized depthwise convolution
|
||||
#if defined(ACL_1902) || defined(ACL_1905)
|
||||
auto layer = std::make_shared<arm_compute::NEDepthwiseConvolutionLayer3x3>();
|
||||
#endif
|
||||
#ifdef ACL_1908
|
||||
auto layer = std::make_shared<arm_compute::NEDepthwiseConvolutionLayerOptimized>();
|
||||
#endif
|
||||
#ifdef ACL_1902
|
||||
layer->configure(tconv.in.get(), tconv.k.get(), (B != nullptr) ? tconv.b.get() : nullptr, tconv.out.get(),
|
||||
aclPadStride, 1 /* depth multiplier */,
|
||||
acl_activ_enabled ? arm_compute::ActivationLayerInfo(acl_activ_func, conv_attrs_.alpha) : arm_compute::ActivationLayerInfo());
|
||||
#else
|
||||
#endif
|
||||
#if defined(ACL_1905) || defined(ACL_1908)
|
||||
layer->configure(tconv.in.get(), tconv.k.get(), (B != nullptr) ? tconv.b.get() : nullptr, tconv.out.get(),
|
||||
aclPadStride, 1 /* depth multiplier */,
|
||||
acl_activ_enabled ? arm_compute::ActivationLayerInfo(acl_activ_func, conv_attrs_.alpha) : arm_compute::ActivationLayerInfo(),
|
||||
|
|
@ -225,7 +233,7 @@ Status Conv<T>::Compute(OpKernelContext* context) const {
|
|||
ret = Conv::convLayers.insert(std::pair<OpKernel*, ACLNEConv>((OpKernel*)this, tconv));
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
#endif //DEPTHWISE_CPU
|
||||
} else {
|
||||
if(tconv.k->info()->tensor_shape()[0] == 1 && tconv.k->info()->tensor_shape()[1] == 1) {
|
||||
//pointwise convolution
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Copyright (c) 2019-2020, NXP Semiconductor, Inc. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
|
@ -36,7 +36,7 @@ typedef struct
|
|||
typedef std::map<OpKernel*, ACLNEConv>::iterator ConvLayersIterator;
|
||||
|
||||
template <typename T>
|
||||
class Conv final : public onnxruntime::Conv<T> {
|
||||
class Conv : public onnxruntime::Conv<T> {
|
||||
public:
|
||||
explicit Conv(const OpKernelInfo& info) : onnxruntime::Conv<T>(info), conv_attrs_(info) {
|
||||
provider_ = (const_cast<ACLExecutionProvider*>(
|
||||
|
|
@ -49,10 +49,11 @@ class Conv final : public onnxruntime::Conv<T> {
|
|||
|
||||
Status Compute(OpKernelContext* context) const override;
|
||||
|
||||
private:
|
||||
protected:
|
||||
static thread_local std::map<OpKernel*, ACLNEConv> convLayers;
|
||||
ConvAttributes conv_attrs_;
|
||||
ACLExecutionProvider* provider_;
|
||||
std::string activation_type;
|
||||
|
||||
arm_compute::TensorShape ACLReshapeWeightsDepthwise(arm_compute::Tensor* kernel) const;
|
||||
};
|
||||
|
|
|
|||
39
onnxruntime/core/providers/acl/nn/fused_conv.cc
Normal file
39
onnxruntime/core/providers/acl/nn/fused_conv.cc
Normal 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.
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "core/providers/acl/nn/conv.h"
|
||||
#include "core/providers/acl/acl_common.h"
|
||||
#include "core/providers/acl/acl_fwd.h"
|
||||
#include "core/providers/acl/acl_execution_provider.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
namespace acl{
|
||||
|
||||
template <typename T>
|
||||
class FusedConv final : public acl::Conv<T> {
|
||||
public:
|
||||
explicit FusedConv(const OpKernelInfo& info) : acl::Conv<T>(info) {
|
||||
ORT_ENFORCE(info.GetAttr<std::string>("activation", &(this->activation_type)).IsOK());
|
||||
// printf("fused\n");
|
||||
}
|
||||
// Status Compute(OpKernelContext* context) const override;
|
||||
};
|
||||
|
||||
ONNX_OPERATOR_TYPED_KERNEL_EX(
|
||||
FusedConv,
|
||||
kMSDomain,
|
||||
1,
|
||||
float,
|
||||
kAclExecutionProvider,
|
||||
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
|
||||
FusedConv<float>);
|
||||
|
||||
} // namespace acl
|
||||
} // namespace onnxruntime
|
||||
Loading…
Reference in a new issue