#include "graph/backend/dnnl/executables/batch_norm.hpp"
namespace dnnl {
namespace impl {
namespace graph {
namespace dnnl_impl {
bn_folding_t::bn_folding_t(std::shared_ptr<op_t> &op,
const dnnl::engine &p_engine, pd_cache_t &pd_cache,
const fpmath_t &fpmath, bool use_block_layout) {
desc_ = create_desc(op, p_engine, pd_cache, fpmath, use_block_layout);
add_prim_ = dnnl::binary(desc_.add_pd_);
#if DNNL_GPU_RUNTIME != DNNL_RUNTIME_NONE \
&& DNNL_GPU_VENDOR == DNNL_VENDOR_NVIDIA
if (p_engine.get_kind() == dnnl::engine::kind::gpu) {
sqrt_prim_ = dnnl::eltwise_forward(desc_.sqrt_pd_);
}
#endif
mul_prim_ = dnnl::binary(desc_.mul_pd_);
sub_prim_ = dnnl::binary(desc_.sub_pd_);
}
void bn_folding_t::execute(const stream &stream,
const std::unordered_map<int, memory> &args) const {
UNUSED(args);
auto weights = args.find(DNNL_ARG_WEIGHTS)->second;
auto bias = desc_.with_bias_ ? args.find(DNNL_ARG_BIAS)->second : memory();
auto scale = args.find(DNNL_ARG_WEIGHTS_1)->second;
auto shift = args.find(DNNL_ARG_WEIGHTS_2)->second;
auto mean = args.find(DNNL_ARG_MEAN)->second;
auto variance = args.find(DNNL_ARG_VARIANCE)->second;
auto scratchpad = args.find(DNNL_ARG_SCRATCHPAD)->second;
auto updated_weights = args.find(DNNL_ARG_DST_0)->second;
auto updated_bias = args.find(DNNL_ARG_DST_1)->second;
char *buf_start = (char *)scratchpad.get_data_handle();
memory sqrt_variance = make_dnnl_memory(
variance.get_desc(), scratchpad.get_engine(), (void *)buf_start);
buf_start += variance.get_desc().get_size();
memory valid_bias = bias;
if (bias.get(true) == nullptr || bias.get_data_handle() == nullptr) {
valid_bias = make_dnnl_memory(variance.get_desc(),
scratchpad.get_engine(), (void *)buf_start);
buf_start += valid_bias.get_desc().get_size();
}
memory epsilon_mem = make_dnnl_memory(
desc_.epsilon_desc_, scratchpad.get_engine(), (void *)buf_start);
if (variance.get_engine().get_kind() == engine::kind::cpu) {
float *ptr = (float *)epsilon_mem.get_data_handle();
*ptr = desc_.epsilon_;
} else {
engine cpu_eng(engine::kind::cpu, 0);
memory cpu_mem = make_dnnl_memory(
desc_.epsilon_desc_, cpu_eng, (void *)&desc_.epsilon_);
dnnl::reorder(cpu_mem, epsilon_mem)
.execute(stream, cpu_mem, epsilon_mem);
}
add_prim_.execute(stream,
{{DNNL_ARG_SRC_0, variance}, {DNNL_ARG_SRC_1, epsilon_mem},
{DNNL_ARG_DST, sqrt_variance}});
memory new_scale(
desc_.new_scale_desc_, scale.get_engine(), scale.get_data_handle());
memory new_sqrt_variance(desc_.new_variance_desc_,
sqrt_variance.get_engine(), sqrt_variance.get_data_handle());
mul_prim_.execute(stream,
{{DNNL_ARG_SRC_0, weights}, {DNNL_ARG_SRC_1, new_scale},
{DNNL_ARG_DST, updated_weights},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(0) | DNNL_ARG_SRC_1,
new_sqrt_variance}});
if (bias.get(true) == nullptr || bias.get_data_handle() == nullptr) {
std::vector<float> zero(
graph::utils::prod(variance.get_desc().get_dims()), 0.0f);
if (mean.get_engine().get_kind() == engine::kind::cpu) {
std::memcpy(valid_bias.get_data_handle(), zero.data(),
valid_bias.get_desc().get_size());
} else {
engine cpu_eng(engine::kind::cpu, 0);
memory cpu_mem = make_dnnl_memory(
variance.get_desc(), cpu_eng, zero.data());
dnnl::reorder(cpu_mem, valid_bias)
.execute(stream, cpu_mem, valid_bias);
}
}
sub_prim_.execute(stream,
{{DNNL_ARG_SRC_0, valid_bias}, {DNNL_ARG_SRC_1, mean},
{DNNL_ARG_DST, updated_bias},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(0) | DNNL_ARG_SRC_1, scale},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(1) | DNNL_ARG_SRC_1,
sqrt_variance},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(2) | DNNL_ARG_SRC_1,
shift}});
}
#ifdef DNNL_WITH_SYCL
std::optional<::sycl::event> bn_folding_t::execute_sycl(const stream &stream,
const std::unordered_map<int, memory> &args,
const std::vector<::sycl::event> &deps) const {
UNUSED(args);
auto weights = args.find(DNNL_ARG_WEIGHTS)->second;
auto bias = desc_.with_bias_ ? args.find(DNNL_ARG_BIAS)->second : memory();
auto scale = args.find(DNNL_ARG_WEIGHTS_1)->second;
auto shift = args.find(DNNL_ARG_WEIGHTS_2)->second;
auto mean = args.find(DNNL_ARG_MEAN)->second;
auto variance = args.find(DNNL_ARG_VARIANCE)->second;
auto scratchpad = args.find(DNNL_ARG_SCRATCHPAD)->second;
auto updated_weights = args.find(DNNL_ARG_DST_0)->second;
auto updated_bias = args.find(DNNL_ARG_DST_1)->second;
char *buf_start = (char *)scratchpad.get_data_handle();
memory sqrt_variance = make_dnnl_memory(
variance.get_desc(), scratchpad.get_engine(), (void *)buf_start);
buf_start += variance.get_desc().get_size();
memory valid_bias = bias;
if (bias.get(true) == nullptr || bias.get_data_handle() == nullptr) {
valid_bias = make_dnnl_memory(variance.get_desc(),
scratchpad.get_engine(), (void *)buf_start);
buf_start += valid_bias.get_desc().get_size();
}
memory epsilon_mem = make_dnnl_memory(
desc_.epsilon_desc_, scratchpad.get_engine(), (void *)buf_start);
auto sycl_queue = dnnl::sycl_interop::get_queue(stream);
::sycl::event sycl_deps;
if (scratchpad.get_engine().get_kind() == engine::kind::gpu) {
#if DNNL_GPU_RUNTIME != DNNL_RUNTIME_NONE \
&& DNNL_GPU_VENDOR == DNNL_VENDOR_NVIDIA
buf_start += epsilon_mem.get_desc().get_size();
memory variance_epsilon = make_dnnl_memory(desc_.epsilon_desc_,
scratchpad.get_engine(), (void *)buf_start);
sycl_queue
.memcpy(epsilon_mem.get_data_handle(), &desc_.epsilon_,
epsilon_mem.get_desc().get_size())
.wait();
auto sycl_deps0 = dnnl::sycl_interop::execute(add_prim_, stream,
{{DNNL_ARG_SRC_0, variance}, {DNNL_ARG_SRC_1, epsilon_mem},
{DNNL_ARG_DST, variance_epsilon}},
deps);
sycl_deps = dnnl::sycl_interop::execute(sqrt_prim_, stream,
{{DNNL_ARG_SRC, variance_epsilon},
{DNNL_ARG_DST, sqrt_variance}},
{sycl_deps0});
#else
sycl_queue
.memcpy(epsilon_mem.get_data_handle(), &desc_.epsilon_,
epsilon_mem.get_desc().get_size())
.wait();
sycl_deps = dnnl::sycl_interop::execute(add_prim_, stream,
{{DNNL_ARG_SRC_0, variance}, {DNNL_ARG_SRC_1, epsilon_mem},
{DNNL_ARG_DST, sqrt_variance}},
deps);
#endif
} else {
sycl_queue
.memcpy(epsilon_mem.get_data_handle(), &desc_.epsilon_,
epsilon_mem.get_desc().get_size())
.wait();
sycl_deps = dnnl::sycl_interop::execute(add_prim_, stream,
{{DNNL_ARG_SRC_0, variance}, {DNNL_ARG_SRC_1, epsilon_mem},
{DNNL_ARG_DST, sqrt_variance}},
deps);
}
memory new_scale(
desc_.new_scale_desc_, scale.get_engine(), scale.get_data_handle());
memory new_sqrt_variance(desc_.new_variance_desc_,
sqrt_variance.get_engine(), sqrt_variance.get_data_handle());
auto sycl_deps2 = dnnl::sycl_interop::execute(mul_prim_, stream,
{{DNNL_ARG_SRC_0, weights}, {DNNL_ARG_SRC_1, new_scale},
{DNNL_ARG_DST, updated_weights},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(0) | DNNL_ARG_SRC_1,
new_sqrt_variance}},
{sycl_deps});
if (bias.get(true) == nullptr || bias.get_data_handle() == nullptr) {
std::vector<float> zero(
graph::utils::prod(variance.get_desc().get_dims()), 0.0f);
sycl_queue
.memcpy(valid_bias.get_data_handle(), zero.data(),
valid_bias.get_desc().get_size())
.wait();
auto sycl_deps3 = dnnl::sycl_interop::execute(sub_prim_, stream,
{{DNNL_ARG_SRC_0, valid_bias}, {DNNL_ARG_SRC_1, mean},
{DNNL_ARG_DST, updated_bias},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(0) | DNNL_ARG_SRC_1,
scale},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(1) | DNNL_ARG_SRC_1,
sqrt_variance},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(2) | DNNL_ARG_SRC_1,
shift}},
{sycl_deps2});
if (stream.get_engine().get_kind() == engine::kind::cpu)
sycl_deps3.wait();
return sycl_deps3;
}
auto sycl_deps3 = dnnl::sycl_interop::execute(sub_prim_, stream,
{{DNNL_ARG_SRC_0, valid_bias}, {DNNL_ARG_SRC_1, mean},
{DNNL_ARG_DST, updated_bias},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(0) | DNNL_ARG_SRC_1, scale},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(1) | DNNL_ARG_SRC_1,
sqrt_variance},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(2) | DNNL_ARG_SRC_1,
shift}},
{sycl_deps2});
if (stream.get_engine().get_kind() == engine::kind::cpu) sycl_deps3.wait();
return sycl_deps3;
}
#endif
#if DNNL_GPU_RUNTIME == DNNL_RUNTIME_OCL
cl_event bn_folding_t::execute_ocl(const stream &stream,
const std::unordered_map<int, memory> &args,
const std::vector<cl_event> &deps) const {
UNUSED(args);
auto weights = args.find(DNNL_ARG_WEIGHTS)->second;
auto bias = desc_.with_bias_ ? args.find(DNNL_ARG_BIAS)->second : memory();
auto scale = args.find(DNNL_ARG_WEIGHTS_1)->second;
auto shift = args.find(DNNL_ARG_WEIGHTS_2)->second;
auto mean = args.find(DNNL_ARG_MEAN)->second;
auto variance = args.find(DNNL_ARG_VARIANCE)->second;
auto scratchpad = args.find(DNNL_ARG_SCRATCHPAD)->second;
auto updated_weights = args.find(DNNL_ARG_DST_0)->second;
auto updated_bias = args.find(DNNL_ARG_DST_1)->second;
char *buf_start = (char *)scratchpad.get_data_handle();
memory sqrt_variance = dnnl::ocl_interop::make_memory(variance.get_desc(),
scratchpad.get_engine(), dnnl::ocl_interop::memory_kind::usm,
(void *)buf_start);
buf_start += variance.get_desc().get_size();
memory valid_bias = bias;
if (bias.get(true) == nullptr || bias.get_data_handle() == nullptr) {
valid_bias = dnnl::ocl_interop::make_memory(variance.get_desc(),
scratchpad.get_engine(), dnnl::ocl_interop::memory_kind::usm,
(void *)buf_start);
buf_start += valid_bias.get_desc().get_size();
}
memory epsilon_mem = dnnl::ocl_interop::make_memory(desc_.epsilon_desc_,
scratchpad.get_engine(), dnnl::ocl_interop::memory_kind::usm,
(void *)buf_start);
cl_event e;
xpu::ocl::usm::memcpy(stream.get(), epsilon_mem.get_data_handle(),
&desc_.epsilon_, epsilon_mem.get_desc().get_size(), 0, nullptr, &e);
xpu::ocl::clWaitForEvents(1, &e);
auto ocl_deps = dnnl::ocl_interop::execute(add_prim_, stream,
{{DNNL_ARG_SRC_0, variance}, {DNNL_ARG_SRC_1, epsilon_mem},
{DNNL_ARG_DST, sqrt_variance}},
deps);
memory new_scale = dnnl::ocl_interop::make_memory(desc_.new_scale_desc_,
scale.get_engine(), dnnl::ocl_interop::memory_kind::usm,
scale.get_data_handle());
memory new_sqrt_variance = dnnl::ocl_interop::make_memory(
desc_.new_variance_desc_, sqrt_variance.get_engine(),
dnnl::ocl_interop::memory_kind::usm,
sqrt_variance.get_data_handle());
auto ocl_deps2 = dnnl::ocl_interop::execute(mul_prim_, stream,
{{DNNL_ARG_SRC_0, weights}, {DNNL_ARG_SRC_1, new_scale},
{DNNL_ARG_DST, updated_weights},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(0) | DNNL_ARG_SRC_1,
new_sqrt_variance}},
{ocl_deps});
if (bias.get(true) == nullptr || bias.get_data_handle() == nullptr) {
std::vector<float> zero(
graph::utils::prod(variance.get_desc().get_dims()), 0.0f);
xpu::ocl::usm::memcpy(stream.get(), valid_bias.get_data_handle(),
zero.data(), valid_bias.get_desc().get_size(), 0, nullptr, &e);
xpu::ocl::clWaitForEvents(1, &e);
auto ocl_deps3 = dnnl::ocl_interop::execute(sub_prim_, stream,
{{DNNL_ARG_SRC_0, valid_bias}, {DNNL_ARG_SRC_1, mean},
{DNNL_ARG_DST, updated_bias},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(0) | DNNL_ARG_SRC_1,
scale},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(1) | DNNL_ARG_SRC_1,
sqrt_variance},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(2) | DNNL_ARG_SRC_1,
shift}},
{ocl_deps2});
return ocl_deps3;
}
auto ocl_deps3 = dnnl::ocl_interop::execute(sub_prim_, stream,
{{DNNL_ARG_SRC_0, valid_bias}, {DNNL_ARG_SRC_1, mean},
{DNNL_ARG_DST, updated_bias},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(0) | DNNL_ARG_SRC_1, scale},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(1) | DNNL_ARG_SRC_1,
sqrt_variance},
{DNNL_ARG_ATTR_MULTIPLE_POST_OP(2) | DNNL_ARG_SRC_1,
shift}},
{ocl_deps2});
return ocl_deps3;
}
#endif
bn_folding_t::desc_t bn_folding_t::create_desc(std::shared_ptr<op_t> &op,
const dnnl::engine &p_engine, pd_cache_t &pd_cache,
const fpmath_t &fpmath, bool use_block_layout) {
UNUSED(pd_cache);
UNUSED(fpmath);
UNUSED(use_block_layout);
desc_t desc;
desc.epsilon_ = op->get_attr<float>(op_attr::epsilon);
desc.data_format_ = op->get_attr<std::string>(op_attr::data_format);
desc.filter_format_ = op->get_attr<std::string>(op_attr::weights_format);
desc.with_bias_ = op->get_attr<bool>(op_attr::with_bias);
size_t in_idx = 0;
auto weights
= make_dnnl_memory_desc(op->get_input_logical_tensor(in_idx++));
auto bias = desc.with_bias_
? make_dnnl_memory_desc(op->get_input_logical_tensor(in_idx++))
: memory::desc();
auto scale = make_dnnl_memory_desc(op->get_input_logical_tensor(in_idx++));
auto shift = make_dnnl_memory_desc(op->get_input_logical_tensor(in_idx++));
auto mean = make_dnnl_memory_desc(op->get_input_logical_tensor(in_idx++));
auto variance
= make_dnnl_memory_desc(op->get_input_logical_tensor(in_idx++));
memory::dims epsilon_dims(variance.get_ndims(), 1);
desc.epsilon_desc_ = memory::desc(
epsilon_dims, memory::data_type::f32, memory::format_tag::a);
#if DNNL_GPU_RUNTIME != DNNL_RUNTIME_NONE \
&& DNNL_GPU_VENDOR == DNNL_VENDOR_NVIDIA
if (p_engine.get_kind() == dnnl::engine::kind::gpu) {
primitive_attr add_attr;
desc.add_pd_
= dnnl::binary::primitive_desc(p_engine, algorithm::binary_add,
variance, desc.epsilon_desc_, variance, add_attr);
primitive_attr sqrt_attr;
desc.sqrt_pd_ = dnnl::eltwise_forward::primitive_desc(p_engine,
prop_kind::forward, algorithm::eltwise_sqrt, variance, variance,
0.0f, 0.0f, sqrt_attr);
} else {
post_ops add_post_ops;
add_post_ops.append_eltwise(algorithm::eltwise_sqrt, 0.0f, 0.0f);
primitive_attr add_attr;
add_attr.set_post_ops(add_post_ops);
desc.add_pd_
= dnnl::binary::primitive_desc(p_engine, algorithm::binary_add,
variance, desc.epsilon_desc_, variance, add_attr);
}
#else
post_ops add_post_ops;
add_post_ops.append_eltwise(algorithm::eltwise_sqrt, 0.0f, 0.0f);
primitive_attr add_attr;
add_attr.set_post_ops(add_post_ops);
desc.add_pd_ = dnnl::binary::primitive_desc(p_engine, algorithm::binary_add,
variance, desc.epsilon_desc_, variance, add_attr);
#endif
desc.new_scale_desc_ = expand(scale, weights.get_ndims());
desc.new_variance_desc_ = expand(variance, weights.get_ndims());
if (desc.filter_format_ == "NCX") { auto perm = dnnl_impl::utils::cast_to_int32(get_permutation(
desc.new_scale_desc_.get_ndims(), "NXC", "NCX"));
desc.new_scale_desc_ = desc.new_scale_desc_.permute_axes(perm);
desc.new_variance_desc_ = desc.new_variance_desc_.permute_axes(perm);
}
if (desc.filter_format_ == "OIX") { auto perm = dnnl_impl::utils::cast_to_int32(get_permutation(
desc.new_scale_desc_.get_ndims(), "XIO", "OIX"));
desc.new_scale_desc_ = desc.new_scale_desc_.permute_axes(perm);
desc.new_variance_desc_ = desc.new_variance_desc_.permute_axes(perm);
}
post_ops mul_post_ops;
mul_post_ops.append_binary(algorithm::binary_div, desc.new_variance_desc_);
primitive_attr mul_attr;
mul_attr.set_post_ops(mul_post_ops);
desc.mul_pd_ = dnnl::binary::primitive_desc(p_engine, algorithm::binary_mul,
weights, desc.new_scale_desc_, weights, mul_attr);
memory::desc valid_bias = bias.is_zero() ? mean : bias;
post_ops sub_post_ops;
sub_post_ops.append_binary(algorithm::binary_mul, scale);
sub_post_ops.append_binary(algorithm::binary_div, variance);
sub_post_ops.append_binary(algorithm::binary_add, shift);
primitive_attr sub_attr;
sub_attr.set_post_ops(sub_post_ops);
desc.sub_pd_ = dnnl::binary::primitive_desc(p_engine, algorithm::binary_sub,
valid_bias, mean, valid_bias, sub_attr);
memory::dims scratchpad_dims = variance.get_dims();
#if DNNL_GPU_RUNTIME != DNNL_RUNTIME_NONE \
&& DNNL_GPU_VENDOR == DNNL_VENDOR_NVIDIA
size_t factor = 0;
if (p_engine.get_kind() == dnnl::engine::kind::gpu) {
factor = bias.is_zero() ? 4 : 3;
} else {
factor = bias.is_zero() ? 3 : 2;
}
#else
size_t factor = bias.is_zero() ? 3 : 2;
#endif
scratchpad_dims[0] *= factor;
desc.scratchpad_desc_ = memory::desc(
scratchpad_dims, variance.get_data_type(), memory::format_tag::a);
return desc;
}
arg_indices_t bn_folding_t::get_arg_indices(const op_t *op) {
arg_indices_t args;
size_t in_idx = 0;
args.insert({DNNL_ARG_WEIGHTS, {indices_t::type_t::input, in_idx++}});
if (op->get_attr<bool>(op_attr::with_bias)) {
args.insert({DNNL_ARG_BIAS, {indices_t::type_t::input, in_idx++}});
}
args.insert({DNNL_ARG_WEIGHTS_1,
{indices_t::type_t::input, in_idx++}}); args.insert({DNNL_ARG_WEIGHTS_2,
{indices_t::type_t::input, in_idx++}}); args.insert({DNNL_ARG_MEAN, {indices_t::type_t::input, in_idx++}}); args.insert({DNNL_ARG_VARIANCE,
{indices_t::type_t::input, in_idx++}});
size_t out_idx = 0;
args.insert({DNNL_ARG_DST_0,
{indices_t::type_t::output, out_idx++}}); args.insert({DNNL_ARG_DST_1,
{indices_t::type_t::output, out_idx++}}); args.insert({DNNL_ARG_SCRATCHPAD,
{indices_t::type_t::output, out_idx++}});
return args;
}
batchnorm_executable_t::desc_t batchnorm_executable_t::create_desc(
std::shared_ptr<op_t> &op, const dnnl::engine &p_engine,
pd_cache_t &pd_cache, const fpmath_t &fpmath, bool use_block_layout) {
if (pd_cache.find(op.get()) != pd_cache.end()) {
auto pd = graph::utils::any_cast<
dnnl::batch_normalization_forward::primitive_desc>(
pd_cache.at(op.get()));
return {pd, true};
}
float epsilon = op->get_attr<float>(op_attr::epsilon);
auto flags = dnnl::normalization_flags::none;
if (!op->get_attr<bool>(op_attr::is_training)) {
flags |= dnnl::normalization_flags::use_global_stats;
flags |= dnnl::normalization_flags::use_scale;
flags |= dnnl::normalization_flags::use_shift;
} else {
if (op->num_inputs() > 3) {
flags |= dnnl::normalization_flags::use_scale;
flags |= dnnl::normalization_flags::use_shift;
}
if (op->has_attr(op_attr::fuse_relu)
&& op->get_attr<bool>(op_attr::fuse_relu))
flags |= dnnl::normalization_flags::fuse_norm_relu;
}
dnnl::primitive_attr prm_attr;
if (op->has_attr(op_attr::fusion_info)) {
const fusion_info_t &fusion_info
= op->get_attr<fusion_info_t>(op_attr::fusion_info);
prm_attr = make_dnnl_primitive_attr(op, fusion_info);
}
prm_attr.set_scratchpad_mode(dnnl::scratchpad_mode::user);
auto src = make_dnnl_memory_desc(op->get_input_logical_tensor(0));
auto dst = make_dnnl_memory_desc(op->get_output_logical_tensor(0));
dst = to_format_any(dst);
if (src.get_inner_nblks() == 1 && src.get_inner_idxs()[0] == 1
&& src.get_inner_blks()[0] == 4) {
src = to_ncx_format(src);
}
auto pkind = op->get_attr<bool>(op_attr::is_training)
? prop_kind::forward_training
: prop_kind::forward_inference;
dnnl::batch_normalization_forward::primitive_desc pd(
p_engine, pkind, src, dst, epsilon, flags, prm_attr);
pd_cache.insert({op.get(), pd});
return {pd, false};
}
void batchnorm_executable_t::execute(const stream &stream,
const std::unordered_map<int, memory> &args) const {
if (!is_training_) {
prim_.execute(stream, args);
return;
}
std::unordered_map<int, memory> exe_args = args;
exe_args.erase(DNNL_ARG_SRC_1);
exe_args.erase(DNNL_ARG_SRC_2);
exe_args.erase(DNNL_ARG_DST_1);
exe_args.erase(DNNL_ARG_DST_2);
prim_.execute(stream, exe_args);
auto it_mean = args.find(DNNL_ARG_MEAN);
auto it_var = args.find(DNNL_ARG_VARIANCE);
auto it_src1 = args.find(DNNL_ARG_SRC_1);
auto it_src2 = args.find(DNNL_ARG_SRC_2);
auto it_dst1 = args.find(DNNL_ARG_DST_1);
auto it_dst2 = args.find(DNNL_ARG_DST_2);
if (graph::utils::one_of(args.end(), it_mean, it_var, it_src1, it_src2,
it_dst1, it_dst2)) {
assert(!"cannot find one of the required memories");
return;
}
auto batch_mean = it_mean->second;
auto batch_variance = it_var->second;
auto old_running_mean = it_src1->second;
auto old_running_variance = it_src2->second;
auto new_running_mean = it_dst1->second;
auto new_running_variance = it_dst2->second;
dnnl::engine p_engine = stream.get_engine();
dnnl::sum({p_engine, scales_,
{old_running_mean.get_desc(), batch_mean.get_desc()}})
.execute(stream,
{{DNNL_ARG_MULTIPLE_SRC, old_running_mean},
{DNNL_ARG_MULTIPLE_SRC + 1, batch_mean},
{DNNL_ARG_DST, new_running_mean}});
dnnl::sum({p_engine, scales_,
{old_running_variance.get_desc(),
batch_variance.get_desc()}})
.execute(stream,
{{DNNL_ARG_MULTIPLE_SRC, old_running_variance},
{DNNL_ARG_MULTIPLE_SRC + 1, batch_variance},
{DNNL_ARG_DST, new_running_variance}});
}
#ifdef DNNL_WITH_SYCL
std::optional<::sycl::event> batchnorm_executable_t::execute_sycl(
const stream &stream, const std::unordered_map<int, memory> &args,
const std::vector<::sycl::event> &deps) const {
if (!is_training_) {
auto e = dnnl::sycl_interop::execute(prim_, stream, args, deps);
if (stream.get_engine().get_kind() == engine::kind::cpu) e.wait();
return e;
}
std::unordered_map<int, memory> exe_args = args;
exe_args.erase(DNNL_ARG_SRC_1);
exe_args.erase(DNNL_ARG_SRC_2);
exe_args.erase(DNNL_ARG_DST_1);
exe_args.erase(DNNL_ARG_DST_2);
auto e0 = dnnl::sycl_interop::execute(prim_, stream, exe_args, deps);
auto batch_mean = args.find(DNNL_ARG_MEAN)->second;
auto batch_variance = args.find(DNNL_ARG_VARIANCE)->second;
auto old_running_mean = args.find(DNNL_ARG_SRC_1)->second;
auto old_running_variance = args.find(DNNL_ARG_SRC_2)->second;
auto new_running_mean = args.find(DNNL_ARG_DST_1)->second;
auto new_running_variance = args.find(DNNL_ARG_DST_2)->second;
dnnl::engine p_engine = stream.get_engine();
auto sum_prim_0 = dnnl::sum({p_engine, scales_,
{old_running_mean.get_desc(), batch_mean.get_desc()}});
auto e1 = dnnl::sycl_interop::execute(sum_prim_0, stream,
{{DNNL_ARG_MULTIPLE_SRC, old_running_mean},
{DNNL_ARG_MULTIPLE_SRC + 1, batch_mean},
{DNNL_ARG_DST, new_running_mean}},
{e0});
auto sum_prim_1 = dnnl::sum({p_engine, scales_,
{old_running_variance.get_desc(), batch_variance.get_desc()}});
auto e2 = dnnl::sycl_interop::execute(sum_prim_1, stream,
{{DNNL_ARG_MULTIPLE_SRC, old_running_variance},
{DNNL_ARG_MULTIPLE_SRC + 1, batch_variance},
{DNNL_ARG_DST, new_running_variance}},
{e1});
if (stream.get_engine().get_kind() == engine::kind::cpu) e2.wait();
return e2;
}
#endif
#if DNNL_GPU_RUNTIME == DNNL_RUNTIME_OCL
cl_event batchnorm_executable_t::execute_ocl(const stream &stream,
const std::unordered_map<int, memory> &args,
const std::vector<cl_event> &deps) const {
if (!is_training_) {
auto e = dnnl::ocl_interop::execute(prim_, stream, args, deps);
return e;
}
std::unordered_map<int, memory> exe_args = args;
exe_args.erase(DNNL_ARG_SRC_1);
exe_args.erase(DNNL_ARG_SRC_2);
exe_args.erase(DNNL_ARG_DST_1);
exe_args.erase(DNNL_ARG_DST_2);
auto e0 = dnnl::ocl_interop::execute(prim_, stream, exe_args, deps);
auto batch_mean = args.find(DNNL_ARG_MEAN)->second;
auto batch_variance = args.find(DNNL_ARG_VARIANCE)->second;
auto old_running_mean = args.find(DNNL_ARG_SRC_1)->second;
auto old_running_variance = args.find(DNNL_ARG_SRC_2)->second;
auto new_running_mean = args.find(DNNL_ARG_DST_1)->second;
auto new_running_variance = args.find(DNNL_ARG_DST_2)->second;
dnnl::engine p_engine = stream.get_engine();
auto sum_prim_0 = dnnl::sum({p_engine, scales_,
{old_running_mean.get_desc(), batch_mean.get_desc()}});
auto e1 = dnnl::ocl_interop::execute(sum_prim_0, stream,
{{DNNL_ARG_MULTIPLE_SRC, old_running_mean},
{DNNL_ARG_MULTIPLE_SRC + 1, batch_mean},
{DNNL_ARG_DST, new_running_mean}},
{e0});
auto sum_prim_1 = dnnl::sum({p_engine, scales_,
{old_running_variance.get_desc(), batch_variance.get_desc()}});
auto e2 = dnnl::ocl_interop::execute(sum_prim_1, stream,
{{DNNL_ARG_MULTIPLE_SRC, old_running_variance},
{DNNL_ARG_MULTIPLE_SRC + 1, batch_variance},
{DNNL_ARG_DST, new_running_variance}},
{e1});
return e2;
}
#endif
batchnorm_bwd_executable_t::desc_t batchnorm_bwd_executable_t::create_desc(
std::shared_ptr<op_t> &op, const dnnl::engine &p_engine,
pd_cache_t &pd_cache, const fpmath_t &fpmath, bool use_block_layout) {
if (pd_cache.find(op.get()) != pd_cache.end()) {
auto pd = graph::utils::any_cast<
dnnl::batch_normalization_backward::primitive_desc>(
pd_cache.at(op.get()));
return {pd, true};
}
float epsilon = op->get_attr<float>(op_attr::epsilon);
auto flags = dnnl::normalization_flags::none;
if (op->num_outputs() > 2) {
flags |= dnnl::normalization_flags::use_scale;
flags |= dnnl::normalization_flags::use_shift;
} else {
flags |= dnnl::normalization_flags::use_global_stats;
}
dnnl::primitive_attr prm_attr;
if (op->has_attr(op_attr::fusion_info)) {
const fusion_info_t &fusion_info
= op->get_attr<fusion_info_t>(op_attr::fusion_info);
prm_attr = make_dnnl_primitive_attr(op, fusion_info);
}
prm_attr.set_scratchpad_mode(dnnl::scratchpad_mode::user);
auto src = make_dnnl_memory_desc(op->get_input_logical_tensor(0));
if (src.get_inner_nblks() == 1 && src.get_inner_idxs()[0] == 1
&& src.get_inner_blks()[0] == 4) {
src = to_ncx_format(src);
}
auto forward_hints = dnnl::batch_normalization_forward::primitive_desc(
p_engine, prop_kind::forward_training, src, src, epsilon, flags);
dnnl::batch_normalization_backward::primitive_desc pd(p_engine,
prop_kind::backward, src, forward_hints.dst_desc(), src, epsilon,
flags, forward_hints);
pd_cache.insert({op.get(), pd});
return {pd, false};
}
arg_indices_t batchnorm_executable_t::get_arg_indices(const op_t *op) {
arg_indices_t args;
size_t idx = 0;
args.insert({DNNL_ARG_SRC, {indices_t::type_t::input, idx++}});
if (!op->get_attr<bool>(op_attr::is_training)) { args.insert({DNNL_ARG_SCALE, {indices_t::type_t::input, idx++}});
args.insert({DNNL_ARG_SHIFT, {indices_t::type_t::input, idx++}});
args.insert({DNNL_ARG_MEAN, {indices_t::type_t::input, idx++}});
args.insert({DNNL_ARG_VARIANCE, {indices_t::type_t::input, idx++}});
} else { args.insert({DNNL_ARG_SRC_1, {indices_t::type_t::input, idx++}});
args.insert({DNNL_ARG_SRC_2, {indices_t::type_t::input, idx++}});
if (op->num_inputs() > 3) {
args.insert({DNNL_ARG_SCALE, {indices_t::type_t::input, idx++}});
args.insert({DNNL_ARG_SHIFT, {indices_t::type_t::input, idx++}});
}
}
idx = 0;
args.insert({DNNL_ARG_DST, {indices_t::type_t::output, idx++}});
if (op->get_attr<bool>(op_attr::is_training)) {
args.insert({DNNL_ARG_DST_1, {indices_t::type_t::output, idx++}});
args.insert({DNNL_ARG_DST_2, {indices_t::type_t::output, idx++}});
args.insert({DNNL_ARG_MEAN, {indices_t::type_t::output, idx++}});
args.insert({DNNL_ARG_VARIANCE, {indices_t::type_t::output, idx++}});
}
if (op->num_outputs() > idx) {
args.insert({DNNL_ARG_SCRATCHPAD, {indices_t::type_t::output, idx++}});
}
if (op->num_outputs() > idx) {
args.insert({DNNL_ARG_WORKSPACE, {indices_t::type_t::output, idx++}});
}
return args;
}
arg_indices_t batchnorm_bwd_executable_t::get_arg_indices(const op_t *op) {
arg_indices_t args;
size_t idx = 0;
args.insert({DNNL_ARG_SRC, {indices_t::type_t::input, idx++}});
args.insert({DNNL_ARG_DIFF_DST, {indices_t::type_t::input, idx++}});
args.insert({DNNL_ARG_MEAN, {indices_t::type_t::input, idx++}});
args.insert({DNNL_ARG_VARIANCE, {indices_t::type_t::input, idx++}});
if (op->num_outputs() > 2) {
args.insert({DNNL_ARG_SCALE, {indices_t::type_t::input, idx++}});
}
idx = 0;
args.insert({DNNL_ARG_DIFF_SRC, {indices_t::type_t::output, idx++}});
if (op->num_outputs() > 2) {
args.insert({DNNL_ARG_DIFF_SCALE, {indices_t::type_t::output, idx++}});
args.insert({DNNL_ARG_DIFF_SHIFT, {indices_t::type_t::output, idx++}});
}
if (op->num_outputs() > idx) {
args.insert({DNNL_ARG_SCRATCHPAD, {indices_t::type_t::output, idx++}});
}
return args;
}
} } } }