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:
Andrews548 2020-04-16 13:14:37 +03:00 committed by GitHub
parent c91527235a
commit 93b957a55a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 167 additions and 105 deletions

View file

@ -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:
```

View file

@ -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()

View file

@ -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));

View file

@ -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() {

View file

@ -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();

View file

@ -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

View file

@ -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;
};

View 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