MKL-DNN 1.0 (#2134)

* MKL-DNN 1.0

* changed libmkldnn version to 1
This commit is contained in:
Sreekanth Yalachigere 2019-10-15 12:06:34 -07:00 committed by George Wu
parent 6857bb8aba
commit 485c24b62d
31 changed files with 1146 additions and 2443 deletions

View file

@ -2,7 +2,7 @@ include (ExternalProject)
set(MKLDNN_URL https://github.com/intel/mkl-dnn.git)
# If MKLDNN_TAG is updated, check if MKLML_VERSION and platform.cmake.patch need to be updated.
set(MKLDNN_TAG v0.18.1)
set(MKLDNN_TAG v1.0.2)
set(MKLML_VERSION 2019.0.5.20190502)
if(WIN32)
@ -21,10 +21,10 @@ if(WIN32)
else()
set(MKLML_FILE_EXTENSION "tgz")
if (APPLE)
set(MKLDNN_SHARED_LIB libmkldnn.0.dylib)
set(MKLDNN_SHARED_LIB libmkldnn.1.dylib)
set(MKLML_OS_VERSION_STR "mac")
else()
set(MKLDNN_SHARED_LIB libmkldnn.so.0)
set(MKLDNN_SHARED_LIB libmkldnn.so.1)
set(MKLML_OS_VERSION_STR "lnx")
endif()
if(onnxruntime_USE_MKLML)
@ -62,7 +62,7 @@ if (onnxruntime_USE_MKLDNN)
endif()
set(MKLDNN_INCLUDE_DIR ${MKLDNN_INSTALL}/include)
set(MKLDNN_CMAKE_EXTRA_ARGS)
set(MKLDNN_PATCH_COMMAND1 git apply ${CMAKE_SOURCE_DIR}/patches/mkldnn/mem-patch.cmake.patch)
set(MKLDNN_PATCH_COMMAND git apply ${CMAKE_SOURCE_DIR}/patches/mkldnn/constexpr.patch)
# discard prior changes due to patching in mkldnn source to unblock incremental builds.
set(MKLDNN_PATCH_DISCARD_COMMAND cd ${MKLDNN_SOURCE} && git checkout -- .)
if(NOT onnxruntime_BUILD_FOR_NATIVE_MACHINE)
@ -75,7 +75,7 @@ if (onnxruntime_USE_MKLDNN)
PREFIX mkl-dnn
GIT_REPOSITORY ${MKLDNN_URL}
GIT_TAG ${MKLDNN_TAG}
PATCH_COMMAND ${MKLDNN_PATCH_DISCARD_COMMAND} COMMAND ${MKLDNN_PATCH_COMMAND1}
PATCH_COMMAND ${MKLDNN_PATCH_DISCARD_COMMAND} COMMAND ${MKLDNN_PATCH_COMMAND}
SOURCE_DIR ${MKLDNN_SOURCE}
CMAKE_ARGS -DMKLDNN_PRODUCT_BUILD_MODE=OFF -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${MKLDNN_INSTALL} -DMKLROOT=${MKML_DIR} ${MKLDNN_CMAKE_EXTRA_ARGS}
)

View file

@ -0,0 +1,43 @@
diff --git a/src/cpu/rnn/rnn_reorders.hpp b/src/cpu/rnn/rnn_reorders.hpp
index fbfedd01..28a6dc1d 100644
--- a/src/cpu/rnn/rnn_reorders.hpp
+++ b/src/cpu/rnn/rnn_reorders.hpp
@@ -128,7 +128,7 @@ struct rnn_weights_reorder_t : public cpu_primitive_t {
return status::success;
}
- format_tag_t itag_;
+ format_tag_t itag_ = mkldnn_format_tag_undef;
private:
void init_scratchpad() {
diff --git a/src/cpu/simple_concat.hpp b/src/cpu/simple_concat.hpp
index fe84eb14..f1b2bd17 100644
--- a/src/cpu/simple_concat.hpp
+++ b/src/cpu/simple_concat.hpp
@@ -98,9 +98,9 @@ struct simple_concat_t: public cpu_primitive_t {
return status::success;
}
- int perm_[MKLDNN_MAX_NDIMS];
- int iperm_[MKLDNN_MAX_NDIMS];
- dims_t blocks_;
+ int perm_[MKLDNN_MAX_NDIMS] {};
+ int iperm_[MKLDNN_MAX_NDIMS] {};
+ dims_t blocks_ {};
dim_t nelems_to_concat(const memory_desc_wrapper &data_d) const {
const int ndims = data_d.ndims();
diff --git a/src/cpu/simple_sum.hpp b/src/cpu/simple_sum.hpp
index 46a24810..c47c4a64 100644
--- a/src/cpu/simple_sum.hpp
+++ b/src/cpu/simple_sum.hpp
@@ -71,7 +71,7 @@ struct simple_sum_t: public cpu_primitive_t {
}
sum_bf16_params_t bf16_p_;
- dim_t block_size_, nelems_, blocks_number_, tail_;
+ dim_t block_size_ = 0, nelems_= 0, blocks_number_= 0, tail_= 0;
private:
const dim_t cacheline_size_ = 64; // bytes

View file

@ -1,107 +0,0 @@
---
src/cpu/jit_avx2_1x1_convolution.cpp | 6 +++---
src/cpu/jit_avx512_common_1x1_convolution.cpp | 9 ++++-----
src/cpu/jit_avx512_core_x8s8s32x_1x1_convolution.cpp | 6 ++++--
src/cpu/jit_uni_1x1_conv_utils.hpp | 3 ++-
4 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/src/cpu/jit_avx2_1x1_convolution.cpp b/src/cpu/jit_avx2_1x1_convolution.cpp
index 46362886..edb2b6fb 100644
--- a/src/cpu/jit_avx2_1x1_convolution.cpp
+++ b/src/cpu/jit_avx2_1x1_convolution.cpp
@@ -50,7 +50,7 @@ void jit_avx2_1x1_convolution_fwd_t::execute_forward() const {
const memory_desc_wrapper weights_d(pd()->weights_pd(0));
const auto &jcp = kernel_->jcp;
- auto rtus_space = scratchpad().get<data_t>(key_conv_rtus_space);
+ auto rtus_space = pd()->rtus_.reduce_src_?scratchpad().get<data_t>(key_conv_rtus_space):NULL;
const int work_amount = jcp.mb * jcp.ngroups * jcp.nb_bcast;
const int ndims = dst_d.ndims();
@@ -180,7 +180,7 @@ void jit_avx2_1x1_convolution_bwd_data_t::execute_backward_data() const {
const memory_desc_wrapper diff_src_d(pd()->diff_src_pd());
const auto &jcp = kernel_->jcp;
- auto rtus_space = scratchpad().get<data_t>(key_conv_rtus_space);
+ auto rtus_space = pd()->rtus_.reduce_src_?scratchpad().get<data_t>(key_conv_rtus_space):NULL;
// TODO (Roma): remove this restriction
assert(jcp.stride_w == 1 && jcp.stride_h == 1);
@@ -306,7 +306,7 @@ void jit_avx2_1x1_convolution_bwd_weights_t::execute_backward_weights() const {
const memory_desc_wrapper diff_bias_d(pd()->diff_weights_pd(1));
const auto &jcp = kernel_->jcp;
- auto rtus_space = scratchpad.get<data_t>(key_conv_rtus_space);
+ auto rtus_space = pd()->rtus_.reduce_src_?scratchpad.get<data_t>(key_conv_rtus_space):NULL;
data_t *diff_bias = pd()->wants_padded_bias()
? scratchpad.get<data_t>(key_conv_padded_bias) : diff_bias_in;
diff --git a/src/cpu/jit_avx512_common_1x1_convolution.cpp b/src/cpu/jit_avx512_common_1x1_convolution.cpp
index 6879cd91..6a32aa49 100644
--- a/src/cpu/jit_avx512_common_1x1_convolution.cpp
+++ b/src/cpu/jit_avx512_common_1x1_convolution.cpp
@@ -106,7 +106,7 @@ execute_forward_thr(const int ithr, const int nthr, const src_data_t *src,
const memory_desc_wrapper weights_d(pd()->weights_pd(0));
const auto &jcp = kernel_->jcp;
- auto rtus_space = scratchpad.get<src_data_t>(key_conv_rtus_space);
+ auto rtus_space = pd()->rtus_.reduce_src_?scratchpad.get<src_data_t>(key_conv_rtus_space):NULL;
const int ndims = src_d.ndims();
const int stride_h = (ndims == 3) ? 1 : pd()->desc()->strides[0];
@@ -301,9 +301,8 @@ void jit_avx512_common_1x1_convolution_bwd_data_t<diff_dst_type, wei_type,
const memory_desc_wrapper diff_src_d(pd()->diff_src_pd());
const auto &jcp = kernel_->jcp;
- auto rtus_space = scratchpad().template get<diff_src_data_t>(
- key_conv_rtus_space);
-
+ auto rtus_space = pd()->rtus_.reduce_src_? scratchpad().template get<diff_src_data_t>(key_conv_rtus_space): NULL;
+
const int ndims = diff_src_d.ndims();
// TODO (Roma): remove this restriction
@@ -470,7 +469,7 @@ void jit_avx512_common_1x1_convolution_bwd_weights_t::execute_backward_weights()
const auto scratchpad = this->scratchpad();
- auto rtus_space = scratchpad.get<data_t>(key_conv_rtus_space);
+ auto rtus_space = pd()->rtus_.reduce_src_?scratchpad.get<data_t>(key_conv_rtus_space):NULL;
data_t *diff_bias = pd()->wants_padded_bias()
? scratchpad.get<data_t>(key_conv_padded_bias) : diff_bias_in;
auto wei_reduction = scratchpad.get<data_t>(key_conv_wei_reduction);
diff --git a/src/cpu/jit_avx512_core_x8s8s32x_1x1_convolution.cpp b/src/cpu/jit_avx512_core_x8s8s32x_1x1_convolution.cpp
index de303cd2..ec0c54e7 100644
--- a/src/cpu/jit_avx512_core_x8s8s32x_1x1_convolution.cpp
+++ b/src/cpu/jit_avx512_core_x8s8s32x_1x1_convolution.cpp
@@ -100,8 +100,10 @@ void jit_avx512_core_x8s8s32x_1x1_convolution_fwd_t<src_type, dst_type>
? types::data_type_size(pd()->desc()->bias_desc.data_type) : 0;
const auto &jcp = kernel_->jcp;
- auto rtus_space = scratchpad.get<src_data_t>(key_conv_rtus_space);
- auto local_scales = scratchpad.get<float>(key_conv_adjusted_scales);
+
+ auto rtus_space = pd()->rtus_.reduce_src_?scratchpad.get<src_data_t>(key_conv_rtus_space):NULL;
+
+ auto local_scales = scratchpad.get<float>(key_conv_adjusted_scales);
const int work_amount = jcp.mb * jcp.ngroups * jcp.nb_bcast;
diff --git a/src/cpu/jit_uni_1x1_conv_utils.hpp b/src/cpu/jit_uni_1x1_conv_utils.hpp
index a3ed769a..5a0e0635 100644
--- a/src/cpu/jit_uni_1x1_conv_utils.hpp
+++ b/src/cpu/jit_uni_1x1_conv_utils.hpp
@@ -94,7 +94,8 @@ inline void rtus_prepare(conv_pd_t *self, const convolution_desc_t *&conv_d,
template <typename conv_pd_t>
inline void rtus_prepare_space_info(conv_pd_t *self,
memory_tracking::registrar_t &scratchpad) {
- const auto &jcp = self->jcp_;
+ if (!self->rtus_.reduce_src_) return;
+ const auto &jcp = self->jcp_;
const int max_threads = mkldnn_get_max_threads();
const size_t factor = utils::pick_by_prop_kind(self->desc()->prop_kind,
--
2.17.0.windows.1

View file

@ -1,195 +0,0 @@
// Copyright(C) 2018 Intel Corporation
// Licensed under the MIT License
#ifdef _WIN32
#pragma warning(disable : 4244)
#endif
#include "core/providers/mkldnn/mkldnn_common.h"
#include "core/providers/mkldnn/activation/activations.h"
#include "core/providers/mkldnn/mkldnn_fwd.h"
namespace onnxruntime {
namespace mkl_dnn {
namespace {
// Struct which encapsulates parameters for MKLDNN Pool primitive.
struct ReluParams {
const mkldnn::memory::dims& src_dims;
const mkldnn::memory::dims& dst_dims;
ReluParams(const mkldnn::memory::dims& src_dims, const mkldnn::memory::dims& dst_dims)
: src_dims(src_dims),
dst_dims(dst_dims) {}
// Used as the key for Pool Primitive Reuse Pool.
std::string ToString() const {
std::string key;
key.reserve(64);
key.append("Relu_");
AddDimsToKey(key, src_dims);
AddDimsToKey(key, dst_dims);
return key;
}
};
template <typename T>
class ReluPrimitive final : public PrimitiveBase {
public:
explicit ReluPrimitive(const ReluParams& params)
: cpu_engine_(GetEngine()) {
context_.stream.reset(new mkldnn::stream(mkldnn::stream::kind::eager));
if (context_.relu_fwd == nullptr) {
Initialize(params);
}
}
~ReluPrimitive() = default;
void Compute(const T* src_data, T* dst_data) {
context_.src_mem->set_data_handle(
static_cast<void*>(const_cast<T*>(src_data)));
context_.dst_mem->set_data_handle(
static_cast<void*>(dst_data));
context_.stream->submit(context_.net);
context_.src_mem->set_data_handle(nullptr);
context_.dst_mem->set_data_handle(nullptr);
return;
}
private:
struct ReluContext {
mkldnn::memory::format src_fmt;
std::unique_ptr<mkldnn::memory> src_mem;
std::unique_ptr<mkldnn::memory> dst_mem;
size_t src_size;
size_t dst_size;
std::unique_ptr<mkldnn::eltwise_forward::desc> fwd_desc;
std::unique_ptr<mkldnn::eltwise_forward::primitive_desc> relu_fwd_pd;
std::unique_ptr<mkldnn::primitive> relu_fwd;
std::unique_ptr<mkldnn::memory::desc> src_md;
std::unique_ptr<mkldnn::memory::desc> dst_md;
std::unique_ptr<mkldnn::stream> stream;
std::vector<mkldnn::primitive> net;
};
void Initialize(const ReluParams& params) {
mkldnn::memory::format fmt = mkldnn::memory::format::any;
switch (params.src_dims.size()) {
case 1: { fmt = mkldnn::memory::format::x; break; }
case 2: { fmt = mkldnn::memory::format::nc; break; }
case 3: { fmt = mkldnn::memory::format::ntc; break; }
case 4: { fmt = mkldnn::memory::format::nchw; break; }
case 5: { fmt = mkldnn::memory::format::ncdhw; break; }
default: { fmt = mkldnn::memory::format::any; break; }
}
context_.src_md.reset(new mkldnn::memory::desc({ params.src_dims}, MklDnnType<T>(), fmt));
mkldnn::algorithm algo = mkldnn::algorithm::eltwise_relu;
context_.fwd_desc.reset(new mkldnn::eltwise_forward::desc(
mkldnn::prop_kind::forward_inference, algo, *context_.src_md, 0));
context_.relu_fwd_pd.reset(new mkldnn::eltwise_forward::primitive_desc(
*context_.fwd_desc, cpu_engine_));
context_.src_fmt = static_cast<mkldnn::memory::format>(
context_.relu_fwd_pd.get()->src_primitive_desc().desc().data.format);
context_.src_size = context_.relu_fwd_pd.get()->src_primitive_desc().get_size();
context_.dst_size = context_.relu_fwd_pd.get()->dst_primitive_desc().get_size();
context_.src_mem.reset(new mkldnn::memory(context_.relu_fwd_pd.get()->src_primitive_desc(), nullptr));
context_.dst_mem.reset(new mkldnn::memory(context_.relu_fwd_pd.get()->dst_primitive_desc(), nullptr));
context_.relu_fwd.reset(
new mkldnn::eltwise_forward(*context_.relu_fwd_pd, *context_.src_mem, *context_.dst_mem));
context_.net.push_back(*context_.relu_fwd);
}
ReluContext context_;
mkldnn::engine& cpu_engine_;
};
// Pool which allows for reuse of MKLDNN Relu primitives which are expensive
// to instantiate. To address thread safety, the primitives are stored in a map
// on thread local storage.
template <typename T>
class ReluPrimitivePool : public PrimitivePool<T> {
public:
static ReluPrimitive<T>* Get(const ReluParams& params) {
ReluPrimitive<T>* primitive = dynamic_cast<ReluPrimitive<T>*>(
ReluPrimitivePool<T>::GetInstance().GetPrimitive(params.ToString()));
if (primitive == nullptr) {
auto relu_primitive = onnxruntime::make_unique<ReluPrimitive<T>>(params);
primitive = relu_primitive.get();
ReluPrimitivePool<T>::GetInstance().SetPrimitive(params.ToString(),
std::move(relu_primitive));
}
return primitive;
}
private:
ReluPrimitivePool() = default;
~ReluPrimitivePool() = default;
static ReluPrimitivePool& GetInstance() {
static ReluPrimitivePool pool;
return pool;
}
};
} // namespace
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 TensorShape& x_shape = X->Shape();
const auto& x_dims = x_shape.GetDims();
if (X->Shape().NumDimensions() > 5 ) {
// Fall Back to CPU implementation.
// mkldnn support up to dim of size 5
return onnxruntime::Relu<T>::Compute(context);
}
const TensorShape& y_shape = Y->Shape();
auto& y_dims = y_shape.GetDims();
const T* src_data = X->template Data<T>();
T* dst_data = Y->template MutableData<T>();
mkldnn::memory::dims src_dims_mkl(x_dims.begin(), x_dims.end());
mkldnn::memory::dims dst_dims_mkl(y_dims.begin(), y_dims.end());
try {
ReluParams pool_params(src_dims_mkl, dst_dims_mkl);
ReluPrimitive<T>* relu_primitive = ReluPrimitivePool<T>::Get(pool_params);
relu_primitive->Compute(src_data, dst_data);
} catch (const mkldnn::error& e) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Status: ", e.status,
", message: ", e.message.c_str());
}
return Status::OK();
}
ONNX_OPERATOR_KERNEL_EX(
Relu,
kOnnxDomain,
6,
kMklDnnExecutionProvider,
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
Relu<float>);
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -1,20 +0,0 @@
// Copyright(C) 2018 Intel Corporation
// Licensed under the MIT License
#pragma once
#include "core/framework/op_kernel.h"
#include "core/providers/cpu/activation/activations.h"
namespace onnxruntime {
namespace mkl_dnn {
template <typename T>
class Relu : public onnxruntime::Relu<T> {
public:
explicit Relu(const OpKernelInfo& info) : onnxruntime::Relu<T>(info) {}
Status Compute(OpKernelContext* context) const override;
};
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -5,6 +5,7 @@
#include "core/providers/cpu/math/gemm_helper.h"
#include "core/util/math_cpuonly.h"
#include "mkldnn.h"
#include "mkldnn.hpp"
#include "core/providers/mkldnn/mkldnn_fwd.h"
namespace onnxruntime {
@ -28,11 +29,14 @@ Status Gemm<float>::Compute(OpKernelContext* ctx) const {
if (!helper.State().IsOK())
return helper.State();
int M = gsl::narrow_cast<int>(helper.M());
int N = gsl::narrow_cast<int>(helper.N());
int K = gsl::narrow_cast<int>(helper.K());
mkldnn::memory::dim M = gsl::narrow_cast<int>(helper.M());
mkldnn::memory::dim N = gsl::narrow_cast<int>(helper.N());
mkldnn::memory::dim K = gsl::narrow_cast<int>(helper.K());
auto Y = ctx->Output(0, TensorShape({M, N}));
if (M <= 0)
return ORT_MAKE_STATUS(ONNXRUNTIME, NOT_IMPLEMENTED, "Empty Tensor not supported");
if (beta_ != 0) {
auto output_mat = EigenMatrixMapRowMajor<float>(
Y->template MutableData<float>(),
@ -77,15 +81,13 @@ Status Gemm<float>::Compute(OpKernelContext* ctx) const {
}
}
// mkldnn_sgemm expects col major matrices, so we need to swap the operands A and B
auto status = mkldnn_sgemm(trans_B_ ? "T" : "N",
trans_A_ ? "T" : "N",
&N, &M, &K,
&alpha_, W->template Data<float>(),
trans_B_ ? &K : &N,
X->template Data<float>(),
trans_A_ ? &M : &K,
&beta_, Y->template MutableData<float>(), &N);
// mkldnn_sgemm expects row major matrices, so no need to swap the operands A and B
auto status = mkldnn_sgemm(trans_A_ ? 'T' : 'N',
trans_B_ ? 'T' : 'N',
M, N, K,
alpha_, X->template Data<float>() , trans_A_ ? M : K,
W->template Data<float>(), trans_B_ ? K : N,
beta_, Y->template MutableData<float>(), N);
if (status == mkldnn_success) {
return Status::OK();
} else {

View file

@ -1,235 +0,0 @@
// Copyright(C) 2018 Intel Corporation
// Licensed under the MIT License
#ifdef _WIN32
#pragma warning(disable : 4244)
#endif
#include "core/providers/mkldnn/mkldnn_common.h"
#include "core/providers/mkldnn/math/sum.h"
#include "core/providers/mkldnn/mkldnn_fwd.h"
namespace onnxruntime {
namespace mkl_dnn {
namespace {
// Struct which encapsulates parameters for MKLDNN Sum primitives.
struct SumParams {
const std::vector<mkldnn::memory::dims>& src_dims;
const mkldnn::memory::dims& dst_dim;
const int num_inputs;
const int num_dimensions;
SumParams(const std::vector<mkldnn::memory::dims>& dims,
const mkldnn::memory::dims& dst_dims, int numinputs,
int dimensions)
: src_dims(dims),
dst_dim(dst_dims),
num_inputs(numinputs),
num_dimensions(dimensions) {}
// Used as the key for Sum Primitive Reuse Sum.
std::string ToString() const {
std::string key;
key.reserve(64);
key.append("sum_");
for (size_t i = 0; i < src_dims.size(); i++) {
AddDimsToKey(key, src_dims[i]);
}
AddDimsToKey(key, dst_dim);
return key;
}
};
template <typename T>
class SumPrimitive final : public PrimitiveBase {
public:
explicit SumPrimitive(const SumParams& params)
: cpu_engine_(GetEngine()) {
context_.stream.reset(new mkldnn::stream(mkldnn::stream::kind::eager));
if (context_.sum_pd == nullptr) {
Initialize(params);
}
}
~SumPrimitive() = default;
void Compute(OpKernelContext* context, int numinputs) {
const Tensor* X1 = context->Input<Tensor>(0);
Tensor* Y = context->Output(0, X1->Shape());
T* dst_data = Y->template MutableData<T>();
context_.dst_mem->set_data_handle(
static_cast<void*>(dst_data));
for (int i = 0; i < numinputs; i++) {
const Tensor* X = context->Input<Tensor>(i);
const T* src_data = X->template Data<T>();
context_.srcs_memory[i].set_data_handle(
static_cast<void*>(const_cast<T*>(src_data)));
}
context_.stream->submit(context_.net);
for (int i = 0; i < numinputs; i++) {
context_.srcs_memory[i].set_data_handle(nullptr);
}
}
std::unique_ptr<mkldnn::memory::desc> GetDstMemoryDesc() const {
return context_.dst_md;
}
std::unique_ptr<mkldnn::sum::primitive_desc>
GetPrimitiveDesc() const {
return context_.sum_pd;
}
private:
struct SumContext {
std::unique_ptr<mkldnn::memory::desc> src_md;
std::unique_ptr<mkldnn::memory::desc> dst_md;
std::vector<mkldnn::memory> srcs_memory;
std::unique_ptr<mkldnn::memory> dst_mem;
std::vector<mkldnn::memory::primitive_desc> srcs_pd;
std::unique_ptr<mkldnn::memory::primitive_desc> src_mpd;
std::unique_ptr<mkldnn::memory::primitive_desc> dst_pd;
std::unique_ptr<mkldnn::sum::primitive_desc> sum_pd;
std::unique_ptr<mkldnn::stream> stream;
std::vector<mkldnn::primitive> net;
};
void Initialize(const SumParams& params) {
std::vector<float> coeff;
mkldnn::memory::format fmt = mkldnn::memory::format::any;
switch (params.num_dimensions) {
case 1: { fmt = mkldnn::memory::format::x; break; }
case 2: { fmt = mkldnn::memory::format::nc; break; }
case 3: { fmt = mkldnn::memory::format::ntc; break; }
case 4: { fmt = mkldnn::memory::format::nchw; break; }
case 5: { fmt = mkldnn::memory::format::ncdhw; break; }
default: { fmt = mkldnn::memory::format::any; break; }
}
for (int i = 0; i < params.num_inputs; i++) {
context_.src_md.reset(
new mkldnn::memory::desc({params.src_dims[i]}, MklDnnType<T>(), fmt));
auto mpd = mkldnn::memory::primitive_desc(*context_.src_md, cpu_engine_);
auto src_memory = mkldnn::memory(mpd, nullptr);
context_.srcs_pd.push_back(mpd);
context_.srcs_memory.push_back(src_memory);
coeff.push_back(1.0);
}
std::unique_ptr<mkldnn::memory> dst;
context_.dst_md.reset(new mkldnn::memory::desc(
{params.dst_dim}, MklDnnType<T>(), mkldnn::memory::format::any));
context_.sum_pd.reset(new mkldnn::sum::primitive_desc(
*context_.dst_md, coeff, context_.srcs_pd));
context_.dst_mem.reset(new mkldnn::memory(
context_.sum_pd->dst_primitive_desc(), nullptr));
std::vector<mkldnn::primitive::at> inputs;
for (int i = 0; i < params.num_inputs; i++) {
inputs.push_back(context_.srcs_memory[i]);
}
auto c = mkldnn::sum(*context_.sum_pd, inputs, *context_.dst_mem);
context_.net.push_back(c);
}
SumContext context_;
mkldnn::engine& cpu_engine_;
};
// Pool which allows for reuse of MKLDNN Sum primitives which are
// expensive to instantiate. To address thread safety, the primitives
// are stored in a map on thread local storage.
template <typename T>
class SumPrimitivePool : public PrimitivePool<T> {
public:
static SumPrimitive<T>* Get(const SumParams& params) {
SumPrimitive<T>* primitive = dynamic_cast<SumPrimitive<T>*>(
SumPrimitivePool<T>::GetInstance().GetPrimitive(params.ToString()));
if (primitive == nullptr) {
auto sum_primitive = onnxruntime::make_unique<SumPrimitive<T>>(params);
primitive = sum_primitive.get();
SumPrimitivePool<T>::GetInstance().SetPrimitive(
params.ToString(), std::move(sum_primitive));
}
return primitive;
}
private:
SumPrimitivePool() = default;
~SumPrimitivePool() = default;
static SumPrimitivePool& GetInstance() {
static SumPrimitivePool pool;
return pool;
}
};
} // namespace_
template <typename T>
Status Sum<T>::Compute(OpKernelContext* context) const {
int num_inputs = static_cast<int>(OpKernel::Node().InputDefs().size());
ORT_ENFORCE(num_inputs > 0, "MKLDNN Sum kernel: Must have at least one input");
if (num_inputs == 1) {
// Fall Back to CPU implementation. For one input, use CPU implementation
// to copy input to output
return onnxruntime::Sum_6<T>::Compute(context);
}
std::vector<mkldnn::memory::dims> src_dims;
const Tensor* X1 = context->Input<Tensor>(0);
if (X1 == nullptr) return Status(common::ONNXRUNTIME, common::FAIL, "input count mismatch");
Tensor* Y = context->Output(0, X1->Shape());
int dimensions = static_cast<int>(X1->Shape().NumDimensions());
const TensorShape& x_shape = X1->Shape();
const auto& x_dims = x_shape.GetDims();
mkldnn::memory::dims src_dim(x_dims.begin(), x_dims.end());
mkldnn::memory::dims dst_dims_mkl(
Y->Shape().GetDims().begin(), Y->Shape().GetDims().end());
for (int i = 0; i < num_inputs; i++) {
const Tensor* X = context->Input<Tensor>(i);
if (X == nullptr) return Status(common::ONNXRUNTIME, common::FAIL, "input count mismatch");
mkldnn::memory::dims src_dims_mkl(
X->Shape().GetDims().begin(), X->Shape().GetDims().end());
src_dims.push_back(src_dims_mkl);
}
try {
SumParams parameters(src_dims, dst_dims_mkl, num_inputs, dimensions);
SumPrimitive<T>* sum_primitive = SumPrimitivePool<T>::Get(parameters);
ORT_RETURN_IF_NOT(sum_primitive != nullptr);
sum_primitive->Compute(context, num_inputs);
} catch (const mkldnn::error& e) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Status: ", e.status,
", message: ", e.message.c_str());
}
return Status::OK();
}
ONNX_OPERATOR_KERNEL_EX(
Sum,
kOnnxDomain,
6,
kMklDnnExecutionProvider,
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
Sum<float>);
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -1,22 +0,0 @@
// Copyright(C) 2018 Intel Corporation
// Licensed under the MIT License
#pragma once
#include "core/framework/op_kernel.h"
#include "core/providers/cpu/math/element_wise_ops.h"
#include "../mkldnn_execution_provider.h"
namespace onnxruntime {
namespace mkl_dnn {
template <typename T>
class Sum final : public onnxruntime::Sum_6<T> {
public:
explicit Sum(const OpKernelInfo& info) : onnxruntime::Sum_6<T>(info) {}
Status Compute(OpKernelContext* context) const override;
private:
};
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -5,6 +5,7 @@
#include "core/common/common.h"
#include "mkldnn.hpp"
#include <unordered_map>
#include <list>
namespace onnxruntime {
namespace mkl_dnn {
@ -19,7 +20,7 @@ mkldnn::memory::data_type MklDnnType<float>() {
}
static mkldnn::engine& GetEngine() {
static mkldnn::engine cpu_engine = mkldnn::engine(mkldnn::engine::cpu, 0);
static mkldnn::engine cpu_engine = mkldnn::engine(mkldnn::engine::kind::cpu, 0);
return cpu_engine;
}
@ -68,108 +69,5 @@ class PrimitivePool {
return map;
}
};
// Struct which encapsulates parameters for MKLDNN memory reorder primitive.
struct MemoryReorderParams {
const mkldnn::memory& src;
const mkldnn::memory& dst;
MemoryReorderParams(const mkldnn::memory& src, const mkldnn::memory& dst) : src(src), dst(dst) {}
// Used as the key for MemoryReorder primitive reuse pool.
std::string ToString() const {
std::string key;
key.reserve(64);
key.append("reorder_");
const auto& src_desc = src.get_primitive_desc().desc().data;
const auto& dst_desc = dst.get_primitive_desc().desc().data;
mkldnn::memory::dims src_dims(src_desc.dims, &src_desc.dims[src_desc.ndims]);
mkldnn::memory::dims dst_dims(dst_desc.dims, &dst_desc.dims[dst_desc.ndims]);
key.append(std::to_string(src_desc.format));
key.append(1, '_');
key.append(std::to_string(dst_desc.data_type));
AddDimsToKey(key, src_dims);
key.append(std::to_string(dst_desc.format));
key.append(1, '_');
key.append(std::to_string(dst_desc.data_type));
AddDimsToKey(key, dst_dims);
return key;
}
};
// Encapsulates an MKLDNN memory reorder primitive.
// these are needed to convert the source/weight/destination memory layout
// to one that is identified by MKLDNN to be optimal for performance.
class MemoryReorderPrimitive : public PrimitiveBase {
public:
explicit MemoryReorderPrimitive(const MemoryReorderParams& params) : cpu_engine_(GetEngine()) {
Initialize(params);
}
~MemoryReorderPrimitive() = default;
std::shared_ptr<mkldnn::primitive> GetPrimitive() {
return context_.primitive;
}
void SetMemory(const MemoryReorderParams& params) {
context_.src_mem->set_data_handle(params.src.get_data_handle());
context_.dst_mem->set_data_handle(params.dst.get_data_handle());
}
private:
struct MemoryReorderContext {
std::shared_ptr<mkldnn::memory> src_mem;
std::shared_ptr<mkldnn::memory> dst_mem;
std::shared_ptr<mkldnn::primitive> primitive;
MemoryReorderContext() : src_mem(nullptr), dst_mem(nullptr), primitive(nullptr) {
}
} context_;
mkldnn::engine& cpu_engine_;
void Initialize(const MemoryReorderParams& params) {
context_.src_mem = std::make_shared<mkldnn::memory>(
mkldnn::memory({params.src.get_primitive_desc().desc(), cpu_engine_}, nullptr));
context_.dst_mem = std::make_shared<mkldnn::memory>(
mkldnn::memory({params.dst.get_primitive_desc().desc(), cpu_engine_}, nullptr));
context_.primitive = std::make_shared<mkldnn::reorder>(
mkldnn::reorder(*context_.src_mem, *context_.dst_mem));
}
};
// Pool which allows for reuse of MKLDNN memory reorder primitives which are expensive to instantiate.
// To address thread safety, the primitives are stored in a map on thread local storage.
template <typename T>
class MemoryReorderPrimitivePool : public PrimitivePool<T> {
public:
static MemoryReorderPrimitivePool& GetInstance() {
static MemoryReorderPrimitivePool pool;
return pool;
}
static MemoryReorderPrimitive* Get(const MemoryReorderParams& params) {
MemoryReorderPrimitive* primitive = static_cast<MemoryReorderPrimitive*>(
MemoryReorderPrimitivePool<T>::GetInstance().GetPrimitive(params.ToString()));
if (primitive == nullptr) {
auto reorder_primitive = onnxruntime::make_unique<MemoryReorderPrimitive>(params);
primitive = reorder_primitive.get();
MemoryReorderPrimitivePool<T>::GetInstance().SetPrimitive(params.ToString(), std::move(reorder_primitive));
}
primitive->SetMemory(params);
return primitive;
}
private:
MemoryReorderPrimitivePool() = default;
~MemoryReorderPrimitivePool() = default;
};
template <typename T>
static void DoReorder(const MemoryReorderParams& params) {
std::vector<mkldnn::primitive> net;
net.push_back(*(MemoryReorderPrimitivePool<T>::Get(params)->GetPrimitive()));
mkldnn::stream(mkldnn::stream::kind::eager).submit(net).wait();
}
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -42,31 +42,11 @@ MKLDNNExecutionProvider::~MKLDNNExecutionProvider() {
}
namespace mkl_dnn {
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, Conv);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 7, Gemm);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 6, Relu);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 6, Sum);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 7, BatchNormalization);
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 7, 8, float, AveragePool);
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, 8, float, GlobalAveragePool);
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, 7, float, MaxPool);
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 8, 8, float, MaxPool);
class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, 8, float, GlobalMaxPool);
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, float, LRN);
void RegisterMKLDNNKernels(KernelRegistry& kernel_registry) {
static const BuildKernelCreateInfoFn function_table[] = {
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, Conv)>,
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 7, Gemm)>,
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 6, Relu)>,
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 6, Sum)>,
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 7, BatchNormalization)>,
BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 7, 8, float, AveragePool)>,
BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, 8, float, GlobalAveragePool)>,
BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, 7, float, MaxPool)>,
BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 8, 8, float, MaxPool)>,
BuildKernelCreateInfo<ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, 8, float, GlobalMaxPool)>,
BuildKernelCreateInfo<ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kMklDnnExecutionProvider, kOnnxDomain, 1, float, LRN)>,
};
for (auto& function_table_entry : function_table) {
@ -161,6 +141,7 @@ void MKLDNNExecutionProvider::CreateOrUpdateMklDnnNode(const Node* node,
subgraph_ptr->mkldnn_nodes.push_back(mkldnn_node);
output_to_source_node_map.insert(std::make_pair(node_outputs[0]->Name(), subgraph_ptr->mkldnn_nodes.size() - 1));
} else {
subgraph_ptr->mkldnn_nodes.back().num_inputs += static_cast<int>(node->InputDefs().size() - 1);
const auto& node_outputs = node->OutputDefs();
output_to_source_node_map.erase(subgraph_ptr->mkldnn_nodes.back().output_name);
subgraph_ptr->mkldnn_nodes.back().output_name = node_outputs[0]->Name();
@ -183,9 +164,17 @@ void MKLDNNExecutionProvider::CreateOrUpdateMklDnnNode(const Node* node,
NodeAttributes attributes = node->GetAttributes();
if (attributes.size() > 0) {
size_t index = subgraph_ptr->mkldnn_nodes.size();
std::string op_name;
if (fused) {
for (auto iter = node->InputNodesBegin(); iter != node->InputNodesEnd(); ++iter) {
op_name = (*iter).OpType();
}
} else {
op_name = node->OpType();
}
for (auto att_it = attributes.begin(); att_it != attributes.end(); ++att_it) {
std::string key = node->OpType() + "-" + std::to_string(index) + "-" + att_it->first;
std::string key = op_name + "-" + std::to_string(index) + "-" + att_it->first;
std::pair<std::string, ONNX_NAMESPACE::AttributeProto> att(key, att_it->second);
subgraph_attributes[key] = att_it->second;
}
@ -245,8 +234,14 @@ std::vector<std::unique_ptr<ComputeCapability>> MKLDNNExecutionProvider::GetCapa
// can we fuse (at mkldnn level) nodes?
bool fused = false;
if (sub_var.subgraph_node_indexes.size() > 1 && node->OpType() == "BatchNormalization") {
if (subgraph_ptr->mkldnn_nodes.back().name == "Conv") {
subgraph_ptr->mkldnn_nodes.back().name += "-BatchNormalization";
fused = true;
}
}
if (sub_var.subgraph_node_indexes.size() > 1 && node->OpType() == "Relu") {
if (subgraph_ptr->mkldnn_nodes.back().name == "BatchNormalization" || subgraph_ptr->mkldnn_nodes.back().name == "Conv") {
if (subgraph_ptr->mkldnn_nodes.back().name == "Conv-BatchNormalization" || subgraph_ptr->mkldnn_nodes.back().name == "BatchNormalization" || subgraph_ptr->mkldnn_nodes.back().name == "Conv") {
subgraph_ptr->mkldnn_nodes.back().name += "-Relu";
fused = true;
}

View file

@ -58,6 +58,24 @@ class MKLDNNExecutionProvider : public IExecutionProvider {
reordered_buffers_.push_back(std::move(buffer));
}
std::shared_ptr<mkldnn::memory> GetBiasMemoryBuffer(const std::string& key) {
auto iter = bias_mem_map_.find(key);
if (iter != bias_mem_map_.end())
return iter->second;
return nullptr;
}
// Conv+BathNorm fusion. save scaled bias memory.
void SetBiasMemoryBuffer(const std::string& key,
const std::shared_ptr<mkldnn::memory>& bias_mem) {
bias_mem_map_.insert(std::make_pair(key, bias_mem));
}
void SaveAllocatedBiasMemory(IAllocatorUniquePtr<void> buffer) {
// keep reordered memory buffers in scope.
biass_buffers_.push_back(std::move(buffer));
}
std::vector<std::unique_ptr<ComputeCapability>>
GetCapability(const onnxruntime::GraphViewer& graph,
const std::vector<const KernelRegistry*>& /*kernel_registries*/) const override;
@ -71,6 +89,11 @@ class MKLDNNExecutionProvider : public IExecutionProvider {
std::unordered_map<std::string, std::shared_ptr<mkldnn::memory>> weights_mem_map_;
// Save reordered memory buffers in list so that memory is not freed.
std::vector<IAllocatorUniquePtr<void>> reordered_buffers_;
// conv+batchnorm fusion. normalized bias memory blocks from first iteration
std::unordered_map<std::string, std::shared_ptr<mkldnn::memory>> bias_mem_map_;
// Conv+BathNorm fusion bias memory buffer.
std::vector<IAllocatorUniquePtr<void>> biass_buffers_;
OrtMutex mutex_;
// SUBGRAPH
@ -79,13 +102,13 @@ class MKLDNNExecutionProvider : public IExecutionProvider {
const auto& dm_to_ver = graph_viewer.DomainToVersionMap();
return dm_to_ver.at(kOnnxDomain);
}
std::string GetGraphName(const onnxruntime::GraphViewer& graph_viewer) const {
std::string graph_name;
int opset = GetOnnxOpSet(graph_viewer);
int index = 0;
int index = 0;
if (graph_viewer.MaxNodeIndex() > 0) {
auto first_node = graph_viewer.GetNode(index);
while (first_node == nullptr) {

View file

@ -1,285 +0,0 @@
// Copyright(C) 2018 Intel Corporation
// Licensed under the MIT License
#ifdef _WIN32
#pragma warning(disable : 4244)
#endif
#include "core/providers/mkldnn/mkldnn_common.h"
#include "core/providers/mkldnn/nn/batch_norm.h"
#include "core/providers/mkldnn/mkldnn_fwd.h"
#include "core/providers/mkldnn/memcpy_s.h"
#include "core/providers/cpu/nn/batch_norm_helper.h"
namespace onnxruntime {
namespace mkl_dnn {
namespace {
// Struct which encapsulates parameters for MKLDNN BatchNorm primitive.
struct BatchNormParams {
const mkldnn::memory::dims& src_dims;
const mkldnn::memory::dims& scale_dims;
const mkldnn::memory::dims& b_dims;
const mkldnn::memory::dims& mean_dims;
const mkldnn::memory::dims& var_dims;
const mkldnn::memory::dims& dst_dims;
const float epsilon;
BatchNormParams(const mkldnn::memory::dims& src_dims_mkl,
const mkldnn::memory::dims& scale_dims_mkl,
const mkldnn::memory::dims& b_dims_mkl, const mkldnn::memory::dims& mean_dims_mkl,
const mkldnn::memory::dims& var_dims_mkl, const mkldnn::memory::dims& dst_dims_mkl,
float eps)
: src_dims(src_dims_mkl),
scale_dims(scale_dims_mkl),
b_dims(b_dims_mkl),
mean_dims(mean_dims_mkl),
var_dims(var_dims_mkl),
dst_dims(dst_dims_mkl),
epsilon(eps) {}
// Used as the key for BatchNorm Primitive Reuse Pool.
std::string ToString() const {
std::string key;
key.reserve(128);
key.append("BatchNorm_");
AddDimsToKey(key, src_dims);
AddDimsToKey(key, scale_dims);
AddDimsToKey(key, b_dims);
AddDimsToKey(key, mean_dims);
AddDimsToKey(key, var_dims);
AddDimsToKey(key, dst_dims);
return key;
}
};
template <typename T>
class BatchNormPrimitive final : public PrimitiveBase {
public:
explicit BatchNormPrimitive(const BatchNormParams& params)
: cpu_engine_(GetEngine()) {
context_.stream.reset(new mkldnn::stream(mkldnn::stream::kind::eager));
if (context_.batchnorm_fwd == nullptr) {
Initialize(params);
}
}
~BatchNormPrimitive() = default;
void Compute(const T* src_data, const T* scale_data, const T* b_data,
const T* mean_data, const T* var_data, T* dst_data,
int scale_dims_channels) {
context_.src_mem->set_data_handle(
static_cast<void*>(const_cast<T*>(src_data)));
context_.mean_mem->set_data_handle(
static_cast<void*>(const_cast<T*>(mean_data)));
context_.var_mem->set_data_handle(
static_cast<void*>(const_cast<T*>(var_data)));
context_.dst_mem->set_data_handle(
static_cast<void*>(dst_data));
T* scale_shift_buf = static_cast<T*>(context_.scale_shift_mem->get_data_handle());
size_t src_bytes = sizeof(T) * scale_dims_channels;
size_t dst_bytes = sizeof(T) * scale_dims_channels;
MEMCPY_S(scale_shift_buf, scale_data, src_bytes, dst_bytes);
MEMCPY_S(&scale_shift_buf[scale_dims_channels], b_data, src_bytes, dst_bytes);
context_.stream->submit(context_.net);
return;
}
std::unique_ptr<mkldnn::convolution_forward::primitive_desc>
GetPrimitiveDesc() const {
return context_.conv_fwd_pd;
}
private:
struct BatchNormContext {
std::unique_ptr<mkldnn::memory> src_mem;
std::unique_ptr<mkldnn::memory> scale_shift_mem;
std::unique_ptr<mkldnn::memory> mean_mem;
std::unique_ptr<mkldnn::memory> var_mem;
std::unique_ptr<mkldnn::memory> dst_mem;
std::unique_ptr<mkldnn::memory::desc> src_md;
std::unique_ptr<mkldnn::memory::desc> scale_shift_md;
std::unique_ptr<mkldnn::memory::desc> mean_md;
std::unique_ptr<mkldnn::memory::desc> var_md;
std::unique_ptr<mkldnn::memory::desc> dst_md;
std::unique_ptr<mkldnn::batch_normalization_forward::desc> batchnorm_fwd;
std::unique_ptr<mkldnn::batch_normalization_forward::primitive_desc>
batchnorm_fwd_pd;
std::unique_ptr<mkldnn::stream> stream;
std::vector<mkldnn::primitive> net;
};
void Initialize(const BatchNormParams& params) {
mkldnn::memory::format fmt = mkldnn::memory::format::any;
switch (params.src_dims.size()) {
case 1: { fmt = mkldnn::memory::format::x; break; }
case 2: { fmt = mkldnn::memory::format::nc; break; }
case 3: { fmt = mkldnn::memory::format::ntc; break; }
case 4: { fmt = mkldnn::memory::format::nchw; break; }
case 5: { fmt = mkldnn::memory::format::ncdhw; break; }
default: { fmt = mkldnn::memory::format::any; break; }
}
context_.src_md.reset(new mkldnn::memory::desc(
{ params.src_dims }, MklDnnType<T>(), fmt));
context_.scale_shift_md.reset(new mkldnn::memory::desc(
{ 2, params.scale_dims[0] }, MklDnnType<T>(), mkldnn::memory::format::nc));
context_.mean_md.reset(new mkldnn::memory::desc(
{ params.mean_dims }, MklDnnType<T>(), mkldnn::memory::format::x));
context_.var_md.reset(new mkldnn::memory::desc(
{ params.var_dims }, MklDnnType<T>(), mkldnn::memory::format::x));
context_.dst_md.reset(new mkldnn::memory::desc(
{ params.dst_dims }, MklDnnType<T>(), fmt));
context_.src_mem.reset(
new mkldnn::memory({ *context_.src_md, cpu_engine_ }, nullptr));
// scale_shift_mem will allocate 2*C*sizeof(float) buffer
//
context_.scale_shift_mem.reset(
new mkldnn::memory({ *context_.scale_shift_md, cpu_engine_ }));
context_.mean_mem.reset(
new mkldnn::memory({ *context_.mean_md, cpu_engine_ }, nullptr));
context_.var_mem.reset(
new mkldnn::memory({ *context_.var_md, cpu_engine_ }, nullptr));
context_.batchnorm_fwd.reset(new mkldnn::batch_normalization_forward::desc(
mkldnn::prop_kind::forward_inference, *context_.src_md, params.epsilon,
mkldnn::batch_normalization_flag::use_scale_shift |
mkldnn::batch_normalization_flag::use_global_stats));
context_.batchnorm_fwd_pd.reset(
new mkldnn::batch_normalization_forward::primitive_desc(
*context_.batchnorm_fwd, cpu_engine_));
context_.dst_mem.reset(
new mkldnn::memory(
context_.batchnorm_fwd_pd->dst_primitive_desc(), nullptr));
auto bn = mkldnn::batch_normalization_forward(
*context_.batchnorm_fwd_pd,
(const mkldnn::primitive::at)*context_.src_mem,
(const mkldnn::primitive::at)*context_.mean_mem,
(const mkldnn::primitive::at)*context_.var_mem,
(const mkldnn::memory)*context_.scale_shift_mem,
(const mkldnn::memory) *context_.dst_mem);
context_.net.push_back(bn);
}
BatchNormContext context_;
mkldnn::engine& cpu_engine_;
};
// Pool which allows for reuse of MKLDNN BatchNorm primitives which are
// expensive to instantiate. To address thread safety, the primitives are
// stored in a map on thread local storage.
template <typename T>
class BatchNormPrimitivePool : public PrimitivePool<T> {
public:
static BatchNormPrimitive<T>* Get(const BatchNormParams& params) {
BatchNormPrimitive<T>* primitive =
dynamic_cast<BatchNormPrimitive<T>*>(
BatchNormPrimitivePool<T>::GetInstance().GetPrimitive(params.ToString()));
if (primitive == nullptr) {
auto BatchNorm_primitive = onnxruntime::make_unique<BatchNormPrimitive<T>>(params);
primitive = BatchNorm_primitive.get();
BatchNormPrimitivePool<T>::GetInstance().SetPrimitive(
params.ToString(), std::move(BatchNorm_primitive));
}
return primitive;
}
private:
BatchNormPrimitivePool() = default;
~BatchNormPrimitivePool() = default;
static BatchNormPrimitivePool& GetInstance() {
static BatchNormPrimitivePool pool;
return pool;
}
};
} // namespace
template <typename T>
Status BatchNorm<T>::Compute(OpKernelContext* context) const {
const Tensor* X = context->Input<Tensor>(0);
int num_dimensions = static_cast<int>(X->Shape().NumDimensions());
if (num_dimensions == 3) {
// Fall back CPU implementation
return onnxruntime::BatchNorm<T>::Compute(context);
}
const T* src_data = X->template Data<T>();
const Tensor* scale = context->Input<Tensor>(1);
const T* scale_data = scale->template Data<T>();
const Tensor* B = context->Input<Tensor>(2);
const T* b_data = B->template Data<T>();
const Tensor* mean = context->Input<Tensor>(3);
const T* mean_data = mean->template Data<T>();
const Tensor* var = context->Input<Tensor>(4);
const T* var_data = var->template Data<T>();
Tensor* Y = context->Output(0, X->Shape());
T* dst_data = Y->template MutableData<T>();
ORT_RETURN_IF_ERROR(
BatchNormHelper::ValidateInputs(X, scale, B, mean, var));
mkldnn::memory::dims src_dims_mkl(
X->Shape().GetDims().begin(), X->Shape().GetDims().end());
mkldnn::memory::dims scale_dims_mkl(
scale->Shape().GetDims().begin(), scale->Shape().GetDims().end());
mkldnn::memory::dims b_dims_mkl(
B->Shape().GetDims().begin(), B->Shape().GetDims().end());
mkldnn::memory::dims mean_dims_mkl(
mean->Shape().GetDims().begin(), mean->Shape().GetDims().end());
mkldnn::memory::dims var_dims_mkl(
var->Shape().GetDims().begin(), var->Shape().GetDims().end());
mkldnn::memory::dims dst_dims_mkl(
Y->Shape().GetDims().begin(), Y->Shape().GetDims().end());
try {
BatchNormParams batchNorm_params(src_dims_mkl, scale_dims_mkl,
b_dims_mkl, mean_dims_mkl, var_dims_mkl, dst_dims_mkl,
onnxruntime::BatchNorm<T>::epsilon_);
BatchNormPrimitive<T>* batchNorm_primitive =
BatchNormPrimitivePool<T>::Get(batchNorm_params);
ORT_RETURN_IF_NOT(batchNorm_primitive != nullptr);
batchNorm_primitive->Compute(src_data, scale_data, b_data,
mean_data, var_data, dst_data, scale_dims_mkl[0]);
} catch (const mkldnn::error& e) {
return ORT_MAKE_STATUS(
ONNXRUNTIME, FAIL, "Status: ", e.status, ", message: ", e.message.c_str());
}
return Status::OK();
}
ONNX_OPERATOR_KERNEL_EX(
BatchNormalization,
kOnnxDomain,
7,
kMklDnnExecutionProvider,
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
BatchNorm<float>);
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -1,18 +0,0 @@
// Copyright(C) 2018 Intel Corporation
// Licensed under the MIT License
#pragma once
#include "core/framework/op_kernel.h"
#include "core/providers/cpu/nn/batch_norm.h"
namespace onnxruntime {
namespace mkl_dnn {
template <typename T>
class BatchNorm final : public onnxruntime::BatchNorm<T> {
public:
explicit BatchNorm(const OpKernelInfo& info) : onnxruntime::BatchNorm<T>(info) {}
Status Compute(OpKernelContext* context) const override;
};
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -1,473 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifdef _WIN32
#pragma warning(disable : 4244)
#endif
#include <thread>
#include <mutex>
#include "core/providers/mkldnn/nn/conv.h"
#include "core/providers/mkldnn/mkldnn_common.h"
#include "core/providers/mkldnn/mkldnn_fwd.h"
namespace onnxruntime {
namespace mkl_dnn {
namespace {
// Struct which encapsulates parameters for MKLDNN Conv primitive.
struct ConvParams {
const mkldnn::memory::dims& src_dims;
const mkldnn::memory::dims& filter_dims;
const mkldnn::memory::dims& bias_dims;
const mkldnn::memory::dims& dst_dims;
const mkldnn::memory::dims& strides;
const mkldnn::memory::dims& dilations;
const mkldnn::memory::dims& padding_left;
const mkldnn::memory::dims& padding_right;
ConvParams(const mkldnn::memory::dims& src_dims, const mkldnn::memory::dims& filter_dims,
const mkldnn::memory::dims& bias_dims, const mkldnn::memory::dims& dst_dims,
const mkldnn::memory::dims& strides, const mkldnn::memory::dims& dilations,
const mkldnn::memory::dims& padding_left, const mkldnn::memory::dims& padding_right)
: src_dims(src_dims),
filter_dims(filter_dims),
bias_dims(bias_dims),
dst_dims(dst_dims),
strides(strides),
dilations(dilations),
padding_left(padding_left),
padding_right(padding_right) {}
// Used as the key for Conv Primitive Reuse Pool.
std::string ToString() const {
std::string key;
key.reserve(128);
key.append("conv_");
AddDimsToKey(key, src_dims);
AddDimsToKey(key, filter_dims);
AddDimsToKey(key, bias_dims);
AddDimsToKey(key, dst_dims);
AddDimsToKey(key, strides);
AddDimsToKey(key, dilations);
AddDimsToKey(key, padding_left);
AddDimsToKey(key, padding_right);
return key;
}
};
template <typename T>
class ConvPrimitive : public PrimitiveBase {
public:
explicit ConvPrimitive(const ConvParams& params)
: cpu_engine_(GetEngine()) {
context_.stream.reset(new mkldnn::stream(mkldnn::stream::kind::eager));
if (context_.conv_fwd == nullptr) {
Initialize(params);
}
}
~ConvPrimitive() = default;
void Compute(const T* src_data, const T* filter_data,
T* dst_data, const T* bias_data = nullptr) {
context_.src_mem->set_data_handle(
static_cast<void*>(const_cast<T*>(src_data)));
context_.filter_mem->set_data_handle(
static_cast<void*>(const_cast<T*>(filter_data)));
if (bias_data != nullptr) {
context_.bias_mem->set_data_handle(
static_cast<void*>(const_cast<T*>(bias_data)));
}
context_.dst_mem->set_data_handle(
static_cast<void*>(dst_data));
context_.stream->submit(context_.net);
context_.src_mem->set_data_handle(nullptr);
context_.filter_mem->set_data_handle(nullptr);
if (bias_data != nullptr) {
context_.bias_mem->set_data_handle(nullptr);
}
context_.dst_mem->set_data_handle(nullptr);
return;
}
mkldnn::memory::format GetSrcMemoryFormat() const { return context_.src_fmt; }
mkldnn::memory::format GetFilterMemoryFormat() const { return context_.filter_fmt; }
mkldnn::memory::format GetDstMemoryFormat() const { return context_.dst_fmt; }
size_t GetSrcSize() const { return context_.src_size; }
size_t GetFilterSize() const { return context_.filter_size; }
size_t GetDstSize() const { return context_.dst_size; }
mkldnn::convolution_forward::primitive_desc* GetPrimitiveDesc() const {
return context_.conv_fwd_pd.get();
}
private:
struct ConvContext {
mkldnn::memory::format src_fmt;
mkldnn::memory::format filter_fmt;
mkldnn::memory::format dst_fmt;
size_t src_size;
size_t filter_size;
size_t dst_size;
std::unique_ptr<mkldnn::memory> src_mem;
std::unique_ptr<mkldnn::memory> filter_mem;
std::unique_ptr<mkldnn::memory> bias_mem;
std::unique_ptr<mkldnn::memory> dst_mem;
std::unique_ptr<mkldnn::convolution_forward::desc> fwd_desc;
std::unique_ptr<mkldnn::memory::desc> src_md;
std::unique_ptr<mkldnn::memory::desc> filter_md;
std::unique_ptr<mkldnn::memory::desc> bias_md;
std::unique_ptr<mkldnn::memory::desc> dst_md;
std::unique_ptr<mkldnn::convolution_forward::primitive_desc> conv_fwd_pd;
std::unique_ptr<mkldnn::primitive> conv_fwd;
std::unique_ptr<mkldnn::stream> stream;
std::vector<mkldnn::primitive> net;
ConvContext()
: src_fmt(mkldnn::memory::format::any),
filter_fmt(mkldnn::memory::format::any),
dst_fmt(mkldnn::memory::format::any),
src_size(0),
filter_size(0),
dst_size(0),
src_mem(nullptr),
filter_mem(nullptr),
bias_mem(nullptr),
dst_mem(nullptr),
fwd_desc(nullptr),
src_md(nullptr),
filter_md(nullptr),
bias_md(nullptr),
conv_fwd_pd(nullptr),
conv_fwd(nullptr),
stream(nullptr) {}
};
void Initialize(const ConvParams& params) {
// Set the memory descriptors to format::any to allow MKLDNN to decide what the optimal memory layout should be
// for the computation given the input params.
context_.src_md.reset(new mkldnn::memory::desc(
{params.src_dims}, MklDnnType<T>(), mkldnn::memory::format::any));
context_.filter_md.reset(new mkldnn::memory::desc(
{params.filter_dims}, MklDnnType<T>(), mkldnn::memory::format::any));
context_.dst_md.reset(new mkldnn::memory::desc(
{params.dst_dims}, MklDnnType<T>(), mkldnn::memory::format::any));
if (!params.bias_dims.empty())
context_.bias_md.reset(new mkldnn::memory::desc(
{params.bias_dims}, MklDnnType<T>(), mkldnn::memory::format::any));
if (!params.bias_dims.empty()) {
context_.fwd_desc.reset(new mkldnn::convolution_forward::desc(
mkldnn::prop_kind::forward_inference, mkldnn::convolution_direct, *context_.src_md,
*context_.filter_md, *context_.bias_md, *context_.dst_md,
params.strides, params.dilations, params.padding_left,
params.padding_right, mkldnn::padding_kind::zero));
} else {
context_.fwd_desc.reset(new mkldnn::convolution_forward::desc(
mkldnn::prop_kind::forward_inference, mkldnn::convolution_direct, *context_.src_md,
*context_.filter_md, *context_.dst_md, params.strides,
params.dilations, params.padding_left,
params.padding_right, mkldnn::padding_kind::zero));
}
context_.conv_fwd_pd.reset(new mkldnn::convolution_forward::primitive_desc(
*context_.fwd_desc, cpu_engine_));
context_.src_fmt = static_cast<mkldnn::memory::format>(
context_.conv_fwd_pd.get()->src_primitive_desc().desc().data.format);
context_.filter_fmt = static_cast<mkldnn::memory::format>(
context_.conv_fwd_pd.get()->weights_primitive_desc().desc().data.format);
context_.dst_fmt = static_cast<mkldnn::memory::format>(
context_.conv_fwd_pd.get()->dst_primitive_desc().desc().data.format);
context_.src_size = context_.conv_fwd_pd.get()->src_primitive_desc().get_size();
context_.filter_size = context_.conv_fwd_pd.get()->weights_primitive_desc().get_size();
context_.dst_size = context_.conv_fwd_pd.get()->dst_primitive_desc().get_size();
context_.src_mem.reset(
new mkldnn::memory(context_.conv_fwd_pd.get()->src_primitive_desc(), nullptr));
context_.filter_mem.reset(
new mkldnn::memory(context_.conv_fwd_pd.get()->weights_primitive_desc(), nullptr));
context_.dst_mem.reset(
new mkldnn::memory(context_.conv_fwd_pd.get()->dst_primitive_desc(), nullptr));
if (!params.bias_dims.empty()) {
context_.bias_mem.reset(
new mkldnn::memory(context_.conv_fwd_pd.get()->bias_primitive_desc(), nullptr));
context_.conv_fwd.reset(new mkldnn::convolution_forward(
*context_.conv_fwd_pd, *context_.src_mem, *context_.filter_mem,
*context_.bias_mem, *context_.dst_mem));
} else {
context_.conv_fwd.reset(
new mkldnn::convolution_forward(*context_.conv_fwd_pd, *context_.src_mem,
*context_.filter_mem, *context_.dst_mem));
}
context_.net.push_back(*context_.conv_fwd);
}
ConvContext context_;
mkldnn::engine& cpu_engine_;
};
// Pool which allows for reuse of MKLDNN Conv primitives which are expensive to instantiate.
// To address thread safety, the primitives are stored in a map on thread local storage.
template <typename T>
class ConvPrimitivePool : public PrimitivePool<T> {
public:
static ConvPrimitive<T>* Get(const ConvParams& params) {
ConvPrimitive<T>* primitive = dynamic_cast<ConvPrimitive<T>*>(
ConvPrimitivePool<T>::GetInstance().GetPrimitive(params.ToString()));
if (primitive == nullptr) {
auto conv_primitive = onnxruntime::make_unique<ConvPrimitive<T>>(params);
primitive = conv_primitive.get();
ConvPrimitivePool<T>::GetInstance().SetPrimitive(params.ToString(), std::move(conv_primitive));
}
return primitive;
}
private:
ConvPrimitivePool() = default;
~ConvPrimitivePool() = default;
static ConvPrimitivePool& GetInstance() {
static ConvPrimitivePool pool;
return pool;
}
};
} // namespace
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];
const int group_mkl = static_cast<int>(this->conv_attrs_.group);
ORT_RETURN_IF_ERROR(this->conv_attrs_.ValidateInputShape(X, W));
std::vector<int64_t> kernel_shape;
ORT_RETURN_IF_ERROR(this->conv_attrs_.ComputeKernelShape(W->Shape(), kernel_shape));
const size_t kernel_rank = kernel_shape.size();
if (kernel_rank > 3) {
// Fall Back to CPU implementation.
return onnxruntime::Conv<T>::Compute(context);
}
if (kernel_rank + 2 != W->Shape().NumDimensions()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "kernel_shape num_dims is not compatible with W num_dims.",
" kernel_shape: ", TensorShape(kernel_shape).ToString().c_str(),
" W: ", W->Shape().ToString().c_str());
}
for (size_t i = 0; i < kernel_rank; ++i) {
if (kernel_shape[i] != W->Shape()[i + 2]) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "kernel_shape is not compatible with W shape.",
" kernel_shape: ", TensorShape(kernel_shape).ToString().c_str(),
" W: ", W->Shape().ToString().c_str());
}
}
std::vector<int64_t> pads(this->conv_attrs_.pads);
if (pads.empty()) {
pads.resize(kernel_rank * 2, 0);
}
std::vector<int64_t> dilations(this->conv_attrs_.dilations);
if (dilations.empty()) {
dilations.resize(kernel_rank, 1);
}
std::vector<int64_t> strides(this->conv_attrs_.strides);
if (strides.empty()) {
strides.resize(kernel_rank, 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(this->conv_attrs_.InferOutputShape(input_shape, kernel_shape,
strides, dilations, &pads, &Y_dims));
Tensor* Y = context->Output(0, TensorShape(Y_dims));
TensorShape output_shape = Y->Shape().Slice(2);
mkldnn::memory::dims src_dims_mkl(X->Shape().GetDims().begin(), X->Shape().GetDims().end());
mkldnn::memory::dims filter_dims_mkl;
if (group_mkl == 1) {
filter_dims_mkl.assign(W->Shape().GetDims().begin(), W->Shape().GetDims().end());
} else {
filter_dims_mkl.assign({group_mkl,
static_cast<int>(W->Shape()[0] / group_mkl)});
filter_dims_mkl.insert(filter_dims_mkl.end(), W->Shape().GetDims().begin() + 1, W->Shape().GetDims().end());
}
mkldnn::memory::dims strides_mkl(strides.begin(), strides.end());
mkldnn::memory::dims dilations_mkl(dilations.begin(), dilations.end());
// mkldnn dilations start from 0 so we need to subtract 1 from each dim.
for (size_t dim = 0; dim < kernel_rank; dim++) {
dilations_mkl[dim] -= 1;
}
mkldnn::memory::dims padding_left_mkl(pads.begin(), pads.begin() + kernel_rank);
mkldnn::memory::dims padding_right_mkl(pads.begin() + kernel_rank, pads.end());
mkldnn::memory::dims dst_dims_mkl(Y_dims.begin(), Y_dims.end());
mkldnn::memory::dims bias_dims_mkl;
if (B != nullptr) {
bias_dims_mkl.assign(B->Shape().GetDims().begin(), B->Shape().GetDims().end());
}
AllocatorPtr alloc;
ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&alloc));
IAllocatorUniquePtr<void> src_reorder_buffer;
IAllocatorUniquePtr<void> dst_reorder_buffer;
const T* src_data = X->template Data<T>();
const T* filter_data = W->template Data<T>();
T* dst_data = Y->template MutableData<T>();
const T* bias_data = nullptr;
if (B != nullptr) {
bias_data = B->template Data<T>();
}
try {
ConvParams conv_params(src_dims_mkl, filter_dims_mkl, bias_dims_mkl,
dst_dims_mkl, strides_mkl, dilations_mkl,
padding_left_mkl, padding_right_mkl);
ConvPrimitive<T>* conv_primitive = ConvPrimitivePool<T>::Get(conv_params);
auto conv_fwd_pd = conv_primitive->GetPrimitiveDesc();
mkldnn::engine& cpu_engine = GetEngine();
enum mkldnn::memory::format src_format = mkldnn::memory::format::format_undef;
enum mkldnn::memory::format filter_format = mkldnn::memory::format::format_undef;
enum mkldnn::memory::format dst_format = mkldnn::memory::format::format_undef;
if (kernel_rank == 1) {
src_format = mkldnn::memory::format::ncw;
if (group_mkl == 1) {
filter_format = mkldnn::memory::format::oiw;
} else {
filter_format = mkldnn::memory::format::goiw;
}
dst_format = mkldnn::memory::format::ncw;
} else if (kernel_rank == 2) {
src_format = mkldnn::memory::format::nchw;
if (group_mkl == 1) {
filter_format = mkldnn::memory::format::oihw;
} else {
filter_format = mkldnn::memory::format::goihw;
}
dst_format = mkldnn::memory::format::nchw;
} else {
src_format = mkldnn::memory::format::ncdhw;
if (group_mkl == 1) {
filter_format = mkldnn::memory::format::oidhw;
} else {
filter_format = mkldnn::memory::format::goidhw;
}
dst_format = mkldnn::memory::format::ncdhw;
}
auto src_md = mkldnn::memory::desc(src_dims_mkl, MklDnnType<T>(), src_format);
auto dst_md = mkldnn::memory::desc(dst_dims_mkl, MklDnnType<T>(), dst_format);
// Reorder src memory layout if necessary.
if (src_md.data.format != conv_primitive->GetSrcMemoryFormat()) {
auto pd = mkldnn::memory::primitive_desc(src_md, cpu_engine);
mkldnn::memory src = mkldnn::memory(pd, (void*)src_data);
// allocate the size queried from memory primitive desc. it may not match tensor logical size due to
// mkldnn using padding to allow use of blocked format.
src_reorder_buffer = IAllocator::MakeUniquePtr<void>(alloc, conv_primitive->GetSrcSize());
mkldnn::memory dst = mkldnn::memory(conv_fwd_pd->src_primitive_desc(), src_reorder_buffer.get());
MemoryReorderParams params(src, dst);
DoReorder<T>(params);
src_data = static_cast<T*>(dst.get_data_handle());
}
// Reorder filter memory layout if necessary
// Avoid data reordering. Save filter memory in mkldnn format from first iteration
// in execution provider mapped by weight name.
{
// lock to make sure reordering is done only once
std::lock_guard<OrtMutex> lock(provider_->GetMutex());
auto weight_name = OpKernel::Node().InputDefs()[1]->Name();
std::shared_ptr<mkldnn::memory> filter_dst_mem = provider_->GetWeightsMemoryBuffer(weight_name);
if (filter_dst_mem == nullptr) {
if (filter_format != conv_primitive->GetFilterMemoryFormat()) {
auto pd = mkldnn::memory::primitive_desc(mkldnn::memory::desc(
filter_dims_mkl, MklDnnType<T>(), filter_format),
cpu_engine);
mkldnn::memory src = mkldnn::memory(pd, (void*)filter_data);
IAllocatorUniquePtr<void> filter_reorder_buffer = IAllocator::MakeUniquePtr<void>(alloc, conv_primitive->GetFilterSize());
filter_dst_mem.reset(
new mkldnn::memory(conv_fwd_pd->weights_primitive_desc(), filter_reorder_buffer.get()));
MemoryReorderParams params(src, *filter_dst_mem);
DoReorder<T>(params);
provider_->SaveAllocatedMemory(std::move(filter_reorder_buffer));
filter_data = static_cast<T*>(filter_dst_mem->get_data_handle());
provider_->SetWeightsMemoryBuffer(weight_name, filter_dst_mem);
}
} else {
filter_data = static_cast<T*>(filter_dst_mem->get_data_handle());
}
}
// Allocate dst buffer if reorder is necessary
if (dst_md.data.format != conv_primitive->GetDstMemoryFormat()) {
// allocate the size queried from memory primitive desc. it may not match tensor logical size due to
// mkldnn using padding to allow use of blocked format.
dst_reorder_buffer = IAllocator::MakeUniquePtr<void>(alloc, conv_primitive->GetDstSize());
dst_data = static_cast<T*>(dst_reorder_buffer.get());
}
conv_primitive->Compute(src_data, filter_data, dst_data, bias_data);
// Reorder dst memory layout if necessary
if (dst_md.data.format != conv_primitive->GetDstMemoryFormat()) {
mkldnn::memory src = mkldnn::memory(conv_fwd_pd->dst_primitive_desc(), (void*)dst_data);
auto pd = mkldnn::memory::primitive_desc(dst_md, cpu_engine);
mkldnn::memory dst = mkldnn::memory(pd, Y->template MutableData<T>());
MemoryReorderParams params(src, dst);
DoReorder<T>(params);
}
} catch (const mkldnn::error& e) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Status: ", e.status, ", message: ", e.message.c_str());
}
return Status::OK();
}
ONNX_OPERATOR_KERNEL_EX(
Conv,
kOnnxDomain,
1,
kMklDnnExecutionProvider,
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
Conv<float>);
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Corporation. 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/mkldnn/mkldnn_execution_provider.h"
namespace onnxruntime {
namespace mkl_dnn {
template <typename T>
class Conv final : public onnxruntime::Conv<T> {
public:
explicit Conv(const OpKernelInfo& info) : onnxruntime::Conv<T>(info) {
provider_ = (const_cast<MKLDNNExecutionProvider*>(
dynamic_cast<const MKLDNNExecutionProvider*>(info.GetExecutionProvider())));
}
Status Compute(OpKernelContext* context) const override;
private:
MKLDNNExecutionProvider* provider_;
};
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -1,241 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifdef _WIN32
#pragma warning(disable : 4244)
#endif
#include "core/providers/mkldnn/mkldnn_common.h"
#include "core/providers/mkldnn/nn/lrn.h"
#include "core/providers/mkldnn/mkldnn_fwd.h"
namespace onnxruntime {
namespace mkl_dnn {
ONNX_OPERATOR_TYPED_KERNEL_EX(
LRN,
kOnnxDomain,
1,
float,
kMklDnnExecutionProvider,
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
LRN<float>);
namespace {
// Struct which encapsulates parameters for MKLDNN LRN primitive.
struct LRNParams {
const mkldnn::memory::dims& dims_;
float alpha_;
float beta_;
float bias_;
int size_;
LRNParams(const mkldnn::memory::dims& dims, float alpha, float beta, float bias, int size)
: dims_(dims), alpha_(alpha), beta_(beta), bias_(bias), size_(size) {}
// Used as the key for LRN Primitive Reuse LRN.
std::string ToString() const {
std::string key;
key.reserve(128);
key.append("lrn");
AddDimsToKey(key, dims_);
key.append('#' + std::to_string(alpha_) + '#');
key.append('#' + std::to_string(beta_) + '#');
key.append('#' + std::to_string(bias_) + '#');
key.append('#' + std::to_string(size_) + '#');
return key;
}
};
template <typename T>
class LRNPrimitive : public PrimitiveBase {
public:
explicit LRNPrimitive(const LRNParams& params)
: cpu_engine_(GetEngine()) {
context_.stream.reset(new mkldnn::stream(mkldnn::stream::kind::eager));
if (context_.lrn_fwd == nullptr) {
Initialize(params);
}
}
~LRNPrimitive() = default;
void Compute(const T* src_data, T* dst_data) {
context_.src_mem->set_data_handle(static_cast<void*>(const_cast<T*>(src_data)));
context_.dst_mem->set_data_handle(static_cast<void*>(dst_data));
context_.stream->submit(context_.net);
context_.src_mem->set_data_handle(nullptr);
context_.dst_mem->set_data_handle(nullptr);
return;
}
mkldnn::memory::format GetSrcMemoryFormat() const { return context_.src_fmt; }
size_t GetSrcSize() const { return context_.src_size; }
size_t GetDstSize() const { return context_.dst_size; }
mkldnn::lrn_forward::primitive_desc* GetPrimitiveDesc() const {
return context_.fwd_primitive_desc.get();
}
private:
struct LRNContext {
mkldnn::memory::format src_fmt;
std::unique_ptr<mkldnn::memory::desc> src_md;
size_t src_size;
size_t dst_size;
std::unique_ptr<mkldnn::memory> src_mem;
std::unique_ptr<mkldnn::memory> dst_mem;
std::unique_ptr<mkldnn::lrn_forward::desc> fwd_desc;
std::unique_ptr<mkldnn::lrn_forward::primitive_desc> fwd_primitive_desc;
std::unique_ptr<mkldnn::primitive> lrn_fwd;
std::unique_ptr<mkldnn::stream> stream;
std::vector<mkldnn::primitive> net;
LRNContext()
: src_fmt(mkldnn::memory::format::any),
src_md(nullptr),
src_size(0),
dst_size(0),
src_mem(nullptr),
dst_mem(nullptr),
fwd_desc(nullptr),
fwd_primitive_desc(nullptr),
lrn_fwd(nullptr),
stream(nullptr) {}
};
void Initialize(const LRNParams& params) {
context_.src_md.reset(new mkldnn::memory::desc({params.dims_}, MklDnnType<T>(), mkldnn::memory::format::nchw));
mkldnn::algorithm algo = mkldnn::algorithm::lrn_across_channels;
context_.fwd_desc.reset(new mkldnn::lrn_forward::desc(
mkldnn::prop_kind::forward_scoring, algo, *context_.src_md,
params.size_, params.alpha_, params.beta_, params.bias_));
context_.fwd_primitive_desc.reset(new mkldnn::lrn_forward::primitive_desc(
*context_.fwd_desc, cpu_engine_));
context_.src_fmt = static_cast<mkldnn::memory::format>(
context_.fwd_primitive_desc.get()->src_primitive_desc().desc().data.format);
context_.src_size = context_.fwd_primitive_desc.get()->src_primitive_desc().get_size();
context_.dst_size = context_.fwd_primitive_desc.get()->dst_primitive_desc().get_size();
context_.src_mem.reset(new mkldnn::memory(context_.fwd_primitive_desc.get()->src_primitive_desc(), nullptr));
context_.dst_mem.reset(new mkldnn::memory(context_.fwd_primitive_desc.get()->dst_primitive_desc(), nullptr));
context_.lrn_fwd.reset(
new mkldnn::lrn_forward(*context_.fwd_primitive_desc, *context_.src_mem, *context_.dst_mem));
context_.net.push_back(*context_.lrn_fwd);
}
LRNContext context_;
mkldnn::engine& cpu_engine_;
};
// Pool which allows for reuse of MKLDNN Pool primitives which are expensive to instantiate.
// To address thread safety, the primitives are stored in a map on thread local storage.
template <typename T>
class LRNPrimitivePool : public PrimitivePool<T> {
public:
static LRNPrimitive<T>* Get(const LRNParams& params) {
LRNPrimitive<T>* primitive = dynamic_cast<LRNPrimitive<T>*>(
LRNPrimitivePool<T>::GetInstance().GetPrimitive(params.ToString()));
if (primitive == nullptr) {
auto pool_primitive = onnxruntime::make_unique<LRNPrimitive<T>>(params);
primitive = pool_primitive.get();
LRNPrimitivePool<T>::GetInstance().SetPrimitive(params.ToString(), std::move(pool_primitive));
}
return primitive;
}
private:
LRNPrimitivePool() = default;
~LRNPrimitivePool() = default;
static LRNPrimitivePool& GetInstance() {
static LRNPrimitivePool pool;
return pool;
}
};
} // namespace
template <typename T>
Status LRN<T>::Compute(OpKernelContext* context) const {
const Tensor* X = context->Input<Tensor>(0);
const T* src_data = X->template Data<T>();
const TensorShape& x_shape = X->Shape();
if (x_shape.NumDimensions() != 4) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Support NCHW image only.");
}
const auto& x_dims = x_shape.GetDims();
mkldnn::memory::dims dims_mkl(x_dims.begin(), x_dims.end());
Tensor* Y = context->Output(0, TensorShape(x_dims));
T* dst_data = Y->template MutableData<T>();
AllocatorPtr alloc;
ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&alloc));
IAllocatorUniquePtr<void> src_reorder_buffer;
IAllocatorUniquePtr<void> dst_reorder_buffer;
try {
LRNParams lrn_params(dims_mkl, this->alpha_, this->beta_, this->bias_, this->size_);
LRNPrimitive<T>* lrn_primitive = LRNPrimitivePool<T>::Get(lrn_params);
auto fwd_primitive_desc = lrn_primitive->GetPrimitiveDesc();
mkldnn::engine& cpu_engine = GetEngine();
mkldnn::memory::format mem_format = dims_mkl.size() == 5 ? mkldnn::memory::format::ncdhw : mkldnn::memory::format::nchw;
// Per ONNX spec, X (src) is NCHW and Y (dst) is NCHW
auto src_md = mkldnn::memory::desc(dims_mkl, MklDnnType<T>(), mem_format);
auto dst_md = mkldnn::memory::desc(dims_mkl, MklDnnType<T>(), mem_format);
// Reorder src memory layout if necessary.
if (src_md.data.format != lrn_primitive->GetSrcMemoryFormat()) {
auto pd = mkldnn::memory::primitive_desc(src_md, cpu_engine);
mkldnn::memory src = mkldnn::memory(pd, (void*)src_data);
// allocate the size queried from memory primitive desc. it may not match tensor logical size due to
// mkldnn using padding to allow use of blocked format.
src_reorder_buffer = IAllocator::MakeUniquePtr<void>(alloc, lrn_primitive->GetSrcSize());
mkldnn::memory dst = mkldnn::memory(fwd_primitive_desc->src_primitive_desc(), src_reorder_buffer.get());
MemoryReorderParams params(src, dst);
DoReorder<T>(params);
src_data = static_cast<T*>(dst.get_data_handle());
}
// Allocate dst buffer if reorder is necessary
if (src_md.data.format != lrn_primitive->GetSrcMemoryFormat()) {
// allocate the size queried from memory primitive desc. it may not match tensor logical size due to
// mkldnn using padding to allow use of blocked format.
dst_reorder_buffer = IAllocator::MakeUniquePtr<void>(alloc, lrn_primitive->GetDstSize());
dst_data = static_cast<T*>(dst_reorder_buffer.get());
}
lrn_primitive->Compute(src_data, dst_data);
// Reorder dst memory layout if necessary
if (src_md.data.format != lrn_primitive->GetSrcMemoryFormat()) {
mkldnn::memory src = mkldnn::memory(fwd_primitive_desc->dst_primitive_desc(), (void*)dst_data);
auto pd = mkldnn::memory::primitive_desc(dst_md, cpu_engine);
mkldnn::memory dst = mkldnn::memory(pd, Y->template MutableData<T>());
MemoryReorderParams params(src, dst);
DoReorder<T>(params);
}
} catch (const mkldnn::error& e) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Status: ", e.status, ", message: ", e.message.c_str());
}
return Status::OK();
}
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -1,20 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "core/framework/op_kernel.h"
#include "core/providers/cpu/nn/lrn.h"
namespace onnxruntime {
namespace mkl_dnn {
template <typename T>
class LRN final : public onnxruntime::LRN<T> {
public:
explicit LRN(const OpKernelInfo& info) : onnxruntime::LRN<T>(info) {}
Status Compute(OpKernelContext* p_op_kernel_context) const override;
};
}
}

View file

@ -1,337 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifdef _WIN32
#pragma warning(disable : 4244)
#endif
#include "core/common/cpuid_info.h"
#include "core/providers/mkldnn/mkldnn_common.h"
#include "core/providers/mkldnn/nn/pool.h"
#include "core/providers/mkldnn/mkldnn_fwd.h"
namespace onnxruntime {
namespace mkl_dnn {
#define POOLING_KERNEL(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, \
kMklDnnExecutionProvider, \
KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<data_type>()), \
Pool<data_type, pool_type>);
POOLING_KERNEL(AveragePool, float, AveragePool, 7, 8)
POOLING_KERNEL(GlobalAveragePool, float, AveragePool, 1, 8)
POOLING_KERNEL(MaxPool, float, MaxPool<1>, 1, 7)
POOLING_KERNEL(MaxPool, float, MaxPool<8>, 8, 8)
POOLING_KERNEL(GlobalMaxPool, float, MaxPool<1>, 1, 8)
namespace {
// Struct which encapsulates parameters for MKLDNN Pool primitive.
struct PoolParams {
std::string op_name;
// Pooling primitive needs version as part of key because there
// are multiple versions of mkldnn pool kernels.
std::string version;
mkldnn::memory::dims& src_dims;
mkldnn::memory::dims& dst_dims;
mkldnn::memory::dims& kernel;
mkldnn::memory::dims& strides;
mkldnn::memory::dims& padding_left;
mkldnn::memory::dims& padding_right;
bool count_include_pad;
PoolParams(const std::string& op_name, const std::string& version,
mkldnn::memory::dims& src_dims, mkldnn::memory::dims& dst_dims,
mkldnn::memory::dims& kernel, mkldnn::memory::dims& strides,
mkldnn::memory::dims& padding_left, mkldnn::memory::dims& padding_right,
bool count_include_pad)
: op_name(op_name),
version(version),
src_dims(src_dims),
dst_dims(dst_dims),
kernel(kernel),
strides(strides),
padding_left(padding_left),
padding_right(padding_right),
count_include_pad(count_include_pad) {}
// Used as the key for Pool Primitive Reuse Pool.
std::string ToString() const {
std::string key;
key.reserve(128);
key.append(op_name);
key.append(version);
AddDimsToKey(key, src_dims);
AddDimsToKey(key, dst_dims);
AddDimsToKey(key, kernel);
AddDimsToKey(key, strides);
AddDimsToKey(key, padding_left);
AddDimsToKey(key, padding_right);
key.append(count_include_pad ? "true" : "false");
return key;
}
};
template <typename T, typename PoolType>
class PoolPrimitive : public PrimitiveBase {
public:
explicit PoolPrimitive(const PoolParams& params)
: cpu_engine_(GetEngine()) {
context_.stream.reset(new mkldnn::stream(mkldnn::stream::kind::eager));
if (context_.pool_fwd == nullptr) {
Initialize(params);
}
}
~PoolPrimitive() = default;
void Compute(const T* src_data, T* dst_data) {
context_.src_mem->set_data_handle(static_cast<void*>(const_cast<T*>(src_data)));
context_.dst_mem->set_data_handle(static_cast<void*>(dst_data));
context_.stream->submit(context_.net);
context_.src_mem->set_data_handle(nullptr);
context_.dst_mem->set_data_handle(nullptr);
return;
}
mkldnn::memory::format GetSrcMemoryFormat() const { return context_.src_fmt; }
mkldnn::memory::format GetDstMemoryFormat() const { return context_.dst_fmt; }
size_t GetSrcSize() const { return context_.src_size; }
size_t GetDstSize() const { return context_.dst_size; }
// std::unique_ptr<mkldnn::memory::desc> GetDstMemoryDesc() const { return context_.dst_md; }
mkldnn::pooling_forward::primitive_desc* GetPrimitiveDesc() const {
return context_.fwd_primitive_desc.get();
}
private:
struct PoolContext {
mkldnn::memory::format src_fmt;
mkldnn::memory::format dst_fmt;
size_t src_size;
size_t dst_size;
std::unique_ptr<mkldnn::memory> src_mem;
std::unique_ptr<mkldnn::memory> dst_mem;
std::unique_ptr<mkldnn::pooling_forward::desc> fwd_desc;
std::unique_ptr<mkldnn::memory::desc> src_md;
std::unique_ptr<mkldnn::memory::desc> dst_md;
std::unique_ptr<mkldnn::pooling_forward::primitive_desc> fwd_primitive_desc;
std::unique_ptr<mkldnn::primitive> pool_fwd;
std::unique_ptr<mkldnn::stream> stream;
std::vector<mkldnn::primitive> net;
PoolContext()
: src_fmt(mkldnn::memory::format::any),
dst_fmt(mkldnn::memory::format::any),
src_size(0),
dst_size(0),
src_mem(nullptr),
dst_mem(nullptr),
fwd_desc(nullptr),
src_md(nullptr),
fwd_primitive_desc(nullptr),
pool_fwd(nullptr),
stream(nullptr) {}
};
void Initialize(const PoolParams& params) {
bool is_2D = params.src_dims.size() == 4 ? true : false;
mkldnn::memory::format fmt = mkldnn::memory::format::any;
if (CPUIDInfo::GetCPUIDInfo().HasAVX512f()) {
fmt = is_2D ? mkldnn::memory::format::nChw16c : mkldnn::memory::format::nCdhw16c;
} else if (CPUIDInfo::GetCPUIDInfo().HasAVX2() && (params.src_dims[1] % 8 == 0)) {
fmt = is_2D ? mkldnn::memory::format::nChw8c : mkldnn::memory::format::ncdhw;
} else {
fmt = is_2D ? mkldnn::memory::format::nchw : mkldnn::memory::format::ncdhw;
}
context_.src_md.reset(new mkldnn::memory::desc(
{params.src_dims}, MklDnnType<T>(), fmt));
context_.dst_md.reset(new mkldnn::memory::desc(
{params.dst_dims}, MklDnnType<T>(), mkldnn::memory::format::any));
mkldnn::algorithm algo = mkldnn::algorithm::pooling_max;
if (PoolType::type == onnxruntime::PoolType::kAveragePool) {
algo = mkldnn::algorithm::pooling_avg_exclude_padding;
if (params.count_include_pad) {
algo = mkldnn::algorithm::pooling_avg_include_padding;
}
}
context_.fwd_desc.reset(new mkldnn::pooling_forward::desc(
mkldnn::prop_kind::forward_inference, algo,
*context_.src_md, *context_.dst_md,
params.strides, params.kernel,
params.padding_left, params.padding_right,
mkldnn::padding_kind::zero));
context_.fwd_primitive_desc.reset(new mkldnn::pooling_forward::primitive_desc(
*context_.fwd_desc, cpu_engine_));
context_.src_fmt = static_cast<mkldnn::memory::format>(
context_.fwd_primitive_desc.get()->src_primitive_desc().desc().data.format);
context_.dst_fmt = static_cast<mkldnn::memory::format>(
context_.fwd_primitive_desc.get()->dst_primitive_desc().desc().data.format);
context_.src_size = context_.fwd_primitive_desc.get()->src_primitive_desc().get_size();
context_.dst_size = context_.fwd_primitive_desc.get()->dst_primitive_desc().get_size();
context_.src_mem.reset(
new mkldnn::memory(context_.fwd_primitive_desc.get()->src_primitive_desc(), nullptr));
context_.dst_mem.reset(
new mkldnn::memory(context_.fwd_primitive_desc.get()->dst_primitive_desc(), nullptr));
context_.pool_fwd.reset(
new mkldnn::pooling_forward(*context_.fwd_primitive_desc, *context_.src_mem, *context_.dst_mem));
context_.net.push_back(*context_.pool_fwd);
}
PoolContext context_;
mkldnn::engine& cpu_engine_;
};
// Pool which allows for reuse of MKLDNN Pool primitives which are expensive to instantiate.
// To address thread safety, the primitives are stored in a map on thread local storage.
template <typename T, typename PoolType>
class PoolPrimitivePool : public PrimitivePool<T> {
public:
static PoolPrimitive<T, PoolType>* Get(const PoolParams& params) {
PoolPrimitive<T, PoolType>* primitive = dynamic_cast<PoolPrimitive<T, PoolType>*>(
PoolPrimitivePool<T, PoolType>::GetInstance().GetPrimitive(params.ToString()));
if (primitive == nullptr) {
auto pool_primitive = onnxruntime::make_unique<PoolPrimitive<T, PoolType>>(params);
primitive = pool_primitive.get();
PoolPrimitivePool<T, PoolType>::GetInstance().SetPrimitive(params.ToString(), std::move(pool_primitive));
}
return primitive;
}
private:
PoolPrimitivePool() = default;
~PoolPrimitivePool() = default;
static PoolPrimitivePool& GetInstance() {
static PoolPrimitivePool pool;
return pool;
}
};
} // namespace
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();
const auto& x_dims = x_shape.GetDims();
if (x_shape.NumDimensions() < 3) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Input dimension cannot be less than 3.");
}
if (x_shape.NumDimensions() == 3) {
// Fall Back to CPU implementation.
return onnxruntime::Pool<T, PoolType>::Compute(context);
}
std::vector<int64_t> kernel_shape = this->pool_attrs_.kernel_shape;
std::vector<int64_t> pads = this->pool_attrs_.pads;
std::vector<int64_t> strides = this->pool_attrs_.strides;
if (this->pool_attrs_.global_pooling) {
kernel_shape.assign(x_dims.begin() + 2, x_dims.end());
pads.assign(kernel_shape.size() * 2, 0);
strides.assign(kernel_shape.size(), 1);
}
std::vector<int64_t> y_dims = this->pool_attrs_.SetOutputSize(x_shape, x_shape[1], &pads);
Tensor* Y = context->Output(0, TensorShape(y_dims));
size_t num_outputs = OpKernel::Node().OutputDefs().size();
if (num_outputs == 2) {
Tensor* I = context->Output(1, TensorShape(y_dims));
if (nullptr != I) {
return onnxruntime::Pool<T, PoolType>::Compute(context);
}
}
const T* src_data = X->template Data<T>();
T* dst_data = Y->template MutableData<T>();
mkldnn::memory::dims src_dims_mkl(x_dims.begin(), x_dims.end());
mkldnn::memory::dims dst_dims_mkl(y_dims.begin(), y_dims.end());
mkldnn::memory::dims kernel_mkl(kernel_shape.begin(), kernel_shape.end());
mkldnn::memory::dims strides_mkl(strides.begin(), strides.end());
mkldnn::memory::dims padding_left_mkl(pads.begin(), pads.begin() + (pads.size() / 2));
mkldnn::memory::dims padding_right_mkl(pads.begin() + (pads.size() / 2), pads.end());
AllocatorPtr alloc;
ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&alloc));
IAllocatorUniquePtr<void> src_reorder_buffer;
IAllocatorUniquePtr<void> dst_reorder_buffer;
try {
PoolParams pool_params(this->op_name_, this->opset_version_,
src_dims_mkl, dst_dims_mkl,
kernel_mkl, strides_mkl,
padding_left_mkl, padding_right_mkl,
this->pool_attrs_.count_include_pad);
PoolPrimitive<T, PoolType>* pool_primitive = PoolPrimitivePool<T, PoolType>::Get(pool_params);
auto fwd_primitive_desc = pool_primitive->GetPrimitiveDesc();
mkldnn::engine& cpu_engine = GetEngine();
mkldnn::memory::format mem_format = src_dims_mkl.size() == 5 ? mkldnn::memory::format::ncdhw : mkldnn::memory::format::nchw;
// Per ONNX spec, X (src) is NCHW and Y (dst) is NCHW
auto src_md = mkldnn::memory::desc(src_dims_mkl, MklDnnType<T>(), mem_format);
auto dst_md = mkldnn::memory::desc(dst_dims_mkl, MklDnnType<T>(), mem_format);
// Reorder src memory layout if necessary.
if (src_md.data.format != pool_primitive->GetSrcMemoryFormat()) {
auto pd = mkldnn::memory::primitive_desc(src_md, cpu_engine);
mkldnn::memory src = mkldnn::memory(pd, (void*)src_data);
// allocate the size queried from memory primitive desc. it may not match tensor logical size due to
// mkldnn using padding to allow use of blocked format.
src_reorder_buffer = IAllocator::MakeUniquePtr<void>(alloc, pool_primitive->GetSrcSize());
mkldnn::memory dst = mkldnn::memory(fwd_primitive_desc->src_primitive_desc(), src_reorder_buffer.get());
MemoryReorderParams params(src, dst);
DoReorder<T>(params);
src_data = static_cast<T*>(dst.get_data_handle());
}
// Allocate dst buffer if reorder is necessary
if (dst_md.data.format != pool_primitive->GetDstMemoryFormat()) {
// allocate the size queried from memory primitive desc. it may not match tensor logical size due to
// mkldnn using padding to allow use of blocked format.
dst_reorder_buffer = IAllocator::MakeUniquePtr<void>(alloc, pool_primitive->GetDstSize());
dst_data = static_cast<T*>(dst_reorder_buffer.get());
}
pool_primitive->Compute(src_data, dst_data);
// Reorder dst memory layout if necessary
if (dst_md.data.format != pool_primitive->GetDstMemoryFormat()) {
mkldnn::memory src = mkldnn::memory(fwd_primitive_desc->dst_primitive_desc(), (void*)dst_data);
auto pd = mkldnn::memory::primitive_desc(dst_md, cpu_engine);
mkldnn::memory dst = mkldnn::memory(pd, Y->template MutableData<T>());
MemoryReorderParams params(src, dst);
DoReorder<T>(params);
}
} catch (const mkldnn::error& e) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Status: ", e.status, ", message: ", e.message.c_str());
}
return Status::OK();
}
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -1,29 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "core/framework/op_kernel.h"
#include "core/providers/cpu/nn/pool.h"
namespace onnxruntime {
namespace mkl_dnn {
template <typename T, typename PoolType>
class Pool final : public onnxruntime::Pool<T, PoolType> {
public:
explicit Pool(const OpKernelInfo& info) : onnxruntime::Pool<T, PoolType>(info) {
// Since there are multiple versions of Pooling kernels, we need to use
// the opset version as part of the key for caching Pooling Primitives.
int start, end;
OpKernel::KernelDef().SinceVersion(&start, &end);
opset_version_ = std::to_string(start);
}
Status Compute(OpKernelContext* context) const override;
private:
std::string opset_version_;
};
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -27,7 +27,7 @@ class MklDnnRelu : public MklDnnKernel {
OrtKernelContext* context,
mkldnn::engine& cpu_engine,
std::vector<mkldnn::primitive>& net,
mkldnn::memory::format& source_format) {
std::vector<std::unordered_map<int, mkldnn::memory>> &net_args) {
Ort::CustomOpApi ort{*api};
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
@ -44,25 +44,32 @@ class MklDnnRelu : public MklDnnKernel {
mkldnn::memory::dims dims(xdim);
ort_source_format_ = GetSourceFormat(static_cast<int>(xdim));
source_format = ort_source_format_;
src_format_ = ort_source_format_;
x_shape = TensorShape(xshape, xdim);
x_shape = TensorShape(xshape, xdim);
mkldnn::memory::dims src_dims_mkl(
if (x_shape.NumDimensions() == 0) {
primitive_created_ = Status(common::ONNXRUNTIME, common::INVALID_ARGUMENT, "Shape of size zero " + x_shape.ToString());
return primitive_created_;
}
mkldnn::memory::dims src_dims(
x_shape.GetDims().begin(), x_shape.GetDims().end());
ort_source_desc_ = mkldnn::memory::desc(
{src_dims}, MklDnnType<T>(), ort_source_format_);
source_desc_ = ort_source_desc_;
src_md_.reset(new mkldnn::memory::desc(
{src_dims_mkl}, MklDnnType<T>(), src_format_));
{src_dims}, MklDnnType<T>(), ort_source_format_));
src_mem_.reset(
new mkldnn::memory({*src_md_, cpu_engine}, nullptr));
new mkldnn::memory({{src_dims}, MklDnnType<T>(), ort_source_format_}, cpu_engine, nullptr));
} else {
src_md_.reset(
new mkldnn::memory::desc(parents_[0].get()->primitive_dst_mem_.get()->get_primitive_desc().desc()));
new mkldnn::memory::desc(parents_[0].get()->primitive_dst_desc_));
src_mem_ = parents_[0].get()->primitive_dst_mem_;
x_shape = parents_[0].get()->primitive_dst_shape_;
ort_source_format_ = source_format;
src_format_ = parents_[0].get()->primitive_dst_format_;
ort_source_format_ = parents_[0].get()->ort_source_format_;
ort_source_desc_ = parents_[0].get()->ort_source_desc_;
source_desc_ = parents_[0].get()->primitive_dst_desc_;
}
primitive_dst_shape_ = TensorShape(x_shape);
@ -75,37 +82,38 @@ class MklDnnRelu : public MklDnnKernel {
relu_fwd_pd_.reset(new mkldnn::eltwise_forward::primitive_desc(
*fwd_desc_, cpu_engine));
primitive_src_format_ = static_cast<mkldnn::memory::format>(
relu_fwd_pd_.get()->src_primitive_desc().desc().data.format);
primitive_dst_format_ = static_cast<mkldnn::memory::format>(
relu_fwd_pd_.get()->dst_primitive_desc().desc().data.format);
primitive_src_desc_ = relu_fwd_pd_.get()->src_desc();
primitive_dst_desc_ = relu_fwd_pd_.get()->dst_desc();
if (mklnode_ptr_->output_index >= 0) {
// last node of sub-graph. need to allocate memory for output_tensor
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_) {
// reorder neded. Use primitive output as input to reorder and
// allocate buffer for reorder output, final output of this subgraph
primitive_dst_mem_.reset(new mkldnn::memory(relu_fwd_pd_.get()->dst_primitive_desc()));
primitive_dst_mem_.reset(new mkldnn::memory(relu_fwd_pd_.get()->dst_desc(), cpu_engine));
} else {
// Last node but re-order not needed. Allocate buffer to output of this node
primitive_dst_mem_.reset(new mkldnn::memory(relu_fwd_pd_.get()->dst_primitive_desc(), nullptr));
primitive_dst_mem_.reset(new mkldnn::memory(relu_fwd_pd_.get()->dst_desc(), cpu_engine, nullptr));
}
} else {
// Intermediate node. Use mkldnn kernel internal memory for output and
// use this as input to next node.
primitive_dst_mem_.reset(new mkldnn::memory(relu_fwd_pd_.get()->dst_primitive_desc()));
primitive_dst_mem_.reset(new mkldnn::memory(relu_fwd_pd_.get()->dst_desc(), cpu_engine));
}
relu_fwd_.reset(
new mkldnn::eltwise_forward(*relu_fwd_pd_, *src_mem_, *primitive_dst_mem_));
new mkldnn::eltwise_forward(*relu_fwd_pd_));
net.push_back(*relu_fwd_);
net_args.push_back({{MKLDNN_ARG_SRC, *src_mem_},
{MKLDNN_ARG_DST, *primitive_dst_mem_}});
if (mklnode_ptr_->output_index >= 0) {
// one of the end nodes. Allocate output buffer memory and
// reorder is necessary
mkldnn::memory::data_type t = MklDnnType<T>();
InitDstReorderOutput(cpu_engine, t, net);
InitDstReorderOutput(cpu_engine, t, net, net_args);
}
return Status::OK();
@ -114,7 +122,10 @@ class MklDnnRelu : public MklDnnKernel {
Status Bind(const OrtCustomOpApi* api, OrtKernelContext* context) override {
Ort::CustomOpApi ort{*api};
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
if (primitive_created_ != Status::OK())
return primitive_created_;
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
if (mklnode_ptr_->parent_nodes.empty()) {
// Sub-graph's first node. Read input from input buffer
@ -129,7 +140,7 @@ class MklDnnRelu : public MklDnnKernel {
OrtValue* output = ort.KernelContext_GetOutput(context, mklnode_ptr_->output_index, &y_dims[0], static_cast<int>(primitive_dst_shape_.GetDims().size()));
T* dst_data = ort.GetTensorMutableData<T>(output);
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_) {
reorder_dst_mem_to_->set_data_handle(dst_data);
} else {
primitive_dst_mem_->set_data_handle(dst_data);

View file

@ -101,7 +101,7 @@ class MklDnnBatchNorm : public MklDnnKernel {
OrtKernelContext* context,
mkldnn::engine& cpu_engine,
std::vector<mkldnn::primitive>& net,
mkldnn::memory::format& source_format) override {
std::vector<std::unordered_map<int, mkldnn::memory>>& net_args) override {
Ort::CustomOpApi ort{*api};
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
@ -114,21 +114,25 @@ class MklDnnBatchNorm : public MklDnnKernel {
auto xshape = tensor_shape.data();
auto xdim = tensor_shape.size();
mkldnn::memory::dims dims(xdim);
ort_source_format_ = GetSourceFormat(static_cast<int>(xdim));
source_format = ort_source_format_;
src_format_ = ort_source_format_;
x_shape = TensorShape(xshape, xdim);
mkldnn::memory::dims src_dims_mkl(
mkldnn::memory::dims src_dims(
x_shape.GetDims().begin(), x_shape.GetDims().end());
ort_source_desc_ = mkldnn::memory::desc(
{src_dims}, MklDnnType<T>(), ort_source_format_);
source_desc_ = ort_source_desc_;
src_md_.reset(new mkldnn::memory::desc(
{src_dims_mkl}, MklDnnType<T>(), src_format_));
{src_dims}, MklDnnType<T>(), ort_source_format_));
} else {
src_md_.reset(new mkldnn::memory::desc(parents_[0].get()->primitive_dst_mem_.get()->get_primitive_desc().desc()));
src_md_.reset(
new mkldnn::memory::desc(parents_[0].get()->primitive_dst_desc_));
x_shape = parents_[0].get()->primitive_dst_shape_;
ort_source_format_ = source_format;
src_format_ = parents_[0].get()->primitive_dst_format_;
ort_source_format_ = parents_[0].get()->ort_source_format_;
ort_source_desc_ = parents_[0].get()->ort_source_desc_;
source_desc_ = parents_[0].get()->primitive_dst_desc_;
}
int num_dimensions = static_cast<int>(x_shape.NumDimensions());
@ -192,13 +196,13 @@ class MklDnnBatchNorm : public MklDnnKernel {
primitive_dst_shape_.GetDims().begin(), primitive_dst_shape_.GetDims().end());
scale_shift_md_.reset(new mkldnn::memory::desc(
{2, scale_dims_mkl[0]}, MklDnnType<T>(), mkldnn::memory::format::nc));
{2, scale_dims_mkl[0]}, MklDnnType<T>(), mkldnn::memory::format_tag::nc));
mean_md_.reset(new mkldnn::memory::desc(
{mean_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::x));
{mean_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::x));
var_md_.reset(new mkldnn::memory::desc(
{var_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::x));
{var_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::x));
primitive_dst_md_.reset(new mkldnn::memory::desc(
{dst_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::any));
{dst_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
// scale_shift_mem will allocate 2*C*sizeof(float) buffer
//
@ -206,18 +210,18 @@ class MklDnnBatchNorm : public MklDnnKernel {
new mkldnn::memory({*scale_shift_md_, cpu_engine}));
mean_mem_.reset(
new mkldnn::memory({*mean_md_, cpu_engine}, nullptr));
new mkldnn::memory(*mean_md_, cpu_engine, nullptr));
var_mem_.reset(
new mkldnn::memory({*var_md_, cpu_engine}, nullptr));
new mkldnn::memory(*var_md_, cpu_engine, nullptr));
batchnorm_fwd_.reset(new mkldnn::batch_normalization_forward::desc(
mkldnn::prop_kind::forward_inference, *src_md_, epsilon_,
mkldnn::batch_normalization_flag::use_scale_shift |
mkldnn::batch_normalization_flag::use_global_stats));
mkldnn::normalization_flags::use_scale_shift |
mkldnn::normalization_flags::use_global_stats));
if (fuse_relu_) {
mkldnn::primitive_attr attr;
attr.set_int_output_round_mode(mkldnn::round_mode::round_nearest);
// attr.set_int_output_round_mode(mkldnn::round_mode::round_nearest);
// Execute RELU as Fuse PostOps
const float ops_scale = 1.f;
const float ops_alpha = 0.f; // relu negative slope
@ -235,44 +239,44 @@ class MklDnnBatchNorm : public MklDnnKernel {
}
// out format of this kernel
primitive_dst_format_ = static_cast<mkldnn::memory::format>(
batchnorm_fwd_pd_.get()->dst_primitive_desc().desc().data.format);
primitive_src_format_ = static_cast<mkldnn::memory::format>(
batchnorm_fwd_pd_.get()->dst_primitive_desc().desc().data.format);
primitive_dst_desc_ = static_cast<mkldnn::memory::desc>(
batchnorm_fwd_pd_.get()->dst_desc());
primitive_src_desc_ = static_cast<mkldnn::memory::desc>(
batchnorm_fwd_pd_.get()->dst_desc());
if (mklnode_ptr_->parent_nodes.empty()) {
src_mem_.reset(
new mkldnn::memory(batchnorm_fwd_pd_.get()->src_primitive_desc(), nullptr));
new mkldnn::memory(batchnorm_fwd_pd_.get()->src_desc(), cpu_engine, nullptr));
} else {
src_mem_ = parents_[0].get()->primitive_dst_mem_;
}
if (mklnode_ptr_->output_index >= 0) {
// Use mkldnn's internal output buffer
if (primitive_dst_format_ != ort_source_format_) {
primitive_dst_mem_.reset(new mkldnn::memory(batchnorm_fwd_pd_->dst_primitive_desc()));
if (primitive_dst_desc_ != ort_source_desc_) {
primitive_dst_mem_.reset(new mkldnn::memory(batchnorm_fwd_pd_->dst_desc(), cpu_engine));
} else {
primitive_dst_mem_.reset(new mkldnn::memory(batchnorm_fwd_pd_->dst_primitive_desc(), nullptr));
primitive_dst_mem_.reset(new mkldnn::memory(batchnorm_fwd_pd_->dst_desc(), cpu_engine, nullptr));
}
} else {
// last node of sub-graph. need to allocate memory for output_tensor
primitive_dst_mem_.reset(new mkldnn::memory(batchnorm_fwd_pd_->dst_primitive_desc()));
primitive_dst_mem_.reset(new mkldnn::memory(batchnorm_fwd_pd_->dst_desc(), cpu_engine));
}
auto bn = mkldnn::batch_normalization_forward(
*batchnorm_fwd_pd_,
(const mkldnn::primitive::at)*src_mem_,
(const mkldnn::primitive::at)*mean_mem_,
(const mkldnn::primitive::at)*var_mem_,
(const mkldnn::memory)*scale_shift_mem_,
(const mkldnn::memory)*primitive_dst_mem_);
*batchnorm_fwd_pd_);
net.push_back(bn);
net_args.push_back({{MKLDNN_ARG_SRC, *src_mem_},
{MKLDNN_ARG_MEAN, *mean_mem_},
{MKLDNN_ARG_VARIANCE, *var_mem_},
{MKLDNN_ARG_SCALE_SHIFT, *scale_shift_mem_},
{MKLDNN_ARG_DST, *primitive_dst_mem_}});
// Allocate dst buffer if reorder is necessary
if (mklnode_ptr_->output_index >= 0) {
// one of the end nodes. Allocate output buffer memory and
// reorder is necessary
mkldnn::memory::data_type t = MklDnnType<T>();
InitDstReorderOutput(cpu_engine, t, net);
InitDstReorderOutput(cpu_engine, t, net, net_args);
}
return Status::OK();
}
@ -330,7 +334,7 @@ class MklDnnBatchNorm : public MklDnnKernel {
OrtValue* output = ort.KernelContext_GetOutput(context, mklnode_ptr_->output_index, &y_dims[0], static_cast<int>(primitive_dst_shape_.GetDims().size()));
T* dst_data = ort.GetTensorMutableData<T>(output);
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_) {
reorder_dst_mem_to_->set_data_handle(dst_data);
} else {
primitive_dst_mem_->set_data_handle(dst_data);

View file

@ -74,8 +74,9 @@ class MklDnnConv : public MklDnnKernel {
OrtKernelContext* context,
mkldnn::engine& cpu_engine,
std::vector<mkldnn::primitive>& net,
mkldnn::memory::format& source_format) override {
std::vector<std::unordered_map<int, mkldnn::memory>>& net_args) override {
Ort::CustomOpApi ort{*api};
stream_.reset(new mkldnn::stream(cpu_engine));
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
const OrtValue* winput_tensor = ort.KernelContext_GetInput(context, input_index + 1);
@ -97,21 +98,18 @@ class MklDnnConv : public MklDnnKernel {
ort.ReleaseTensorTypeAndShapeInfo(tensor_info);
auto xshape = tensor_shape.data();
auto xdim = tensor_shape.size();
ort_source_format_ = mkldnn::memory::format_tag::any;
x_shape = TensorShape(xshape, xdim);
mkldnn::memory::dims src_dims_mkl(x_shape.GetDims().begin(), x_shape.GetDims().end());
src_md_.reset(new mkldnn::memory::desc(
{src_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::any));
} else {
// get the output of previous node (mkldnn block propagation).
// TODO Sourcenode will set src of this node.
x_shape = parents_[0].get()->primitive_dst_shape_;
ort_source_format_ = source_format;
src_format_ = parents_[0].get()->primitive_dst_format_;
mkldnn::memory::dims src_dims_mkl(x_shape.GetDims().begin(), x_shape.GetDims().end());
src_md_.reset(new mkldnn::memory::desc(
{src_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::any));
ort_source_format_ = parents_[0].get()->ort_source_format_;
ort_source_desc_ = parents_[0].get()->ort_source_desc_;
source_desc_ = parents_[0].get()->primitive_dst_desc_;
mkldnn::memory::dims src_dims_mkl(x_shape.GetDims().begin(), x_shape.GetDims().end());
src_md_.reset(new mkldnn::memory::desc(source_desc_));
}
primitive_created_ = ValidateInputShape(x_shape, w_shape);
@ -168,7 +166,7 @@ class MklDnnConv : public MklDnnKernel {
TensorShape output_shape = y_shape.Slice(2);
mkldnn::memory::dims dst_dims_mkl(y_dims.begin(), y_dims.end());
primitive_dst_md_.reset(new mkldnn::memory::desc(
{dst_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::any));
{dst_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
mkldnn::memory::dims filter_dims_mkl;
if (group_mkl == 1) {
@ -199,60 +197,65 @@ class MklDnnConv : public MklDnnKernel {
bias_dims_mkl.assign(b_shape.GetDims().begin(), b_shape.GetDims().end());
}
auto fmt = mkldnn::memory::format::any;
auto src_format = mkldnn::memory::format_tag::any;
if (kernel_rank == 1) {
fmt = mkldnn::memory::format::ncw;
src_format = mkldnn::memory::format_tag::ncw;
if (group_mkl == 1) {
filter_format_ = mkldnn::memory::format::oiw;
filter_format_ = mkldnn::memory::format_tag::oiw;
} else {
filter_format_ = mkldnn::memory::format::goiw;
filter_format_ = mkldnn::memory::format_tag::goiw;
}
} else if (kernel_rank == 2) {
fmt = mkldnn::memory::format::nchw;
src_format = mkldnn::memory::format_tag::nchw;
if (group_mkl == 1) {
filter_format_ = mkldnn::memory::format::oihw;
filter_format_ = mkldnn::memory::format_tag::oihw;
} else {
filter_format_ = mkldnn::memory::format::goihw;
filter_format_ = mkldnn::memory::format_tag::goihw;
}
} else {
fmt = mkldnn::memory::format::ncdhw;
src_format = mkldnn::memory::format_tag::ncdhw;
if (group_mkl == 1) {
filter_format_ = mkldnn::memory::format::oidhw;
filter_format_ = mkldnn::memory::format_tag::oidhw;
} else {
filter_format_ = mkldnn::memory::format::goidhw;
filter_format_ = mkldnn::memory::format_tag::goidhw;
}
}
if (src_format_ == mkldnn::memory::format::any) {
src_format_ = fmt;
ort_source_format_ = fmt;
source_format = fmt;
if (mklnode_ptr_->parent_nodes.empty()) {
mkldnn::memory::dims src_dims_mkl(x_shape.GetDims().begin(), x_shape.GetDims().end());
ort_source_format_ = src_format;
src_md_.reset(new mkldnn::memory::desc({src_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
ort_source_desc_ = mkldnn::memory::desc({src_dims_mkl}, MklDnnType<T>(), src_format);
source_desc_ = mkldnn::memory::desc({src_dims_mkl}, MklDnnType<T>(), src_format);
}
// Set the memory descriptors to format::any to allow MKLDNN to decide what the optimal memory layout should be
// for the computation given the input
filter_md_.reset(new mkldnn::memory::desc(
{filter_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::any));
{filter_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
if (!bias_dims_mkl.empty())
bias_md_.reset(new mkldnn::memory::desc(
{bias_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::any));
{bias_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
mkldnn::memory::dims conv_zero_padding = {0, 0};
if (!bias_dims_mkl.empty()) {
fwd_desc_.reset(new mkldnn::convolution_forward::desc(
mkldnn::prop_kind::forward_inference, mkldnn::convolution_direct, *src_md_,
mkldnn::prop_kind::forward_inference, mkldnn::algorithm::convolution_direct, *src_md_,
*filter_md_, *bias_md_, *primitive_dst_md_,
strides_mkl, dilations_mkl, padding_left_mkl,
padding_right_mkl, mkldnn::padding_kind::zero));
padding_right_mkl));
} else {
fwd_desc_.reset(new mkldnn::convolution_forward::desc(
mkldnn::prop_kind::forward_inference, mkldnn::convolution_direct, *src_md_,
mkldnn::prop_kind::forward_inference, mkldnn::algorithm::convolution_direct, *src_md_,
*filter_md_, *primitive_dst_md_, strides_mkl,
dilations_mkl, padding_left_mkl,
padding_right_mkl, mkldnn::padding_kind::zero));
dilations_mkl, padding_left_mkl, padding_right_mkl));
}
if (fuse_relu_) {
mkldnn::primitive_attr attr;
attr.set_int_output_round_mode(mkldnn::round_mode::round_nearest);
// attr.set_int_output_round_mode(mkldnn::round_mode::round_nearest);
// Execute RELU as Fuse PostOps
const float ops_scale = 1.f;
const float ops_alpha = 0.f; // relu negative slope
@ -268,37 +271,38 @@ class MklDnnConv : public MklDnnKernel {
*fwd_desc_, cpu_engine));
}
primitive_src_format_ = static_cast<mkldnn::memory::format>(
conv_fwd_pd_.get()->src_primitive_desc().desc().data.format);
primitive_src_desc_ = static_cast<mkldnn::memory::desc>(
conv_fwd_pd_.get()->src_desc());
mkldnn_filter_format_ = static_cast<mkldnn::memory::format>(
conv_fwd_pd_.get()->weights_primitive_desc().desc().data.format);
filter_desc_= static_cast<mkldnn::memory::desc>(
conv_fwd_pd_.get()->weights_desc());
primitive_dst_format_ = static_cast<mkldnn::memory::format>(
conv_fwd_pd_.get()->dst_primitive_desc().desc().data.format);
primitive_dst_desc_ = static_cast<mkldnn::memory::desc>(
conv_fwd_pd_.get()->dst_desc());
src_size_ = conv_fwd_pd_.get()->src_primitive_desc().get_size();
filter_size_ = conv_fwd_pd_.get()->weights_primitive_desc().get_size();
dst_size_ = conv_fwd_pd_.get()->dst_primitive_desc().get_size();
src_size_ = conv_fwd_pd_.get()->src_desc().get_size();
filter_size_ = conv_fwd_pd_.get()->weights_desc().get_size();
dst_size_ = conv_fwd_pd_.get()->dst_desc().get_size();
filter_mem_.reset(
new mkldnn::memory(conv_fwd_pd_.get()->weights_primitive_desc(), nullptr));
new mkldnn::memory(conv_fwd_pd_.get()->weights_desc(), cpu_engine, nullptr));
if (primitive_src_format_ != src_format_) {
mkldnn::memory::dims src_dims_mkl(x_shape.GetDims().begin(), x_shape.GetDims().end());
auto src_md = mkldnn::memory::desc(src_dims_mkl, MklDnnType<T>(), src_format_);
auto pd = mkldnn::memory::primitive_desc(src_md, cpu_engine);
if (primitive_src_desc_ != source_desc_) {
mkldnn::memory::dims src_dims(x_shape.GetDims().begin(), x_shape.GetDims().end());
auto pd = mkldnn::memory::desc({{src_dims}, MklDnnType<T>(), ort_source_format_});
if (mklnode_ptr_->parent_nodes.empty())
src_mem_from_.reset(new mkldnn::memory(pd, nullptr));
src_mem_from_.reset(new mkldnn::memory(pd, cpu_engine, nullptr));
else
src_mem_from_ = parents_[0].get()->primitive_dst_mem_;
src_mem_.reset(new mkldnn::memory(conv_fwd_pd_->src_primitive_desc(), nullptr));
src_mem_.reset(new mkldnn::memory(conv_fwd_pd_->src_desc(), cpu_engine, nullptr));
net.push_back(mkldnn::reorder(*src_mem_from_, *src_mem_));
net_args.push_back({{MKLDNN_ARG_FROM, *src_mem_from_},
{MKLDNN_ARG_TO, *src_mem_}});
} else {
if (mklnode_ptr_->parent_nodes.empty()) {
src_mem_.reset(new mkldnn::memory(conv_fwd_pd_->src_primitive_desc(), nullptr));
src_mem_.reset(new mkldnn::memory(conv_fwd_pd_->src_desc(), cpu_engine, nullptr));
} else {
src_mem_ = parents_[0].get()->primitive_dst_mem_;
}
@ -306,31 +310,36 @@ class MklDnnConv : public MklDnnKernel {
if (mklnode_ptr_->output_index >= 0) {
// Use mkldnn's internal output buffer
if (primitive_dst_format_ != ort_source_format_) {
primitive_dst_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->dst_primitive_desc()));
if (primitive_dst_desc_ != ort_source_desc_) {
primitive_dst_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->dst_desc(), cpu_engine));
} else {
primitive_dst_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->dst_primitive_desc(), nullptr));
primitive_dst_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->dst_desc(), cpu_engine, nullptr));
}
} else {
// last node of sub-graph. need to allocate memory for output_tensor
primitive_dst_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->dst_primitive_desc()));
primitive_dst_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->dst_desc(), cpu_engine));
}
if (!bias_dims_mkl.empty()) {
bias_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->bias_primitive_desc(), nullptr));
conv_fwd_.reset(new mkldnn::convolution_forward(*conv_fwd_pd_, *src_mem_, *filter_mem_,
*bias_mem_, *primitive_dst_mem_));
bias_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->bias_desc(), cpu_engine, nullptr));
conv_fwd_.reset(new mkldnn::convolution_forward(*conv_fwd_pd_));
net.push_back(*conv_fwd_);
net_args.push_back({{MKLDNN_ARG_SRC, *src_mem_},
{MKLDNN_ARG_WEIGHTS, *filter_mem_},
{MKLDNN_ARG_BIAS, *bias_mem_},
{MKLDNN_ARG_DST, *primitive_dst_mem_}});
} else {
conv_fwd_.reset(new mkldnn::convolution_forward(*conv_fwd_pd_, *src_mem_,
*filter_mem_, *primitive_dst_mem_));
conv_fwd_.reset(new mkldnn::convolution_forward(*conv_fwd_pd_));
net.push_back(*conv_fwd_);
net_args.push_back({{MKLDNN_ARG_SRC, *src_mem_},
{MKLDNN_ARG_WEIGHTS, *filter_mem_},
{MKLDNN_ARG_DST, *primitive_dst_mem_}});
}
net.push_back(*conv_fwd_);
if (mklnode_ptr_->output_index >= 0) {
// one of the end nodes. Allocate output buffer memory and
// reorder is necessary
mkldnn::memory::data_type t = MklDnnType<T>();
InitDstReorderOutput(cpu_engine, t, net);
InitDstReorderOutput(cpu_engine, t, net, net_args);
}
primitive_created_ = Status::OK();
return primitive_created_;
@ -367,17 +376,16 @@ class MklDnnConv : public MklDnnKernel {
std::shared_ptr<mkldnn::memory> filter_dst_mem = provider_->GetWeightsMemoryBuffer(mklnode_ptr_->weight_name);
if (filter_dst_mem == nullptr) {
auto pd = mkldnn::memory::primitive_desc(
mkldnn::memory::desc(filter_dims_mkl, MklDnnType<T>(), filter_format_), cpu_engine);
mkldnn::memory src = mkldnn::memory(pd, (void*)filter_data);
mkldnn::memory src = mkldnn::memory({{filter_dims_mkl}, MklDnnType<T>(), filter_format_}, cpu_engine, (void*)filter_data);
IAllocatorUniquePtr<void> filter_reorder_buffer =
IAllocator::MakeUniquePtr<void>(alloc_, filter_size_);
filter_dst_mem.reset(
new mkldnn::memory(conv_fwd_pd_->weights_primitive_desc(), filter_reorder_buffer.get()));
new mkldnn::memory(conv_fwd_pd_->weights_desc(), cpu_engine, filter_reorder_buffer.get()));
mkldnn::reorder(src, *filter_dst_mem)
.execute(cpu_engine, src, *filter_dst_mem);
MemoryReorderParams params(src, *filter_dst_mem);
DoReorder<T>(params);
provider_->SaveAllocatedMemory(std::move(filter_reorder_buffer));
provider_->SaveAllocatedMemory(std::move(filter_reorder_buffer));
filter_data = static_cast<T*>(filter_dst_mem->get_data_handle());
provider_->SetWeightsMemoryBuffer(mklnode_ptr_->weight_name, filter_dst_mem);
}
@ -417,7 +425,7 @@ class MklDnnConv : public MklDnnKernel {
bias_mem_->set_data_handle(static_cast<void*>(const_cast<T*>(bias_data)));
}
if (primitive_src_format_ != src_format_) {
if (primitive_src_desc_ != source_desc_) {
if (mklnode_ptr_->parent_nodes.empty()) {
const OrtValue* input_tensor = ort.KernelContext_GetInput(context, input_index);
const T* src_data = const_cast<T*>(ort.GetTensorData<T>(input_tensor));
@ -426,7 +434,7 @@ class MklDnnConv : public MklDnnKernel {
src_mem_from_ = parents_[0].get()->primitive_dst_mem_;
}
auto src_size = conv_fwd_pd_.get()->src_primitive_desc().get_size();
auto src_size = conv_fwd_pd_.get()->src_desc().get_size();
src_reorder_buffer_ = IAllocator::MakeUniquePtr<void>(alloc_, src_size);
src_mem_->set_data_handle(src_reorder_buffer_.get());
} else {
@ -445,7 +453,7 @@ class MklDnnConv : public MklDnnKernel {
OrtValue* output = ort.KernelContext_GetOutput(context, mklnode_ptr_->output_index, &y_dims[0], static_cast<int>(primitive_dst_shape_.GetDims().size()));
T* dst_data = ort.GetTensorMutableData<T>(output);
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_) {
reorder_dst_mem_to_->set_data_handle(dst_data);
} else {
primitive_dst_mem_->set_data_handle(dst_data);
@ -514,8 +522,8 @@ class MklDnnConv : public MklDnnKernel {
}
private:
mkldnn::memory::format mkldnn_filter_format_;
mkldnn::memory::format filter_format_;
mkldnn::memory::desc filter_desc_;
mkldnn::memory::format_tag filter_format_;
std::shared_ptr<mkldnn::memory> src_mem_from_;
std::unique_ptr<mkldnn::memory> src_mem_to_;
@ -623,6 +631,8 @@ class MklDnnConv : public MklDnnKernel {
}
private:
std::unique_ptr<mkldnn::stream> stream_;
std::vector<int64_t> kernel_shape_; // must use ComputeKernelShape(...), instead of kernel_shape_
AutoPadType auto_pad_;
int64_t group_;

View file

@ -0,0 +1,696 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "mkldnn_types.h"
#include "core/framework/op_kernel.h"
#include "core/providers/mkldnn/mkldnn_fwd.h"
#include "core/providers/cpu/nn/autopad_type.h"
#include "core/providers/mkldnn/mkldnn_execution_provider.h"
#include "core/providers/mkldnn/subgraph/mkldnn_kernel.h"
#include "core/util/math.h"
namespace onnxruntime {
namespace mkl_dnn {
template <typename T>
class MklDnnConvBatchNorm : public MklDnnKernel {
public:
MklDnnConvBatchNorm(const MklDnnNode& node,
MKLDNNExecutionProvider* provider,
const NodeAttributes& attributes,
const std::string attributes_prefix = "") : MklDnnKernel(node, provider) {
ReadAttributes(attributes, attributes_prefix);
}
Status CreatePrimitives(const OrtCustomOpApi* api,
OrtKernelContext* context,
mkldnn::engine& cpu_engine,
std::vector<mkldnn::primitive>& net,
std::vector<std::unordered_map<int, mkldnn::memory>>& net_args) override {
Ort::CustomOpApi ort{*api};
stream_.reset(new mkldnn::stream(cpu_engine));
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
const OrtValue* winput_tensor = ort.KernelContext_GetInput(context, input_index + 1);
auto wtensor_info = ort.GetTensorTypeAndShape(winput_tensor);
auto wtensor_shape = ort.GetTensorShape(wtensor_info);
ort.ReleaseTensorTypeAndShapeInfo(wtensor_info);
auto wshape = wtensor_shape.data();
auto wdim = wtensor_shape.size();
TensorShape w_shape(wshape, wdim);
const int group_mkl = static_cast<int>(group_);
TensorShape x_shape;
// std::unique_ptr<TensorShape> x_shape;
if (mklnode_ptr_->parent_nodes.empty()) {
const OrtValue* input_tensor = ort.KernelContext_GetInput(context, input_index);
auto tensor_info = ort.GetTensorTypeAndShape(input_tensor);
auto tensor_shape = ort.GetTensorShape(tensor_info);
ort.ReleaseTensorTypeAndShapeInfo(tensor_info);
auto xshape = tensor_shape.data();
auto xdim = tensor_shape.size();
ort_source_format_ = mkldnn::memory::format_tag::any;
x_shape = TensorShape(xshape, xdim);
} else {
// get the output of previous node (mkldnn block propagation).
// TODO Sourcenode will set src of this node.
x_shape = parents_[0].get()->primitive_dst_shape_;
ort_source_format_ = parents_[0].get()->ort_source_format_;
ort_source_desc_ = parents_[0].get()->ort_source_desc_;
source_desc_ = parents_[0].get()->primitive_dst_desc_;
}
primitive_created_ = ValidateInputShape(x_shape, w_shape);
if (!primitive_created_.IsOK())
return primitive_created_;
std::vector<int64_t> kernel_shape;
primitive_created_ = ComputeKernelShape(w_shape, kernel_shape);
if (!primitive_created_.IsOK())
return primitive_created_;
const size_t kernel_rank = kernel_shape.size();
if (kernel_rank + 2 != wdim) {
primitive_created_ = ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "kernel_shape num_dims is not compatible with W num_dims.",
" kernel_shape: ", TensorShape(kernel_shape).ToString().c_str(),
" W: ", w_shape.ToString().c_str());
return primitive_created_;
}
for (size_t i = 0; i < kernel_rank; ++i) {
if (kernel_shape[i] != w_shape[i + 2]) {
primitive_created_ = ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "kernel_shape is not compatible with W shape.",
" kernel_shape: ", TensorShape(kernel_shape).ToString().c_str(),
" W: ", w_shape.ToString().c_str());
return primitive_created_;
}
}
std::vector<int64_t> pads(pads_);
if (pads.empty()) {
pads.resize(kernel_rank * 2, 0);
}
std::vector<int64_t> dilations(dilations_);
if (dilations.empty()) {
dilations.resize(kernel_rank, 1);
}
std::vector<int64_t> strides(strides_);
if (strides.empty()) {
strides.resize(kernel_rank, 1);
}
const int64_t N = x_shape[0];
const int64_t M = w_shape[0];
std::vector<int64_t> y_dims;
y_dims.insert(y_dims.begin(), {N, M});
TensorShape input_shape = x_shape.Slice(2);
primitive_created_ = InferOutputShape(input_shape, kernel_shape, strides, dilations, &pads, &y_dims);
if (!primitive_created_.IsOK())
return primitive_created_;
TensorShape y_shape(y_dims);
primitive_dst_shape_ = TensorShape(y_dims);
TensorShape output_shape = y_shape.Slice(2);
mkldnn::memory::dims dst_dims_mkl(y_dims.begin(), y_dims.end());
primitive_dst_md_.reset(new mkldnn::memory::desc(
{dst_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
mkldnn::memory::dims filter_dims_mkl;
if (group_mkl == 1) {
filter_dims_mkl.assign(w_shape.GetDims().begin(), w_shape.GetDims().end());
} else {
filter_dims_mkl.assign({group_mkl,
static_cast<int>(w_shape[0] / group_mkl)});
filter_dims_mkl.insert(filter_dims_mkl.end(), w_shape.GetDims().begin() + 1, w_shape.GetDims().end());
}
mkldnn::memory::dims strides_mkl(strides.begin(), strides.end());
mkldnn::memory::dims dilations_mkl(dilations.begin(), dilations.end());
// mkldnn dilations start from 0 so we need to subtract 1 from each dim.
for (size_t dim = 0; dim < kernel_rank; dim++) {
dilations_mkl[dim] -= 1;
}
mkldnn::memory::dims padding_left_mkl(pads.begin(), pads.begin() + kernel_rank);
mkldnn::memory::dims padding_right_mkl(pads.begin() + kernel_rank, pads.end());
mkldnn::memory::dims bias_dims_mkl;
int batchNormIndix = (mklnode_ptr_->num_inputs == 7) ? input_index + 3 : input_index + 2;
if (mklnode_ptr_->num_inputs == 7) {
const OrtValue* binput_tensor = ort.KernelContext_GetInput(context, input_index + 2);
auto btensor_info = ort.GetTensorTypeAndShape(binput_tensor);
auto btensor_shape = ort.GetTensorShape(btensor_info);
ort.ReleaseTensorTypeAndShapeInfo(btensor_info);
auto bshape = btensor_shape.data();
auto bdim = btensor_shape.size();
TensorShape b_shape(bshape, bdim);
bias_dims_mkl.assign(b_shape.GetDims().begin(), b_shape.GetDims().end());
} else {
const OrtValue* b_input_tensor = ort.KernelContext_GetInput(context, batchNormIndix + 1);
auto b_tensor_info = ort.GetTensorTypeAndShape(b_input_tensor);
auto b_tensor_shape = ort.GetTensorShape(b_tensor_info);
ort.ReleaseTensorTypeAndShapeInfo(b_tensor_info);
auto bshape = b_tensor_shape.data();
auto bdim = b_tensor_shape.size();
TensorShape b_shape(bshape, bdim);
bias_dims_mkl.assign(b_shape.GetDims().begin(), b_shape.GetDims().end());
}
auto src_format = mkldnn::memory::format_tag::any;
if (kernel_rank == 1) {
src_format = mkldnn::memory::format_tag::ncw;
if (group_mkl == 1) {
filter_format_ = mkldnn::memory::format_tag::oiw;
} else {
filter_format_ = mkldnn::memory::format_tag::goiw;
}
} else if (kernel_rank == 2) {
src_format = mkldnn::memory::format_tag::nchw;
if (group_mkl == 1) {
filter_format_ = mkldnn::memory::format_tag::oihw;
} else {
filter_format_ = mkldnn::memory::format_tag::goihw;
}
} else {
src_format = mkldnn::memory::format_tag::ncdhw;
if (group_mkl == 1) {
filter_format_ = mkldnn::memory::format_tag::oidhw;
} else {
filter_format_ = mkldnn::memory::format_tag::goidhw;
}
}
mkldnn::memory::dims src_dims_mkl(x_shape.GetDims().begin(), x_shape.GetDims().end());
if (mklnode_ptr_->parent_nodes.empty()) {
ort_source_format_ = src_format;
ort_source_desc_ = mkldnn::memory::desc({src_dims_mkl}, MklDnnType<T>(), src_format);
source_desc_ = mkldnn::memory::desc({src_dims_mkl}, MklDnnType<T>(), src_format);
}
src_md_.reset(new mkldnn::memory::desc({src_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
// Set the memory descriptors to format::any to allow MKLDNN to decide what the optimal memory layout should be
// for the computation given the input
filter_md_.reset(new mkldnn::memory::desc(
{filter_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
bias_md_.reset(new mkldnn::memory::desc(
{bias_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
mkldnn::memory::dims conv_zero_padding = {0, 0};
fwd_desc_.reset(new mkldnn::convolution_forward::desc(
mkldnn::prop_kind::forward_inference, mkldnn::algorithm::convolution_direct, *src_md_,
*filter_md_, *bias_md_, *primitive_dst_md_,
strides_mkl, dilations_mkl, padding_left_mkl,
padding_right_mkl));
if (fuse_relu_) {
mkldnn::primitive_attr attr;
// attr.set_int_output_round_mode(mkldnn::round_mode::round_nearest);
// Execute RELU as Fuse PostOps
const float ops_scale = 1.f;
const float ops_alpha = 0.f; // relu negative slope
const float ops_beta = 0.f;
mkldnn::post_ops ops;
ops.append_eltwise(ops_scale, mkldnn::algorithm::eltwise_relu, ops_alpha, ops_beta);
attr.set_post_ops(ops);
conv_fwd_pd_.reset(new mkldnn::convolution_forward::primitive_desc(
*fwd_desc_, attr, cpu_engine));
} else {
conv_fwd_pd_.reset(new mkldnn::convolution_forward::primitive_desc(
*fwd_desc_, cpu_engine));
}
primitive_src_desc_ = static_cast<mkldnn::memory::desc>(
conv_fwd_pd_.get()->src_desc());
filter_desc_ = static_cast<mkldnn::memory::desc>(
conv_fwd_pd_.get()->weights_desc());
primitive_dst_desc_ = static_cast<mkldnn::memory::desc>(
conv_fwd_pd_.get()->dst_desc());
src_size_ = conv_fwd_pd_.get()->src_desc().get_size();
filter_size_ = conv_fwd_pd_.get()->weights_desc().get_size();
dst_size_ = conv_fwd_pd_.get()->dst_desc().get_size();
filter_mem_.reset(
new mkldnn::memory(conv_fwd_pd_.get()->weights_desc(), cpu_engine, nullptr));
if (primitive_src_desc_ != source_desc_) {
mkldnn::memory::dims src_dims(x_shape.GetDims().begin(), x_shape.GetDims().end());
auto pd = mkldnn::memory::desc({{src_dims}, MklDnnType<T>(), ort_source_format_});
if (mklnode_ptr_->parent_nodes.empty())
src_mem_from_.reset(new mkldnn::memory(pd, cpu_engine, nullptr));
else
src_mem_from_ = parents_[0].get()->primitive_dst_mem_;
src_mem_.reset(new mkldnn::memory(conv_fwd_pd_->src_desc(), cpu_engine, nullptr));
net.push_back(mkldnn::reorder(*src_mem_from_, *src_mem_));
net_args.push_back({{MKLDNN_ARG_FROM, *src_mem_from_},
{MKLDNN_ARG_TO, *src_mem_}});
} else {
if (mklnode_ptr_->parent_nodes.empty()) {
src_mem_.reset(new mkldnn::memory(conv_fwd_pd_->src_desc(), cpu_engine, nullptr));
} else {
src_mem_ = parents_[0].get()->primitive_dst_mem_;
}
}
if (mklnode_ptr_->output_index >= 0) {
// Use mkldnn's internal output buffer
if (primitive_dst_desc_ != ort_source_desc_) {
primitive_dst_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->dst_desc(), cpu_engine));
} else {
primitive_dst_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->dst_desc(), cpu_engine, nullptr));
}
} else {
// last node of sub-graph. need to allocate memory for output_tensor
primitive_dst_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->dst_desc(), cpu_engine));
}
bias_mem_.reset(new mkldnn::memory(conv_fwd_pd_.get()->bias_desc(), cpu_engine, nullptr));
conv_fwd_.reset(new mkldnn::convolution_forward(*conv_fwd_pd_));
net.push_back(*conv_fwd_);
net_args.push_back({{MKLDNN_ARG_SRC, *src_mem_},
{MKLDNN_ARG_WEIGHTS, *filter_mem_},
{MKLDNN_ARG_BIAS, *bias_mem_},
{MKLDNN_ARG_DST, *primitive_dst_mem_}});
if (mklnode_ptr_->output_index >= 0) {
// one of the end nodes. Allocate output buffer memory and
// reorder is necessary
mkldnn::memory::data_type t = MklDnnType<T>();
InitDstReorderOutput(cpu_engine, t, net, net_args);
}
primitive_created_ = Status::OK();
return primitive_created_;
}
void GamaInverseVariance(const OrtCustomOpApi* api, OrtKernelContext* context, std::vector<float>& inv_scale_factor, size_t O) {
Ort::CustomOpApi ort{*api};
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
int batchNormIndix = (mklnode_ptr_->num_inputs == 7) ? input_index + 3 : input_index + 2;
const OrtValue* scale_input_tensor = ort.KernelContext_GetInput(context, batchNormIndix);
const T* bn_scale_data = reinterpret_cast<const T*>(ort.GetTensorData<T>(scale_input_tensor));
const OrtValue* var_input_tensor = ort.KernelContext_GetInput(context, batchNormIndix + 3);
const T* bn_var_data = reinterpret_cast<const T*>(ort.GetTensorData<T>(var_input_tensor));
std::vector<float> inv_scale;
inv_scale.assign(static_cast<size_t>(O), 0.0f);
float* data = inv_scale_factor.data();
for (size_t i = 0; i < O; i++) {
data[i] = bn_scale_data[i] / std::sqrt(bn_var_data[i] + epsilon_);
}
}
void WeightsScaleByAxix(const OrtCustomOpApi* api, OrtKernelContext* context,
std::vector<float>& weights_scaled, std::vector<float> inv_scale,
TensorShape& W) {
Ort::CustomOpApi ort{*api};
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
const OrtValue* input_tensor = ort.KernelContext_GetInput(context, input_index + 1);
const auto& w_dims = W.GetDims();
const T* filter_data = const_cast<T*>(ort.GetTensorData<T>(input_tensor));
int batchNormIndix = (mklnode_ptr_->num_inputs == 7) ? input_index + 3 : input_index + 2;
const OrtValue* scale_input_tensor = ort.KernelContext_GetInput(context, batchNormIndix);
auto bn_tensor_info = ort.GetTensorTypeAndShape(scale_input_tensor);
auto bn_tensor_shape = ort.GetTensorShape(bn_tensor_info);
ort.ReleaseTensorTypeAndShapeInfo(bn_tensor_info);
auto bn_shape = bn_tensor_shape.data();
auto bn_dim = bn_tensor_shape.size();
TensorShape bn_scale_shape(bn_shape, bn_dim);
const auto& bn_dims = bn_scale_shape.GetDims();
int64_t num = 1;
int axis = 1;
for (size_t k = axis; k < w_dims.size(); k++) {
num *= w_dims[k];
}
int64_t w_size = std::accumulate(w_dims.begin(), w_dims.end(), static_cast<int64_t>(1), std::multiplies<int64_t>{});
int64_t bn_scale_size = std::accumulate(bn_dims.begin(), bn_dims.end(), static_cast<int64_t>(1), std::multiplies<int64_t>{});
int64_t n = w_size / num;
float* w_scale_data = weights_scaled.data();
for (auto i = 0; i < n; i++) {
int index = bn_scale_size == 1 ? 0 : i;
for (int64_t j = 0; j < num; j++) {
w_scale_data[i * num + j] = filter_data[i * num + j] * inv_scale[index];
}
}
}
virtual void ReorderWeights(const OrtCustomOpApi* api, OrtKernelContext* context, mkldnn::engine& cpu_engine) override {
Ort::CustomOpApi ort{*api};
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
const OrtValue* input_tensor = ort.KernelContext_GetInput(context, input_index + 1);
auto tensor_info = ort.GetTensorTypeAndShape(input_tensor);
auto tensor_shape = ort.GetTensorShape(tensor_info);
ort.ReleaseTensorTypeAndShapeInfo(tensor_info);
auto xshape = tensor_shape.data();
auto xdim = tensor_shape.size();
TensorShape W(xshape, xdim);
const int group_mkl = static_cast<int>(group_);
mkldnn::memory::dims filter_dims_mkl;
if (group_mkl == 1) {
filter_dims_mkl.assign(W.GetDims().begin(), W.GetDims().end());
} else {
filter_dims_mkl.assign({group_mkl,
static_cast<int>(W[0] / group_mkl)});
filter_dims_mkl.insert(filter_dims_mkl.end(), W.GetDims().begin() + 1, W.GetDims().end());
}
std::vector<float> inv_scale_factor;
const auto& w_dims = W.GetDims();
const size_t O = w_dims[0];
inv_scale_factor.assign(static_cast<size_t>(O), 0.0f);
GamaInverseVariance(api, context, inv_scale_factor, O);
std::vector<float> weights_scaled_by_axis;
weights_scaled_by_axis.assign(static_cast<size_t>(O), 0.0f);
auto w_size = std::accumulate(w_dims.begin(), w_dims.end(), static_cast<int64_t>(1), std::multiplies<int64_t>{});
weights_scaled_by_axis.assign(static_cast<size_t>(w_size), 0.0f);
WeightsScaleByAxix(api, context, weights_scaled_by_axis, inv_scale_factor, W);
{
// lock to make sure reordering is done only once
std::lock_guard<OrtMutex> lock(provider_->GetMutex());
std::shared_ptr<mkldnn::memory> filter_dst_mem = provider_->GetWeightsMemoryBuffer(mklnode_ptr_->weight_name);
if (filter_dst_mem == nullptr) {
mkldnn::memory src = mkldnn::memory({{filter_dims_mkl}, MklDnnType<T>(), filter_format_}, cpu_engine, (void*)weights_scaled_by_axis.data());
IAllocatorUniquePtr<void> filter_reorder_buffer =
IAllocator::MakeUniquePtr<void>(alloc_, filter_size_);
filter_dst_mem.reset(
new mkldnn::memory(conv_fwd_pd_->weights_desc(), cpu_engine, filter_reorder_buffer.get()));
mkldnn::reorder(src, *filter_dst_mem)
.execute(cpu_engine, src, *filter_dst_mem);
provider_->SaveAllocatedMemory(std::move(filter_reorder_buffer));
provider_->SetWeightsMemoryBuffer(mklnode_ptr_->weight_name, filter_dst_mem);
}
std::shared_ptr<mkldnn::memory> bias_mem = provider_->GetBiasMemoryBuffer(mklnode_ptr_->weight_name);
if (bias_mem == nullptr) {
auto bias_size = conv_fwd_pd_.get()->bias_desc().get_size();
IAllocatorUniquePtr<void> bias_buffer =
IAllocator::MakeUniquePtr<void>(alloc_, bias_size);
bias_mem.reset(
new mkldnn::memory(conv_fwd_pd_->bias_desc(), cpu_engine, bias_buffer.get()));
float* bias_buffer_data = static_cast<float*>(bias_buffer.get());
if (mklnode_ptr_->num_inputs == 7) {
const OrtValue* conv_bias_tensor = ort.KernelContext_GetInput(context, input_index + 2);
const T* conv_bias_data = reinterpret_cast<const T*>(ort.GetTensorData<T>(conv_bias_tensor));
int batchNormIndix = input_index + 3;
const OrtValue* b_input_tensor = ort.KernelContext_GetInput(context, batchNormIndix + 1);
const T* bn_b_data = reinterpret_cast<const T*>(ort.GetTensorData<T>(b_input_tensor));
const OrtValue* mean_input_tensor = ort.KernelContext_GetInput(context, batchNormIndix + 2);
const T* bn_mean_data = reinterpret_cast<const T*>(ort.GetTensorData<T>(mean_input_tensor));
//norm_bias = (conv_bias_arr - mean_arr) * inv_std + bn_b_arr;
for (size_t j = 0; j < O; j++) {
bias_buffer_data[j] = (conv_bias_data[j] - bn_mean_data[j]) * inv_scale_factor[j] + bn_b_data[j];
}
} else {
// norm_bias = inv_std * (mean_arr - bn_b_arr);
int batchNormIndix = (mklnode_ptr_->num_inputs == 7) ? input_index + 3 : input_index + 2;
const OrtValue* b_input_tensor = ort.KernelContext_GetInput(context, batchNormIndix + 1);
const T* bn_b_data = reinterpret_cast<const T*>(ort.GetTensorData<T>(b_input_tensor));
const OrtValue* mean_input_tensor = ort.KernelContext_GetInput(context, batchNormIndix + 2);
const T* bn_mean_data = reinterpret_cast<const T*>(ort.GetTensorData<T>(mean_input_tensor));
for (size_t j = 0; j < O; j++) {
bias_buffer_data[j] = bn_b_data[j] - (bn_mean_data[j] * inv_scale_factor[j]);
}
}
provider_->SaveAllocatedBiasMemory(std::move(bias_buffer));
provider_->SetBiasMemoryBuffer(mklnode_ptr_->weight_name, bias_mem);
}
}
}
Status Bind(const OrtCustomOpApi* api, OrtKernelContext* context) override {
Ort::CustomOpApi ort{*api};
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
if (!primitive_created_.IsOK()) {
// abort as MKLDNN cannot execute this. but
// ORT try to delete output_tensor buffer data. allocate memory so that it can delete
// fix for test_averagepool_1d_default node test
//auto xshape = input_tensors[input_index].shape;
//auto xdim = input_tensors[input_index].ndim;
//AllocateOutputTensor(output_tensors, mklnode_ptr_->output_index, xshape, xdim, input_tensors[0].dtype);
return primitive_created_;
}
const OrtValue* winput_tensor = ort.KernelContext_GetInput(context, input_index + 1);
const T* filter_data = const_cast<T*>(ort.GetTensorData<T>(winput_tensor));
std::shared_ptr<mkldnn::memory> filter_dst_mem = provider_->GetWeightsMemoryBuffer(mklnode_ptr_->weight_name);
if (filter_dst_mem == nullptr) {
ReorderWeights(api, context, GetEngine());
filter_dst_mem = provider_->GetWeightsMemoryBuffer(mklnode_ptr_->weight_name);
}
filter_data = static_cast<T*>(filter_dst_mem->get_data_handle());
filter_mem_->set_data_handle(static_cast<void*>(const_cast<T*>(filter_data)));
std::shared_ptr<mkldnn::memory> bias_mem = provider_->GetBiasMemoryBuffer(mklnode_ptr_->weight_name);
const T* bias_data = static_cast<T*>(bias_mem->get_data_handle());
bias_mem_->set_data_handle(static_cast<void*>(const_cast<T*>(bias_data)));
if (primitive_src_desc_ != source_desc_) {
if (mklnode_ptr_->parent_nodes.empty()) {
const OrtValue* input_tensor = ort.KernelContext_GetInput(context, input_index);
const T* src_data = const_cast<T*>(ort.GetTensorData<T>(input_tensor));
src_mem_from_->set_data_handle(static_cast<void*>(const_cast<T*>(src_data)));
} else {
src_mem_from_ = parents_[0].get()->primitive_dst_mem_;
}
auto src_size = conv_fwd_pd_.get()->src_desc().get_size();
src_reorder_buffer_ = IAllocator::MakeUniquePtr<void>(alloc_, src_size);
src_mem_->set_data_handle(src_reorder_buffer_.get());
} else {
if (mklnode_ptr_->parent_nodes.empty()) {
const OrtValue* input_tensor = ort.KernelContext_GetInput(context, input_index);
const T* src_data = const_cast<T*>(ort.GetTensorData<T>(input_tensor));
src_mem_->set_data_handle(static_cast<void*>(const_cast<T*>(src_data)));
} else {
src_mem_ = parents_[0].get()->primitive_dst_mem_;
}
}
if (mklnode_ptr_->output_index >= 0) {
auto& y_dims = primitive_dst_shape_.GetDims();
// Allocate memory for output bufffer
OrtValue* output = ort.KernelContext_GetOutput(context, mklnode_ptr_->output_index, &y_dims[0], static_cast<int>(primitive_dst_shape_.GetDims().size()));
T* dst_data = ort.GetTensorMutableData<T>(output);
if (primitive_dst_desc_ != ort_source_desc_) {
reorder_dst_mem_to_->set_data_handle(dst_data);
} else {
primitive_dst_mem_->set_data_handle(dst_data);
}
}
return Status::OK();
}
private:
void ReadAttributes(const NodeAttributes& attributes,
const std::string attributes_prefix = "") override {
std::string auto_pad;
auto attr = attributes.find(attributes_prefix + "auto_pad");
if (attr != attributes.end() &&
attr->second.type() == ::ONNX_NAMESPACE::AttributeProto_AttributeType::AttributeProto_AttributeType_STRING) {
auto_pad = attr->second.s();
}
auto_pad_ = (auto_pad != "") ? StringToAutoPadType(auto_pad) : AutoPadType::NOTSET;
kernel_shape_specified_ = false;
attr = attributes.find(attributes_prefix + "kernel_shape");
if (attr != attributes.end()) {
ONNX_NAMESPACE::AttributeProto proto = attr->second;
Status status = GetIntsAttr(proto, kernel_shape_);
kernel_shape_specified_ = true;
}
attr = attributes.find(attributes_prefix + "strides");
if (attr != attributes.end()) {
ONNX_NAMESPACE::AttributeProto proto = attr->second;
Status status = GetIntsAttr(proto, strides_);
}
bool attr_read = false;
attr = attributes.find(attributes_prefix + "pads");
if (attr != attributes.end()) {
ONNX_NAMESPACE::AttributeProto proto = attr->second;
if (GetIntsAttr(proto, pads_) == Status::OK())
attr_read = true;
}
if (!attr_read) {
pads_.resize(kernel_shape_.size() * 2, 0);
}
attr_read = false;
attr = attributes.find(attributes_prefix + "dilations");
if (attr != attributes.end()) {
ONNX_NAMESPACE::AttributeProto proto = attr->second;
if (GetIntsAttr(proto, dilations_) == Status::OK())
attr_read = true;
}
if (!attr_read) {
dilations_.resize(kernel_shape_.size(), 1);
}
attr_read = false;
attr = attributes.find(attributes_prefix + "group");
if (attr != attributes.end()) {
ONNX_NAMESPACE::AttributeProto proto = attr->second;
if (GetIntAttr(proto, group_) == Status::OK())
attr_read = true;
}
if (!attr_read) {
group_ = 1;
}
attr = attributes.find(attributes_prefix + "epsilon");
if (attr != attributes.end()) {
epsilon_ = attr->second.f();
}
}
private:
mkldnn::memory::desc filter_desc_;
mkldnn::memory::format_tag filter_format_;
std::shared_ptr<mkldnn::memory> src_mem_from_;
std::unique_ptr<mkldnn::memory> src_mem_to_;
size_t src_size_;
size_t filter_size_;
size_t dst_size_;
std::shared_ptr<mkldnn::memory> src_mem_;
std::unique_ptr<mkldnn::memory> filter_mem_;
std::unique_ptr<mkldnn::memory> bias_mem_;
std::unique_ptr<mkldnn::convolution_forward::desc> fwd_desc_;
std::unique_ptr<mkldnn::memory::desc> src_md_;
std::unique_ptr<mkldnn::memory::desc> filter_md_;
std::unique_ptr<mkldnn::memory::desc> bias_md_;
std::unique_ptr<mkldnn::convolution_forward::primitive_desc> conv_fwd_pd_;
std::unique_ptr<mkldnn::primitive> conv_fwd_;
private:
IAllocatorUniquePtr<void> src_reorder_buffer_;
IAllocatorUniquePtr<void> dst_reorder_buffer_;
private:
Status ComputeKernelShape(const TensorShape& weight_shape, std::vector<int64_t>& kernel_shape) const {
if (kernel_shape_specified_) {
kernel_shape = kernel_shape_;
if (kernel_shape.size() + 2 != weight_shape.NumDimensions()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "kernel_shape num_dims is not compatible with W num_dims.",
" kernel_shape: ", TensorShape(kernel_shape).ToString().c_str(),
" W: ", weight_shape.ToString().c_str());
}
for (size_t i = 0; i < kernel_shape.size(); ++i) {
if (kernel_shape[i] != weight_shape[i + 2]) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "kernel_shape is not compatible with W shape.",
" kernel_shape: ", TensorShape(kernel_shape).ToString().c_str(),
" W: ", weight_shape.ToString().c_str());
}
}
} else {
auto& weight_dims = weight_shape.GetDims();
kernel_shape = std::vector<int64_t>(weight_dims.begin() + 2, weight_dims.end());
}
return Status::OK();
}
Status ValidateInputShape(const TensorShape& X, const TensorShape& W) const {
const int64_t C = X[1];
const int64_t M = W[0];
if (X.NumDimensions() != W.NumDimensions()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "X num_dims does not match W num_dims.",
" X: ", X.ToString().c_str(),
" W: ", W.ToString().c_str());
}
if (C != W[1] * group_) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Input channels C is not equal to kernel channels * group.",
" C: ", C,
" kernel channels: ", W[1],
" group: ", group_);
}
if (M % group_ != 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Output channels M is not divisible by group.",
" M: ", M,
" group: ", group_);
}
return Status::OK();
}
template <bool ForceSymmetricAutoPadding = false>
Status InferOutputShape(const TensorShape& input_shape,
const std::vector<int64_t>& kernel_shape,
const std::vector<int64_t>& strides,
const std::vector<int64_t>& dilations,
std::vector<int64_t>* pads,
std::vector<int64_t>* output_shape) const {
size_t rank = gsl::narrow_cast<int>(input_shape.NumDimensions());
for (size_t dim = 0; dim < rank; ++dim) {
if (dim >= strides.size() || dim >= kernel_shape.size() ||
dim >= dilations.size() || dim >= pads->size() ||
rank + dim >= pads->size()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Out of bound access to array");
}
int64_t dim_size = 0;
ORT_RETURN_IF_ERROR(ComputePadAndOutputShape<ForceSymmetricAutoPadding>(
input_shape[dim],
strides[dim],
kernel_shape[dim],
dilations[dim],
auto_pad_,
&pads->at(dim),
&pads->at(input_shape.NumDimensions() + dim),
&dim_size));
if (dim_size <= 0) {
return Status(common::ONNXRUNTIME, common::INVALID_ARGUMENT, "Invalid input shape: " + input_shape.ToString());
}
output_shape->push_back(dim_size);
}
return Status::OK();
}
private:
std::unique_ptr<mkldnn::stream> stream_;
std::vector<int64_t> kernel_shape_; // must use ComputeKernelShape(...), instead of kernel_shape_
AutoPadType auto_pad_;
int64_t group_;
bool kernel_shape_specified_;
std::vector<int64_t> strides_;
std::vector<int64_t> pads_;
std::vector<int64_t> dilations_;
std::string activation_;
float alpha_;
float epsilon_ = 1e-5f; // attribute of fused batchnorm.
};
} // namespace mkl_dnn
} // namespace onnxruntime

View file

@ -10,6 +10,7 @@
#include "core/providers/mkldnn/mkldnn_common.h"
#include "core/providers/mkldnn/subgraph/mkldnn_conv.h"
#include "core/providers/mkldnn/subgraph/mkldnn_batchnorm.h"
#include "core/providers/mkldnn/subgraph/mkldnn_conv_batchnorm.h"
#include "core/providers/mkldnn/subgraph/mkldnn_activations.h"
#include "core/providers/mkldnn/subgraph/mkldnn_pool.h"
#include "core/providers/mkldnn/subgraph/mkldnn_sum.h"
@ -27,7 +28,7 @@ class SubgraphPrimitive : public PrimitiveBase {
OrtKernelContext* context,
const SubgraphParams& params)
: cpu_engine_(GetEngine()) {
context_.stream.reset(new mkldnn::stream(mkldnn::stream::kind::eager));
context_.stream.reset(new mkldnn::stream(cpu_engine_));
if (context_.net.size() == 0) {
CreateKernels(params);
@ -50,8 +51,10 @@ class SubgraphPrimitive : public PrimitiveBase {
if (!status.IsOK())
break;
}
if (status.IsOK())
context_.stream->submit(context_.net);
if (status.IsOK()) {
for (size_t i = 0; i < context_.net.size(); ++i)
context_.net.at(i).execute(*context_.stream, context_.net_args.at(i));
}
return status;
}
@ -107,6 +110,25 @@ class SubgraphPrimitive : public PrimitiveBase {
kernel->parents_.push_back(context_.kernels[index]);
}
context_.kernels.push_back(kernel);
} else if (mkldnn_node.name == "Conv-BatchNormalization") {
std::ostringstream os;
os << "Conv-" << mkldnn_node.node_index << "-";
std::shared_ptr<MklDnnConvBatchNorm<T>> kernel;
kernel.reset(new MklDnnConvBatchNorm<T>(mkldnn_node, params.provider, params.attributes, os.str()));
for (auto index : mkldnn_node.parent_nodes) {
kernel->parents_.push_back(context_.kernels[index]);
}
context_.kernels.push_back(kernel);
} else if (mkldnn_node.name == "Conv-BatchNormalization-Relu") {
std::ostringstream os;
os << "Conv-" << mkldnn_node.node_index << "-";
std::shared_ptr<MklDnnConvBatchNorm<T>> kernel;
kernel.reset(new MklDnnConvBatchNorm<T>(mkldnn_node, params.provider, params.attributes, os.str()));
kernel->fuse_relu_ = true;
for (auto index : mkldnn_node.parent_nodes) {
kernel->parents_.push_back(context_.kernels[index]);
}
context_.kernels.push_back(kernel);
} else if (mkldnn_node.name == "MaxPool") {
std::ostringstream os;
os << "MaxPool-" << mkldnn_node.node_index << "-";
@ -143,15 +165,6 @@ class SubgraphPrimitive : public PrimitiveBase {
kernel->parents_.push_back(context_.kernels[index]);
}
context_.kernels.push_back(kernel);
} else if (mkldnn_node.name == "Sum") {
std::ostringstream os;
os << "Sum-" << mkldnn_node.node_index << "-";
std::shared_ptr<MklDnnSum<T>> kernel;
kernel.reset(new MklDnnSum<T>(mkldnn_node, params.provider, params.attributes, os.str()));
for (auto index : mkldnn_node.parent_nodes) {
kernel->parents_.push_back(context_.kernels[index]);
}
context_.kernels.push_back(kernel);
} else if (mkldnn_node.name == "LRN") {
std::ostringstream os;
os << "LRN-" << mkldnn_node.node_index << "-";
@ -161,14 +174,24 @@ class SubgraphPrimitive : public PrimitiveBase {
kernel->parents_.push_back(context_.kernels[index]);
}
context_.kernels.push_back(kernel);
} else if (mkldnn_node.name == "Sum") {
std::ostringstream os;
os << "Sum-" << mkldnn_node.node_index << "-";
std::shared_ptr<MklDnnSum<T>> kernel;
kernel.reset(new MklDnnSum<T>(mkldnn_node, params.provider, params.attributes, os.str()));
for (auto index : mkldnn_node.parent_nodes) {
kernel->parents_.push_back(context_.kernels[index]);
}
context_.kernels.push_back(kernel);
}
}
}
private:
struct SubgraphContext {
std::unique_ptr<mkldnn::stream> stream;
std::vector<mkldnn::primitive> net;
std::vector<std::unordered_map<int, mkldnn::memory>> net_args;
std::vector<std::shared_ptr<MklDnnKernel>> kernels;
SubgraphContext() : stream(nullptr) {}
@ -177,9 +200,8 @@ class SubgraphPrimitive : public PrimitiveBase {
Status Initialize(const OrtCustomOpApi* api, OrtKernelContext* context) {
// Propagate mkldnn block format
// dst format of current node to src format of next node
mkldnn::memory::format source_format = mkldnn::memory::format::any; // ONNXRuntime format
for (auto& kernel : context_.kernels) {
Status status = kernel->CreatePrimitives(api, context, cpu_engine_, context_.net, source_format);
Status status = kernel->CreatePrimitives(api, context, cpu_engine_, context_.net, context_.net_args);
if (status.IsOK())
kernel->ReorderWeights(api, context, cpu_engine_);
else
@ -246,7 +268,7 @@ Status MkldnnFuncKernel<T>::Compute(const OrtCustomOpApi* api, OrtKernelContext*
status = primitive->Compute(api, context);
} catch (const mkldnn::error& e) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Status: ", e.status,
", message: ", e.message.c_str());
", message: ", e.what());
}
return status;
}

View file

@ -6,6 +6,7 @@
#include "core/providers/mkldnn/mkldnn_execution_provider.h"
#include "core/session/onnxruntime_c_api.h"
#include "core/framework/func_api.h"
#include "mkldnn_kernel.h"
namespace onnxruntime {
namespace mkl_dnn {
@ -37,8 +38,8 @@ class MkldnnFuncKernel {
if (sub_it->second.type() == ONNX_NAMESPACE::AttributeProto_AttributeType::AttributeProto_AttributeType_STRING) {
params_.subgraph_id = sub_it->second.s();
params_.subgraph = provider->GetMklDnnSubgraph(params_.subgraph_id);
std::ostringstream key_os;
std::ostringstream key_os;
key_os << params_.subgraph->graph_name << "_" << params_.subgraph_id << "-";
key_os << params_.subgraph->mkldnn_nodes.back().ToString() << "-";
key_os << params_.subgraph->mkldnn_nodes.back().output_name;
@ -146,7 +147,7 @@ class MkldnnFuncKernel {
if (attr != attributes.end()) {
key.append(1, '#');
ONNX_NAMESPACE::AttributeProto proto = attr->second;
key.append(proto.s());
key.append(proto.s());
key.append(1, '#');
}
@ -183,19 +184,19 @@ class MkldnnFuncKernel {
key.append(1, '#');
}
attr = attributes.find(attributes_prefix + "group");
attr = attributes.find(attributes_prefix + "group");
if (attr != attributes.end()) {
key.append(1, '#');
ONNX_NAMESPACE::AttributeProto proto = attr->second;
key.append(std::to_string(proto.i()));
key.append(std::to_string(proto.i()));
key.append(1, '#');
}
return key;
}
std::string GetLrnAttributeKey(const NodeAttributes& attributes,
const std::string attributes_prefix = "") {
const std::string attributes_prefix = "") {
std::string key;
auto attr = attributes.find(attributes_prefix + "alpha");
@ -214,7 +215,7 @@ class MkldnnFuncKernel {
key.append(1, '#');
}
attr = attributes.find(attributes_prefix + "bias");
attr = attributes.find(attributes_prefix + "bias");
if (attr != attributes.end()) {
key.append(1, '#');
ONNX_NAMESPACE::AttributeProto proto = attr->second;

View file

@ -8,44 +8,48 @@ namespace mkl_dnn {
void MklDnnKernel::InitDstReorderOutput(mkldnn::engine& cpu_engine,
mkldnn::memory::data_type& data_type,
std::vector<mkldnn::primitive>& net) {
std::vector<mkldnn::primitive>& net,
std::vector<std::unordered_map<int, mkldnn::memory>>& net_args) {
// Allocate dst buffer if reorder is necessary
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_)
{
// reorder to ONNXRuntime format
mkldnn::memory::dims dst_dims_mkl(
primitive_dst_shape_.GetDims().begin(), primitive_dst_shape_.GetDims().end());
mkldnn::memory::desc dst_des = mkldnn::memory::desc(dst_dims_mkl,
data_type, ort_source_format_);
reorder_dst_mem_to_.reset(new mkldnn::memory({dst_des, cpu_engine}, nullptr));
reorder_dst_mem_to_.reset(new mkldnn::memory(dst_des, cpu_engine));
net.push_back(mkldnn::reorder(*primitive_dst_mem_, *reorder_dst_mem_to_));
net_args.push_back({{MKLDNN_ARG_FROM, *primitive_dst_mem_},
{MKLDNN_ARG_TO, *reorder_dst_mem_to_}});
}
}
mkldnn::memory::format MklDnnKernel::GetSourceFormat(int dim_size) {
mkldnn::memory::format source_format = mkldnn::memory::format::any;
mkldnn::memory::format_tag MklDnnKernel::GetSourceFormat(int dim_size) {
mkldnn::memory::format_tag source_format = mkldnn::memory::format_tag::any;
switch (dim_size) {
case 1: {
source_format = mkldnn::memory::format::x;
source_format = mkldnn::memory::format_tag::x;
break;
}
case 2: {
source_format = mkldnn::memory::format::nc;
source_format = mkldnn::memory::format_tag::nc;
break;
}
case 3: {
source_format = mkldnn::memory::format::ntc;
source_format = mkldnn::memory::format_tag::ntc;
break;
}
case 4: {
source_format = mkldnn::memory::format::nchw;
source_format = mkldnn::memory::format_tag::nchw;
break;
}
case 5: {
source_format = mkldnn::memory::format::ncdhw;
source_format = mkldnn::memory::format_tag::ncdhw;
break;
}
default: {
source_format = mkldnn::memory::format::any;
source_format = mkldnn::memory::format_tag::any;
break;
}
}

View file

@ -19,6 +19,7 @@ class MklDnnKernel {
public:
MklDnnKernel(const MklDnnNode& node,
MKLDNNExecutionProvider* provider) {
name_ = node.name;
mklnode_ptr_ = std::make_shared<MklDnnNode>(node);
provider_ = provider;
alloc_ = provider_->GetAllocator(0, OrtMemTypeDefault);
@ -29,7 +30,7 @@ class MklDnnKernel {
OrtKernelContext* context,
mkldnn::engine& cpu_engine,
std::vector<mkldnn::primitive>& net,
mkldnn::memory::format& src_fmt) = 0;
std::vector<std::unordered_map<int, mkldnn::memory>> &net_args) = 0;
virtual void ReorderWeights(const OrtCustomOpApi* api, OrtKernelContext* context, mkldnn::engine& cpu_engine) {
ORT_UNUSED_PARAMETER(api);
@ -80,33 +81,35 @@ class MklDnnKernel {
void InitDstReorderOutput(mkldnn::engine& cpu_engine,
mkldnn::memory::data_type& data_type,
std::vector<mkldnn::primitive>& net);
std::vector<mkldnn::primitive>& net,
std::vector<std::unordered_map<int, mkldnn::memory>>& net_args);
//void AllocateMemoryAndReorderIfNeeded(const OrtCustomOpApi* api);
//void AllocateOutputTensor(const OrtCustomOpApi* api, int index, const int64_t* shape, size_t dim);
mkldnn::memory::format GetSourceFormat(int dim_size);
mkldnn::memory::format_tag GetSourceFormat(int dim_size);
public:
std::string name_;
std::vector<std::shared_ptr<MklDnnKernel>> parents_;
bool fuse_relu_ = false;
bool fuse_sum_ = false;
std::shared_ptr<mkldnn::memory> primitive_dst_mem_;
std::unique_ptr<mkldnn::memory::desc> primitive_dst_md_;
TensorShape primitive_dst_shape_;
mkldnn::memory::format primitive_dst_format_ = mkldnn::memory::format::any;
mkldnn::memory::desc primitive_dst_desc_;
// ONNX Runtime format
mkldnn::memory::format_tag ort_source_format_;
mkldnn::memory::desc ort_source_desc_;
// input format.
// It can be ORT format (nchw) or blocked memory format from parent node
// mkldnn::memory::format_tag source_format_ = mkldnn::memory::format_tag::any;
mkldnn::memory::desc source_desc_ = mkldnn::memory::desc();
protected:
// ONNX Runtime format
mkldnn::memory::format ort_source_format_ = mkldnn::memory::format::any;
// input format.
// It can be ORT format (nchw) or blocked memory format from parent nce
mkldnn::memory::format src_format_ = mkldnn::memory::format::any;
// Pointer to MklNode of subgraph IR
std::shared_ptr<MklDnnNode> mklnode_ptr_;
// input format expected by primitive object
mkldnn::memory::format primitive_src_format_ = mkldnn::memory::format::any;
mkldnn::memory::desc primitive_src_desc_;
// memory used for reorders
std::unique_ptr<mkldnn::memory> reorder_dst_mem_to_;

View file

@ -26,7 +26,7 @@ class MklDnnLrn : public MklDnnKernel {
OrtKernelContext* context,
mkldnn::engine& cpu_engine,
std::vector<mkldnn::primitive>& net,
mkldnn::memory::format& source_format) {
std::vector<std::unordered_map<int, mkldnn::memory>>& net_args) override {
Ort::CustomOpApi ort{*api};
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
@ -40,22 +40,25 @@ class MklDnnLrn : public MklDnnKernel {
auto xdim = tensor_shape.size();
ort_source_format_ = GetSourceFormat(static_cast<int>(xdim));
source_format = ort_source_format_;
x_shape = TensorShape(xshape, xdim);
mkldnn::memory::dims src_dims_mkl(
mkldnn::memory::dims src_dims(
x_shape.GetDims().begin(), x_shape.GetDims().end());
ort_source_desc_ = mkldnn::memory::desc(
{src_dims}, MklDnnType<T>(), ort_source_format_);
src_md_.reset(new mkldnn::memory::desc(
{src_dims_mkl}, MklDnnType<T>(), source_format));
{src_dims}, MklDnnType<T>(), ort_source_format_));
src_mem_.reset(
new mkldnn::memory({*src_md_, cpu_engine}, nullptr));
new mkldnn::memory(*src_md_, cpu_engine, nullptr));
} else {
src_md_.reset(
new mkldnn::memory::desc(parents_[0].get()->primitive_dst_mem_.get()->get_primitive_desc().desc()));
new mkldnn::memory::desc(parents_[0].get()->primitive_dst_desc_));
src_mem_ = parents_[0].get()->primitive_dst_mem_;
x_shape = parents_[0].get()->primitive_dst_shape_;
ort_source_format_ = source_format;
ort_source_format_ = parents_[0].get()->ort_source_format_;
ort_source_desc_ = parents_[0].get()->ort_source_desc_;
source_desc_ = parents_[0].get()->primitive_dst_desc_;
}
primitive_dst_shape_ = TensorShape(x_shape);
@ -68,39 +71,39 @@ class MklDnnLrn : public MklDnnKernel {
fwd_primitive_desc_.reset(new mkldnn::lrn_forward::primitive_desc(
*fwd_desc_, cpu_engine));
primitive_src_format_ = static_cast<mkldnn::memory::format>(
fwd_primitive_desc_.get()->src_primitive_desc().desc().data.format);
primitive_dst_format_ = static_cast<mkldnn::memory::format>(
fwd_primitive_desc_.get()->dst_primitive_desc().desc().data.format);
primitive_src_desc_ = fwd_primitive_desc_.get()->src_desc();
primitive_dst_desc_ = fwd_primitive_desc_.get()->dst_desc();
if (mklnode_ptr_->output_index >= 0) {
// last node of sub-graph. need to allocate memory for output_tensor
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_) {
// reorder neded. Use primitive output as input to reorder and
// allocate buffer for reorder output, final output of this subgraph
primitive_dst_mem_.reset(
new mkldnn::memory(fwd_primitive_desc_.get()->dst_primitive_desc()));
new mkldnn::memory(fwd_primitive_desc_.get()->dst_desc(), cpu_engine));
} else {
// Last node but re-order not needed. Allocate buffer to output of this node
primitive_dst_mem_.reset(
new mkldnn::memory(fwd_primitive_desc_.get()->dst_primitive_desc(), nullptr));
new mkldnn::memory(fwd_primitive_desc_.get()->dst_desc(), cpu_engine, nullptr));
}
} else {
// Intermediate node. Use mkldnn kernel internal memory for output and
// use this as input to next node.
primitive_dst_mem_.reset(
new mkldnn::memory(fwd_primitive_desc_.get()->dst_primitive_desc()));
new mkldnn::memory(fwd_primitive_desc_.get()->dst_desc(), cpu_engine));
}
lrn_fwd_.reset(
new mkldnn::lrn_forward(*fwd_primitive_desc_, *src_mem_, *primitive_dst_mem_));
new mkldnn::lrn_forward(*fwd_primitive_desc_));
net.push_back(*lrn_fwd_);
net_args.push_back({{MKLDNN_ARG_SRC, *src_mem_},
{MKLDNN_ARG_DST, *primitive_dst_mem_}});
if (mklnode_ptr_->output_index >= 0) {
// one of the end nodes. Allocate output buffer memory and
// reorder is necessary
mkldnn::memory::data_type t = MklDnnType<T>();
InitDstReorderOutput(cpu_engine, t, net);
InitDstReorderOutput(cpu_engine, t, net, net_args);
}
return Status::OK();
@ -123,7 +126,7 @@ class MklDnnLrn : public MklDnnKernel {
OrtValue* output = ort.KernelContext_GetOutput(context, mklnode_ptr_->output_index, &y_dims[0], static_cast<int>(primitive_dst_shape_.GetDims().size()));
T* dst_data = ort.GetTensorMutableData<T>(output);
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_!= ort_source_desc_) {
reorder_dst_mem_to_->set_data_handle(dst_data);
} else {
primitive_dst_mem_->set_data_handle(dst_data);

View file

@ -24,7 +24,7 @@ class MklDnnPool : public MklDnnKernel {
Status CreatePrimitives(const OrtCustomOpApi* api,
OrtKernelContext* context,
mkldnn::engine& cpu_engine, std::vector<mkldnn::primitive>& net,
mkldnn::memory::format& source_format) override {
std::vector<std::unordered_map<int, mkldnn::memory>>& net_args) override {
Ort::CustomOpApi ort{*api};
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
@ -37,33 +37,36 @@ class MklDnnPool : public MklDnnKernel {
auto xdim = tensor_shape.size();
mkldnn::memory::dims dims(xdim);
ort_source_format_ = GetSourceFormat(static_cast<int>(xdim));
source_format = ort_source_format_;
src_format_ = ort_source_format_;
x_shape_ = TensorShape(xshape, xdim);
mkldnn::memory::dims src_dims_mkl(x_shape_.GetDims().begin(), x_shape_.GetDims().end());
ort_source_format_ = GetSourceFormat(static_cast<int>(xdim));
// ort_source_desc is the format of ONNX Runtime tensor format
ort_source_desc_ = mkldnn::memory::desc({src_dims_mkl}, MklDnnType<T>(), ort_source_format_);
// source_desc is propagating format. input to this op.
source_desc_ = mkldnn::memory::desc({src_dims_mkl}, MklDnnType<T>(), ort_source_format_);
// reorder for better performance
mkldnn::memory::format fmt = GetAVXFormat(src_dims_mkl);
src_md_.reset(new mkldnn::memory::desc(
{src_dims_mkl}, MklDnnType<T>(), fmt));
mkldnn::memory::format_tag src_format = GetAVXFormat(src_dims_mkl);
src_md_.reset(new mkldnn::memory::desc({src_dims_mkl}, MklDnnType<T>(), src_format));
} else {
// get the output of previous node (mkldnn block propagation).
// TODO Sourcenode will set src of this node.
x_shape_ = parents_[0].get()->primitive_dst_shape_;
ort_source_format_ = source_format;
src_format_ = parents_[0].get()->primitive_dst_format_;
source_desc_ = parents_[0].get()->primitive_dst_desc_;
mkldnn::memory::dims src_dims_mkl(x_shape_.GetDims().begin(), x_shape_.GetDims().end());
if (src_format_ == ort_source_format_) {
ort_source_format_ = parents_[0].get()->ort_source_format_;
ort_source_desc_ = parents_[0].get()->ort_source_desc_;
source_desc_ = parents_[0].get()->primitive_dst_desc_;
if (source_desc_ == ort_source_desc_) {
// reorder for better performance
mkldnn::memory::format fmt = GetAVXFormat(src_dims_mkl);
mkldnn::memory::format_tag fmt = GetAVXFormat(src_dims_mkl);
src_md_.reset(new mkldnn::memory::desc(
{src_dims_mkl}, MklDnnType<T>(), fmt));
} else {
src_md_.reset(new mkldnn::memory::desc(
parents_[0].get()->primitive_dst_mem_.get()->get_primitive_desc().desc()));
src_md_.reset(new mkldnn::memory::desc(parents_[0].get()->primitive_dst_mem_->get_desc()));
}
}
@ -93,7 +96,7 @@ class MklDnnPool : public MklDnnKernel {
mkldnn::memory::dims padding_right_mkl(pads_.begin() + (pads_.size() / 2), pads_.end());
primitive_dst_md_.reset(new mkldnn::memory::desc(
{dst_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::any));
{dst_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
mkldnn::algorithm algo = mkldnn::algorithm::pooling_max;
if (op_name_ == "AveragePool" || op_name_ == "GlobalAveragePool") {
@ -106,8 +109,7 @@ class MklDnnPool : public MklDnnKernel {
mkldnn::prop_kind::forward_inference, algo,
*src_md_, *primitive_dst_md_,
strides_mkl, kernel_mkl,
padding_left_mkl, padding_right_mkl,
mkldnn::padding_kind::zero));
padding_left_mkl, padding_right_mkl));
fwd_primitive_desc_.reset(new mkldnn::pooling_forward::primitive_desc(
*fwd_desc_, cpu_engine));
@ -115,37 +117,35 @@ class MklDnnPool : public MklDnnKernel {
if (mklnode_ptr_->parent_nodes.empty()) {
// Sub-graph's first node. Read input from input buffer
src_mem_.reset(new mkldnn::memory(
fwd_primitive_desc_.get()->src_primitive_desc(), nullptr));
fwd_primitive_desc_.get()->src_desc(), cpu_engine, nullptr));
} else {
// Sub-graph's inner node. set input to parent's output
src_mem_ = parents_[0].get()->primitive_dst_mem_;
}
primitive_src_format_ = static_cast<mkldnn::memory::format>(
fwd_primitive_desc_.get()->src_primitive_desc().desc().data.format);
primitive_src_desc_ = fwd_primitive_desc_.get()->src_desc();
primitive_dst_desc_ = fwd_primitive_desc_.get()->dst_desc();
primitive_dst_format_ = static_cast<mkldnn::memory::format>(
fwd_primitive_desc_.get()->dst_primitive_desc().desc().data.format);
src_size_ = fwd_primitive_desc_.get()->src_desc().get_size();
dst_size_ = fwd_primitive_desc_.get()->dst_desc().get_size();
src_size_ = fwd_primitive_desc_.get()->src_primitive_desc().get_size();
dst_size_ = fwd_primitive_desc_.get()->dst_primitive_desc().get_size();
// reorder source memory for best performance (AVX512);
if (primitive_src_format_ != src_format_) {
mkldnn::memory::dims src_dims_mkl(x_shape_.GetDims().begin(), x_shape_.GetDims().end());
auto src_md = mkldnn::memory::desc(src_dims_mkl, MklDnnType<T>(), src_format_);
auto pd = mkldnn::memory::primitive_desc(src_md, cpu_engine);
// reorder source memory for best performance (AVX512);
if (primitive_src_desc_ != source_desc_) {
mkldnn::memory::dims src_dims(x_shape_.GetDims().begin(), x_shape_.GetDims().end());
auto pd = mkldnn::memory::desc(source_desc_);
if (mklnode_ptr_->parent_nodes.empty())
src_mem_from_.reset(new mkldnn::memory(pd, nullptr));
src_mem_from_.reset(new mkldnn::memory(pd, cpu_engine, nullptr));
else
src_mem_from_ = parents_[0].get()->primitive_dst_mem_;
src_mem_.reset(new mkldnn::memory(fwd_primitive_desc_->src_primitive_desc(), nullptr));
src_mem_.reset(new mkldnn::memory(fwd_primitive_desc_->src_desc(), cpu_engine, nullptr));
net.push_back(mkldnn::reorder(*src_mem_from_, *src_mem_));
net_args.push_back({{MKLDNN_ARG_FROM, *src_mem_from_},
{MKLDNN_ARG_TO, *src_mem_}});
} else {
if (mklnode_ptr_->parent_nodes.empty()) {
src_mem_.reset(new mkldnn::memory(fwd_primitive_desc_->src_primitive_desc(), nullptr));
src_mem_.reset(new mkldnn::memory(fwd_primitive_desc_->src_desc(), cpu_engine, nullptr));
} else {
src_mem_ = parents_[0].get()->primitive_dst_mem_;
}
@ -153,32 +153,33 @@ class MklDnnPool : public MklDnnKernel {
if (mklnode_ptr_->output_index >= 0) {
// last node of sub-graph. need to allocate memory for output_tensor
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_) {
// reorder neded. Use primitive output as input to reorder and
// allocate buffer for reorder output, final output of this subgraph
primitive_dst_mem_.reset(
new mkldnn::memory(fwd_primitive_desc_.get()->dst_primitive_desc()));
new mkldnn::memory(fwd_primitive_desc_.get()->dst_desc(), cpu_engine));
} else {
// Last node but re-order not needed. Allocate buffer to output of this node
primitive_dst_mem_.reset(
new mkldnn::memory(fwd_primitive_desc_.get()->dst_primitive_desc(), nullptr));
new mkldnn::memory(fwd_primitive_desc_.get()->dst_desc(), cpu_engine, nullptr));
}
} else {
// Intermediate node. Use mkldnn kernel internal memory for output and
// use this as input to next node.
primitive_dst_mem_.reset(
new mkldnn::memory(fwd_primitive_desc_.get()->dst_primitive_desc()));
new mkldnn::memory(fwd_primitive_desc_.get()->dst_desc(), cpu_engine));
}
pool_fwd_.reset(
new mkldnn::pooling_forward(*fwd_primitive_desc_, *src_mem_, *primitive_dst_mem_));
new mkldnn::pooling_forward(*fwd_primitive_desc_));
net.push_back(*pool_fwd_);
net_args.push_back({{MKLDNN_ARG_SRC, *src_mem_},
{MKLDNN_ARG_DST, *primitive_dst_mem_}});
if (mklnode_ptr_->output_index >= 0) {
// one of the end nodes. Allocate output buffer memory and
// reorder is necessary
mkldnn::memory::data_type t = MklDnnType<T>();
InitDstReorderOutput(cpu_engine, t, net);
InitDstReorderOutput(cpu_engine, t, net, net_args);
}
return Status::OK();
}
@ -200,7 +201,7 @@ class MklDnnPool : public MklDnnKernel {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Please call default CPU kernel.");
}
if (primitive_src_format_ != src_format_) {
if (fwd_primitive_desc_.get()->src_desc() != source_desc_) {
if (mklnode_ptr_->parent_nodes.empty()) {
const OrtValue* input_tensor = ort.KernelContext_GetInput(context, input_index);
const T* src_data = const_cast<T*>(ort.GetTensorData<T>(input_tensor));
@ -209,7 +210,7 @@ class MklDnnPool : public MklDnnKernel {
src_mem_from_ = parents_[0].get()->primitive_dst_mem_;
}
auto src_size = fwd_primitive_desc_.get()->src_primitive_desc().get_size();
auto src_size = fwd_primitive_desc_.get()->src_desc().get_size();
src_reorder_buffer_ = IAllocator::MakeUniquePtr<void>(alloc_, src_size);
src_mem_->set_data_handle(src_reorder_buffer_.get());
} else {
@ -230,7 +231,7 @@ class MklDnnPool : public MklDnnKernel {
OrtValue* output = ort.KernelContext_GetOutput(context, mklnode_ptr_->output_index, &y_dims[0], static_cast<int>(primitive_dst_shape_.GetDims().size()));
T* dst_data = ort.GetTensorMutableData<T>(output);
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_) {
reorder_dst_mem_to_->set_data_handle(dst_data);
} else {
primitive_dst_mem_->set_data_handle(dst_data);
@ -322,15 +323,15 @@ class MklDnnPool : public MklDnnKernel {
std::unique_ptr<mkldnn::memory> dst_mem_to_;
private:
mkldnn::memory::format GetAVXFormat(const mkldnn::memory::dims& src_dims_mkl) {
mkldnn::memory::format_tag GetAVXFormat(const mkldnn::memory::dims& src_dims_mkl) {
bool is_2D = src_dims_mkl.size() == 4 ? true : false;
mkldnn::memory::format fmt = mkldnn::memory::format::any;
mkldnn::memory::format_tag fmt = mkldnn::memory::format_tag::any;
if (CPUIDInfo::GetCPUIDInfo().HasAVX512f()) {
fmt = is_2D ? mkldnn::memory::format::nChw16c : mkldnn::memory::format::nCdhw16c;
fmt = is_2D ? mkldnn::memory::format_tag::nChw16c : mkldnn::memory::format_tag::nCdhw16c;
} else if (CPUIDInfo::GetCPUIDInfo().HasAVX2() && (src_dims_mkl[1] % 8 == 0)) {
fmt = is_2D ? mkldnn::memory::format::nChw8c : mkldnn::memory::format::ncdhw;
fmt = is_2D ? mkldnn::memory::format_tag::nChw8c : mkldnn::memory::format_tag::ncdhw;
} else {
fmt = is_2D ? mkldnn::memory::format::nchw : mkldnn::memory::format::ncdhw;
fmt = is_2D ? mkldnn::memory::format_tag::nchw : mkldnn::memory::format_tag::ncdhw;
}
return fmt;
}

View file

@ -25,7 +25,7 @@ class MklDnnSum : public MklDnnKernel {
OrtKernelContext* context,
mkldnn::engine& cpu_engine,
std::vector<mkldnn::primitive>& net,
mkldnn::memory::format& source_format) override {
std::vector<std::unordered_map<int, mkldnn::memory>>& net_args) override {
Ort::CustomOpApi ort{*api};
int num_inputs = mklnode_ptr_->num_inputs;
int input_index = mklnode_ptr_->input_start_index < 0 ? 0 : mklnode_ptr_->input_start_index;
@ -41,12 +41,18 @@ class MklDnnSum : public MklDnnKernel {
auto xdim = tensor_shape.size();
ort_source_format_ = GetSourceFormat(static_cast<int>(xdim));
source_format = ort_source_format_;
src_format_ = ort_source_format_;
x_shape = TensorShape(xshape, xdim);
mkldnn::memory::dims src_dims(
x_shape.GetDims().begin(), x_shape.GetDims().end());
ort_source_desc_ = mkldnn::memory::desc(
{src_dims}, MklDnnType<T>(), ort_source_format_);
source_desc_ = ort_source_desc_;
} else {
x_shape = parents_[0].get()->primitive_dst_shape_;
src_format_ = parents_[0].get()->primitive_dst_format_;
ort_source_format_ = parents_[0].get()->ort_source_format_;
ort_source_desc_ = parents_[0].get()->ort_source_desc_;
source_desc_ = parents_[0].get()->primitive_dst_desc_;
}
primitive_dst_shape_ = TensorShape(x_shape);
@ -65,66 +71,60 @@ class MklDnnSum : public MklDnnKernel {
auto xdim = tensor_shape.size();
mkldnn::memory::dims dims(xdim);
ort_source_format_ = GetSourceFormat(static_cast<int>(xdim));
x_shape1 = TensorShape(xshape, xdim);
mkldnn::memory::dims src_dims_mkl(
mkldnn::memory::dims src_dims(
x_shape1.GetDims().begin(), x_shape1.GetDims().end());
src_md_.reset(new mkldnn::memory::desc(
{src_dims_mkl}, MklDnnType<T>(), src_format_));
auto mpd = mkldnn::memory::primitive_desc(*src_md_, cpu_engine);
auto src_memory = mkldnn::memory(mpd, nullptr);
auto mpd = mkldnn::memory::desc({src_dims}, MklDnnType<T>(), ort_source_format_);
auto src_memory = mkldnn::memory(mpd, cpu_engine, nullptr);
srcs_pd_.push_back(mpd);
srcs_memory_.push_back(src_memory);
coeff.push_back(1.0);
} else {
src_md_.reset(
new mkldnn::memory::desc(parents_[i].get()->primitive_dst_mem_.get()->get_primitive_desc().desc()));
auto mpd = mkldnn::memory::primitive_desc(*src_md_, cpu_engine);
auto src_memory = *parents_[i].get()->primitive_dst_mem_; //mkldnn::memory(mpd);
x_shape = parents_[0].get()->primitive_dst_shape_;
auto mpd = mkldnn::memory::desc(parents_[i].get()->primitive_dst_desc_);
auto src_memory = *parents_[i].get()->primitive_dst_mem_;
srcs_pd_.push_back(mpd);
srcs_memory_.push_back(src_memory);
coeff.push_back(1.0);
ort_source_format_ = source_format;
}
}
primitive_dst_md_.reset(new mkldnn::memory::desc(
{dst_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format::any));
primitive_dst_md_.reset(new mkldnn::memory::desc(
{dst_dims_mkl}, MklDnnType<T>(), mkldnn::memory::format_tag::any));
sum_pd_.reset(new mkldnn::sum::primitive_desc(
*primitive_dst_md_, coeff, srcs_pd_));
primitive_dst_format_ = static_cast<mkldnn::memory::format>(sum_pd_->dst_primitive_desc().desc().data.format);
*primitive_dst_md_, coeff, srcs_pd_, cpu_engine));
if (mklnode_ptr_->output_index >= 0) {
// last node of sub-graph. need to allocate memory for output_tensor
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_) {
// reorder neded. Use primitive output as input to reorder and
// allocate buffer for reorder output, final output of this subgraph
primitive_dst_mem_.reset(new mkldnn::memory(sum_pd_->dst_primitive_desc()));
primitive_dst_mem_.reset(new mkldnn::memory(sum_pd_->dst_desc(), cpu_engine));
} else {
// Last node but re-order not needed. Allocate buffer to output of this node
primitive_dst_mem_.reset(new mkldnn::memory(sum_pd_->dst_primitive_desc(), nullptr));
primitive_dst_mem_.reset(new mkldnn::memory(sum_pd_->dst_desc(), cpu_engine, nullptr));
}
} else {
// Intermediate node. Use mkldnn kernel internal memory for output and
// use this as input to next node.
primitive_dst_mem_.reset(new mkldnn::memory(sum_pd_->dst_primitive_desc()));
primitive_dst_mem_.reset(new mkldnn::memory(sum_pd_->dst_desc(), cpu_engine));
}
primitive_dst_format_ = static_cast<mkldnn::memory::format>(sum_pd_->dst_primitive_desc().desc().data.format);
primitive_dst_desc_ = sum_pd_->dst_desc();
std::vector<mkldnn::primitive::at> inputs;
for (int i = 0; i < num_inputs; i++) {
inputs.push_back(srcs_memory_[i]);
}
auto c = mkldnn::sum(*sum_pd_, inputs, *primitive_dst_mem_);
auto c = mkldnn::sum(*sum_pd_);
net.push_back(c);
std::unordered_map<int, mkldnn::memory> args{
{MKLDNN_ARG_DST, *primitive_dst_mem_}};
for (int i = 0; i < (int)num_inputs; i++) {
args.insert({MKLDNN_ARG_MULTIPLE_SRC + i, srcs_memory_[i]});
}
net_args.push_back(args);
if (mklnode_ptr_->output_index >= 0) {
// one of the end nodes. Allocate output buffer memory and
// reorder is necessary
mkldnn::memory::data_type t = MklDnnType<T>();
InitDstReorderOutput(cpu_engine, t, net);
InitDstReorderOutput(cpu_engine, t, net, net_args);
}
return Status::OK();
}
@ -150,7 +150,7 @@ class MklDnnSum : public MklDnnKernel {
OrtValue* output = ort.KernelContext_GetOutput(context, mklnode_ptr_->output_index, &y_dims[0], static_cast<int>(primitive_dst_shape_.GetDims().size()));
T* dst_data = ort.GetTensorMutableData<T>(output);
if (primitive_dst_format_ != ort_source_format_) {
if (primitive_dst_desc_ != ort_source_desc_) {
reorder_dst_mem_to_->set_data_handle(dst_data);
} else {
primitive_dst_mem_->set_data_handle(dst_data);
@ -161,11 +161,12 @@ class MklDnnSum : public MklDnnKernel {
private:
std::unique_ptr<mkldnn::memory::desc> src_md_;
std::vector<mkldnn::memory::desc> src_mds_;
std::vector<mkldnn::memory> srcs_memory_;
std::vector<mkldnn::memory::primitive_desc> srcs_pd_;
std::unique_ptr<mkldnn::memory::primitive_desc> src_mpd_;
std::unique_ptr<mkldnn::memory::primitive_desc> dst_pd_;
std::vector<mkldnn::memory::desc> srcs_pd_;
std::unique_ptr<mkldnn::memory::desc> src_mpd_;
std::unique_ptr<mkldnn::memory::desc> dst_pd_;
std::unique_ptr<mkldnn::sum::primitive_desc> sum_pd_;
};
} // namespace mkl_dnn

View file

@ -3,7 +3,6 @@
#include "gtest/gtest.h"
#include "test/providers/provider_test_utils.h"
#include "test/common/cuda_op_test_utils.h"
namespace onnxruntime {
namespace test {
@ -30,11 +29,6 @@ TEST(GemmOpTest, GemmNoTrans) {
// Only CUDA kernel has float 16 support
#ifdef USE_CUDA
TEST(GemmOpTest, GemmNoTrans_f16) {
int min_cuda_architecture = 530;
if (!HasCudaEnvironment(min_cuda_architecture)) {
LOGS_DEFAULT(WARNING) << "Hardware NOT support FP16";
return;
}
OpTester test("Gemm");
test.AddAttribute("transA", (int64_t)0);
@ -235,7 +229,7 @@ TEST(GemmOpTest, GemmEmptyTensor) {
test.AddInput<float>("C", {3}, std::vector<float>(3, 1.0f));
test.AddOutput<float>("Y", {0, 3},
{});
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); //TensorRT: doesn't support dynamic shape yet
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kMklDnnExecutionProvider}); //TensorRT: doesn't support dynamic shape yet
}
TEST(GemmOpTest, GemmNoBiasOpset11) {