#include <functional>
#include <new>
#include "oneapi/dnnl/dnnl_types.h"
#include "common/c_types_map.hpp"
#include "common/dnnl_thread.hpp"
#include "common/nstl.hpp"
#include "common/type_helpers.hpp"
#include "cpu/x64/jit_uni_pooling.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
namespace jit_uni_pooling_utils {
struct trans_wrapper_t {
trans_wrapper_t(data_type_t inp_dt, dim_t inp_str, data_type_t out_dt,
dim_t out_str, dim_t ysize, dim_t xsize)
: inp_dt_size_(types::data_type_size(inp_dt))
, out_dt_size_(types::data_type_size(out_dt))
, inp_str_(inp_str)
, out_str_(out_str)
, nb_x_(xsize / 8)
, nb_y_(ysize / 8)
, x_tail_(xsize % 8)
, y_tail_(ysize % 8) {
using namespace cpu::x64::tr;
auto create_ker = [=](dim_t ys, dim_t y_inp_str, dim_t y_out_str,
dim_t xs, dim_t x_inp_str, dim_t x_out_str) {
tr::prb_t prb;
kernel_t::desc_t desc;
prb.ndims = 2;
prb.ioff = 0;
prb.ooff = 0;
prb.src_scale_type = scale_type_t::NONE;
prb.dst_scale_type = scale_type_t::NONE;
prb.beta = 0;
prb.nodes[0].ss = prb.nodes[1].ss = 1;
prb.itype = inp_dt;
prb.otype = out_dt;
prb.nodes[0].n = ys;
prb.nodes[0].is = y_inp_str;
prb.nodes[0].os = y_out_str;
prb.nodes[1].n = xs;
prb.nodes[1].is = x_inp_str;
prb.nodes[1].os = x_out_str;
prb.full_ndims = prb.ndims;
kernel_t::desc_init(desc, prb, 2);
return kernel_t::create(desc);
};
if (nb_x_ * nb_y_ > 0)
ker_.reset(create_ker(8, inp_str_, 1, 8, 1, out_str_));
if (x_tail_)
ker_x_tail_.reset(create_ker(8, inp_str_, 1, x_tail_, 1, out_str_));
if (y_tail_)
ker_y_tail_.reset(
create_ker(y_tail_, inp_str_, 1, xsize, 1, out_str_));
}
status_t create_kernel() {
if (ker_) CHECK(ker_->create_kernel());
if (ker_x_tail_) CHECK(ker_x_tail_->create_kernel());
if (ker_y_tail_) CHECK(ker_y_tail_->create_kernel());
return status::success;
}
void exec(const void *inp, void *out) {
dim_t x_blocked = nb_x_ * 8;
dim_t y_blocked = nb_y_ * 8;
auto call_ker = [&](tr::kernel_t &ker, dim_t inp_y, dim_t inp_x,
dim_t out_y, dim_t out_x) {
tr::call_param_t cp;
cp.src_scales = nullptr;
cp.dst_scales = nullptr;
dim_t inp_off = (inp_y * inp_str_ + inp_x) * inp_dt_size_;
dim_t out_off = (out_y * out_str_ + out_x) * out_dt_size_;
cp.in = (uint8_t *)inp + inp_off;
cp.out = (uint8_t *)out + out_off;
(ker)(&cp);
};
for (dim_t by = 0; by < nb_y_; by++) {
for (dim_t bx = 0; bx < nb_x_; bx++)
call_ker(*ker_, 8 * by, 8 * bx, 8 * bx, 8 * by);
if (x_tail_)
call_ker(*ker_x_tail_, 8 * by, x_blocked, x_blocked, 8 * by);
}
if (y_tail_) call_ker(*ker_y_tail_, y_blocked, 0, 0, y_blocked);
}
~trans_wrapper_t() = default;
private:
std::unique_ptr<tr::kernel_t> ker_;
std::unique_ptr<tr::kernel_t> ker_x_tail_;
std::unique_ptr<tr::kernel_t> ker_y_tail_;
const size_t inp_dt_size_;
const size_t out_dt_size_;
const dim_t inp_str_;
const dim_t out_str_;
const dim_t nb_x_;
const dim_t nb_y_;
const dim_t x_tail_;
const dim_t y_tail_;
};
struct trans_context_t {
std::unique_ptr<trans_wrapper_t> src_trans_ = nullptr;
std::unique_ptr<trans_wrapper_t> src_tail_trans_ = nullptr;
std::unique_ptr<trans_wrapper_t> ind_trans_ = nullptr;
std::unique_ptr<trans_wrapper_t> ind_tail_trans_ = nullptr;
std::unique_ptr<trans_wrapper_t> dst_trans_ = nullptr;
std::unique_ptr<trans_wrapper_t> dst_tail_trans_ = nullptr;
status_t create_kernel() {
if (src_trans_) CHECK(src_trans_->create_kernel());
if (src_tail_trans_) CHECK(src_tail_trans_->create_kernel());
if (ind_trans_) CHECK(ind_trans_->create_kernel());
if (ind_tail_trans_) CHECK(ind_tail_trans_->create_kernel());
if (dst_trans_) CHECK(dst_trans_->create_kernel());
if (dst_tail_trans_) CHECK(dst_tail_trans_->create_kernel());
return status::success;
}
};
static void trans_exec(trans_wrapper_t *trans, trans_wrapper_t *trans_tail,
dim_t cs, const void *inp, void *out, dim_t c_block) {
if (cs == c_block)
trans->exec(inp, out);
else
trans_tail->exec(inp, out);
}
template <typename src_data_t, typename dst_data_t>
struct transpose_ncsp_to_block_fmt_t {
transpose_ncsp_to_block_fmt_t(trans_wrapper_t *transposer,
trans_wrapper_t *transposer_tail, const src_data_t *src_nscp_base,
const memory_desc_wrapper &src_nscp_desc,
dst_data_t *__restrict dst_blocked_base, dim_t block_size,
const jit_pool_conf_t &jpp, std::size_t offset_multiplier = 1u)
: transposer_(transposer)
, transposer_tail_(transposer_tail)
, c_without_padding_(jpp.c_without_padding)
, c_block_(jpp.c_block)
, src_nscp_base_(src_nscp_base)
, src_nscp_desc_(src_nscp_desc)
, dst_blocked_base_(dst_blocked_base)
, block_size_(block_size)
, offset_multiplier_(offset_multiplier) {}
void operator()(std::size_t ithr, int n, int b_c) const {
const dim_t cs
= nstl::min(c_without_padding_ - b_c * c_block_, c_block_);
const src_data_t *src_nscp = src_nscp_base_
+ src_nscp_desc_.blk_off(n, b_c * c_block_, 0)
* offset_multiplier_;
dst_data_t *dst_blocked
= dst_blocked_base_ + ithr * block_size_ * offset_multiplier_;
trans_exec(transposer_, transposer_tail_, cs, src_nscp, dst_blocked,
c_block_);
}
private:
trans_wrapper_t *transposer_;
trans_wrapper_t *transposer_tail_;
const int c_without_padding_;
const int c_block_;
const src_data_t *src_nscp_base_;
const memory_desc_wrapper &src_nscp_desc_;
dst_data_t *__restrict dst_blocked_base_;
const dim_t block_size_;
std::size_t offset_multiplier_;
};
template <typename src_data_t, typename dst_data_t>
struct transpose_block_fmt_to_ncsp_t {
transpose_block_fmt_to_ncsp_t(trans_wrapper_t *transposer,
trans_wrapper_t *transposer_tail,
const src_data_t *__restrict src_blocked_base, dim_t block_size,
dst_data_t *dst_ncsp_base, const memory_desc_wrapper &dst_nscp_desc,
const jit_pool_conf_t &jpp, std::size_t offset_multiplier = 1u)
: transposer_(transposer)
, transposer_tail_(transposer_tail)
, c_without_padding_(jpp.c_without_padding)
, c_block_(jpp.c_block)
, src_blocked_base_(src_blocked_base)
, block_size_(block_size)
, dst_ncsp_base_(dst_ncsp_base)
, dst_nscp_desc_(dst_nscp_desc)
, offset_multiplier_(offset_multiplier) {}
void operator()(std::size_t ithr, int n, int b_c) const {
const dim_t cs
= nstl::min(c_without_padding_ - b_c * c_block_, c_block_);
const src_data_t *src_blocked
= src_blocked_base_ + ithr * block_size_ * offset_multiplier_;
dst_data_t *dst_ncsp = dst_ncsp_base_
+ dst_nscp_desc_.blk_off(n, b_c * c_block_, 0)
* offset_multiplier_;
trans_exec(transposer_, transposer_tail_, cs, src_blocked, dst_ncsp,
c_block_);
}
private:
trans_wrapper_t *transposer_;
trans_wrapper_t *transposer_tail_;
const int c_without_padding_;
const int c_block_;
const src_data_t *__restrict src_blocked_base_;
const dim_t block_size_;
dst_data_t *dst_ncsp_base_;
memory_desc_wrapper dst_nscp_desc_;
std::size_t offset_multiplier_;
};
template <typename wsp_data_t, impl::data_type_t d_type>
class transpose_facade_base_t {
public:
transpose_facade_base_t(const jit_pool_conf_t &jpp,
const memory_desc_wrapper &src_d, const memory_desc_wrapper &dst_d,
const memory_desc_wrapper &indices_d, const char *indices,
const data_type_t wsp_dt, const exec_ctx_t &ctx)
: src_sp_(static_cast<dim_t>(jpp.id) * jpp.ih * jpp.iw)
, dst_sp_(static_cast<dim_t>(jpp.od) * jpp.oh * jpp.ow)
, src_slice_(src_sp_ * jpp.c_block)
, dst_slice_(dst_sp_ * jpp.c_block)
, transpose_src_(jpp.tag_kind == jit_memory_tag_kind_t::ncsp)
, transpose_dst_(jpp.tag_kind == jit_memory_tag_kind_t::ncsp)
, src_d_(src_d)
, dst_d_(dst_d)
, indices_d_(indices_d)
, ind_dt_size_(
indices ? types::data_type_size(indices_d_.data_type()) : 0)
, cvt_slice_src_wsp_(nullptr)
, cvt_slice_dst_wsp_(nullptr)
, cvt_slice_ind_wsp_(nullptr)
, execute_transpose_input_(nullptr)
, execute_transpose_output_(nullptr) {
const auto &scratchpad = ctx.get_scratchpad_grantor();
if (transpose_src_)
cvt_slice_src_wsp_ = scratchpad.template get<wsp_data_t>(
memory_tracking::names::key_pool_src_plain2blocked_cvt);
if (transpose_dst_) {
cvt_slice_dst_wsp_ = scratchpad.template get<wsp_data_t>(
memory_tracking::names::key_pool_dst_plain2blocked_cvt);
cvt_slice_ind_wsp_ = scratchpad.template get<char>(
memory_tracking::names::key_pool_ind_plain2blocked_cvt);
}
}
inline bool should_transpose_src() const noexcept { return transpose_src_; }
inline bool should_transpose_dst() const noexcept { return transpose_dst_; }
const void *get_src_addr(
std::size_t ithr, int ih, const jit_pool_conf_t &jpp) const {
const wsp_data_t *const wsp = cvt_slice_src_wsp_ + ithr * src_slice_;
return static_cast<const void *>(&wsp[ih * jpp.iw * jpp.c_block]);
}
const void *get_dst_addr(
std::size_t ithr, int oh, const jit_pool_conf_t &jpp) const {
const wsp_data_t *const wsp = cvt_slice_dst_wsp_ + ithr * dst_slice_;
return static_cast<const void *>(&wsp[oh * jpp.ow * jpp.c_block]);
}
const void *get_indices_addr(
std::size_t ithr, int oh, const jit_pool_conf_t &jpp) const {
const char *const wsp
= cvt_slice_ind_wsp_ + ithr * dst_slice_ * ind_dt_size_;
return static_cast<const void *>(
&wsp[oh * jpp.ow * jpp.c_block * ind_dt_size_]);
}
const void *get_src_addr_3d(std::size_t ithr, int id, int ih,
const jit_pool_conf_t &jpp) const {
const wsp_data_t *const wsp = cvt_slice_src_wsp_ + ithr * src_slice_;
return static_cast<const void *>(&wsp[ih * jpp.iw * jpp.c_block
+ id * jpp.ih * jpp.iw * jpp.c_block]);
}
const void *get_dst_addr_3d(std::size_t ithr, int od, int oh,
const jit_pool_conf_t &jpp) const {
const wsp_data_t *const wsp = cvt_slice_dst_wsp_ + ithr * dst_slice_;
return static_cast<const void *>(&wsp[oh * jpp.ow * jpp.c_block
+ od * jpp.oh * jpp.ow * jpp.c_block]);
}
const void *get_indices_addr_3d(std::size_t ithr, int od, int oh,
const jit_pool_conf_t &jpp) const {
const char *const wsp
= cvt_slice_ind_wsp_ + ithr * dst_slice_ * ind_dt_size_;
return static_cast<const void *>(
&wsp[oh * jpp.ow * jpp.c_block * ind_dt_size_
+ od * jpp.oh * jpp.ow * jpp.c_block * ind_dt_size_]);
}
void execute_transpose_input(std::size_t ithr, int n, int b_c) const {
execute_transpose_input_(ithr, n, b_c);
}
void execute_transpose_output(std::size_t ithr, int n, int b_c) const {
execute_transpose_output_(ithr, n, b_c);
}
protected:
const dim_t src_sp_;
const dim_t dst_sp_;
const dim_t src_slice_;
const dim_t dst_slice_;
const bool transpose_src_;
const bool transpose_dst_;
memory_desc_wrapper src_d_;
memory_desc_wrapper dst_d_;
memory_desc_wrapper indices_d_;
const size_t ind_dt_size_;
wsp_data_t *__restrict cvt_slice_src_wsp_;
wsp_data_t *__restrict cvt_slice_dst_wsp_;
char *__restrict cvt_slice_ind_wsp_;
std::function<void(std::size_t, int, int)> execute_transpose_input_;
std::function<void(std::size_t, int, int)> execute_transpose_output_;
};
template <typename data_t, typename wsp_data_t, impl::data_type_t d_type>
class fwd_pooling_transpose_facade_t
: public transpose_facade_base_t<wsp_data_t, d_type> {
public:
fwd_pooling_transpose_facade_t(const jit_pool_conf_t &jpp,
trans_context_t *trans_ctx, const memory_desc_wrapper &src_d,
const memory_desc_wrapper &dst_d,
const memory_desc_wrapper &indices_d, const data_type_t wsp_dt,
const data_t *src, data_t *dst, char *indices,
const exec_ctx_t &ctx)
: transpose_facade_base_t<wsp_data_t, d_type>(
jpp, src_d, dst_d, indices_d, indices, wsp_dt, ctx) {
if (this->should_transpose_src()) {
this->execute_transpose_input_
= transpose_ncsp_to_block_fmt_t<data_t, wsp_data_t>(
trans_ctx->src_trans_.get(),
trans_ctx->src_tail_trans_.get(), src, this->src_d_,
this->cvt_slice_src_wsp_, this->src_slice_, jpp);
}
if (this->should_transpose_dst()) {
using namespace std::placeholders;
this->execute_transpose_output_ = std::bind(
[=](const transpose_block_fmt_to_ncsp_t<wsp_data_t, data_t>
&trans_dst,
transpose_block_fmt_to_ncsp_t<char, char>
&trans_indices,
std::size_t ithr, int n, int b_c) {
trans_dst(ithr, n, b_c);
if (indices) trans_indices(ithr, n, b_c);
},
transpose_block_fmt_to_ncsp_t<wsp_data_t, data_t>(
trans_ctx->dst_trans_.get(),
trans_ctx->dst_tail_trans_.get(),
this->cvt_slice_dst_wsp_, this->dst_slice_, dst,
this->dst_d_, jpp, 1u),
transpose_block_fmt_to_ncsp_t<char, char>(
trans_ctx->ind_trans_.get(),
trans_ctx->ind_tail_trans_.get(),
this->cvt_slice_ind_wsp_, this->dst_slice_, indices,
this->indices_d_, jpp, this->ind_dt_size_),
_1, _2, _3);
}
}
};
template <typename data_t, typename wsp_data_t, impl::data_type_t d_type>
class bwd_pooling_transpose_facade_t
: public transpose_facade_base_t<wsp_data_t, d_type> {
public:
bwd_pooling_transpose_facade_t(const jit_pool_conf_t &jpp,
trans_context_t *trans_ctx, const memory_desc_wrapper &src_d,
const memory_desc_wrapper &dst_d,
const memory_desc_wrapper &indices_d, const data_type_t wsp_dt,
data_t *src, const data_t *dst, const char *indices,
const exec_ctx_t &ctx)
: transpose_facade_base_t<wsp_data_t, d_type>(
jpp, src_d, dst_d, indices_d, indices, wsp_dt, ctx)
, c_tail_(jpp.c_without_padding % jpp.c_block) {
if (this->should_transpose_src())
this->execute_transpose_output_
= transpose_block_fmt_to_ncsp_t<wsp_data_t, data_t>(
trans_ctx->src_trans_.get(),
trans_ctx->src_tail_trans_.get(),
this->cvt_slice_src_wsp_, this->src_slice_, src,
this->src_d_, jpp, 1u);
if (this->should_transpose_dst()) {
using namespace std::placeholders;
this->execute_transpose_input_ = std::bind(
[=](const transpose_ncsp_to_block_fmt_t<data_t, wsp_data_t>
&trans_dst,
transpose_ncsp_to_block_fmt_t<char, char>
&trans_indices,
std::size_t ithr, int n, int b_c) {
trans_dst(ithr, n, b_c);
if (indices) trans_indices(ithr, n, b_c);
},
transpose_ncsp_to_block_fmt_t<data_t, wsp_data_t>(
trans_ctx->dst_trans_.get(),
trans_ctx->dst_tail_trans_.get(), dst, this->dst_d_,
this->cvt_slice_dst_wsp_, this->dst_slice_, jpp),
transpose_ncsp_to_block_fmt_t<char, char>(
trans_ctx->ind_trans_.get(),
trans_ctx->ind_tail_trans_.get(), indices,
this->indices_d_, this->cvt_slice_ind_wsp_,
this->dst_slice_, jpp, this->ind_dt_size_),
_1, _2, _3);
}
}
inline bool should_fill_input_c_tail_with_zeros() const noexcept {
return this->should_transpose_dst() && c_tail_ != 0;
}
void fill_input_c_tail_with_zeros(
std::size_t ithr, const jit_pool_conf_t &jpp) const {
wsp_data_t *__restrict wsp_ptr
= this->cvt_slice_dst_wsp_ + ithr * this->dst_slice_;
for_(dim_t s = 0; s < this->dst_sp_; s++)
for (dim_t c = c_tail_; c < jpp.c_block; c++)
wsp_ptr[s * jpp.c_block + c] = 0.f;
char *__restrict ind_ptr = this->cvt_slice_ind_wsp_
+ ithr * this->dst_slice_ * this->ind_dt_size_;
for_(dim_t s = 0; s < this->dst_sp_; s++)
for_(dim_t c = c_tail_; c < jpp.c_block; c++)
for (size_t i = 0; i < this->ind_dt_size_; i++)
ind_ptr[(s * jpp.c_block + c) * this->ind_dt_size_ + i] = 0;
}
private:
const dim_t c_tail_;
};
struct bwd_f32_accum_for_bf16_t {
using value_type = typename prec_traits_t<data_type::f32>::type;
bwd_f32_accum_for_bf16_t(const jit_pool_conf_t &jpp, const exec_ctx_t &ctx);
value_type *get_addr_2d(int ithr, dim_t ih) const {
return blk_data(ithr, 0, ih, 0);
}
value_type *get_addr_3d(int ithr, dim_t id, dim_t ih) const {
return blk_data(ithr, 0, id, ih, 0);
}
void zero_data(int ithr);
void cvt_to_bf16_slice_2d(int ithr, bfloat16_t *dst,
memory_desc_wrapper const &dst_d, dim_t n, dim_t b_c,
dim_t ur_bc) const;
void cvt_to_bf16_slice_3d(int ithr, bfloat16_t *dst,
memory_desc_wrapper const &dst_d, dim_t n, dim_t b_c,
dim_t ur_bc) const;
private:
template <typename... Args>
value_type *blk_data(Args... args) const {
assert(wsp_);
return wsp_ + accum_d_.blk_off(std::forward<Args>(args)...);
}
const jit_pool_conf_t &jpp_;
value_type *wsp_ {nullptr};
memory_desc_wrapper accum_d_ {nullptr};
};
bwd_f32_accum_for_bf16_t::bwd_f32_accum_for_bf16_t(
const jit_pool_conf_t &jpp, const exec_ctx_t &ctx)
: jpp_ {jpp} {
if (jpp_.needs_f32_accum_for_bf16) {
accum_d_ = memory_desc_wrapper(jpp_.tmp_md);
const auto &scratchpad = ctx.get_scratchpad_grantor();
wsp_ = scratchpad.template get<value_type>(
memory_tracking::names::key_pool_src_f32_accum);
assert(wsp_);
}
}
void bwd_f32_accum_for_bf16_t::zero_data(int ithr) {
auto *data = blk_data(ithr);
memset(data, 0,
jpp_.tmp_md.format_desc.blocking.strides[0] * sizeof(value_type));
}
void bwd_f32_accum_for_bf16_t::cvt_to_bf16_slice_2d(int ithr, bfloat16_t *dst,
memory_desc_wrapper const &dst_d, dim_t n, dim_t b_c,
dim_t ur_bc) const {
assert(wsp_ && (jpp_.ndims == 3 || jpp_.ndims == 4)
&& (jpp_.tag_kind == jit_memory_tag_kind_t::nspc
|| jpp_.tag_kind == jit_memory_tag_kind_t::blocked));
if (jpp_.tag_kind == jit_memory_tag_kind_t::nspc) {
if (jpp_.tmp_md.dims[1] == jpp_.c && b_c == 0
&& jpp_.c == ur_bc * jpp_.c_block) {
const size_t nelems = jpp_.ih * jpp_.iw * jpp_.c;
const auto *cur_src = blk_data(ithr);
auto *cur_dst = dst + dst_d.blk_off(n);
cvt_float_to_bfloat16(cur_dst, cur_src, nelems);
} else {
const auto c_b = jpp_.c_block * b_c;
const auto c_e = nstl::min(
static_cast<dim_t>(jpp_.c), jpp_.c_block * (b_c + ur_bc));
if (c_b >= c_e) return;
const size_t nelems = c_e - c_b;
if (jpp_.ndims == 4) {
for (dim_t h = 0; h < jpp_.ih; ++h) {
for (dim_t w = 0; w < jpp_.iw; ++w) {
const auto *cur_src = blk_data(ithr, 0, h, w);
auto *cur_dst = dst + dst_d.blk_off(n, c_b, h, w);
cvt_float_to_bfloat16(cur_dst, cur_src, nelems);
}
}
} else {
for (dim_t w = 0; w < jpp_.iw; ++w) {
const auto *cur_src = blk_data(ithr, 0, w);
auto *cur_dst = dst + dst_d.blk_off(n, c_b, w);
cvt_float_to_bfloat16(cur_dst, cur_src, nelems);
}
}
}
} else if (jpp_.tag_kind == jit_memory_tag_kind_t::blocked) {
assert(ur_bc == 1);
const size_t nelems = jpp_.ih * jpp_.iw * jpp_.c_block;
const auto *src_b = blk_data(ithr);
auto *dst_b = dst + dst_d.blk_off(n, b_c);
cvt_float_to_bfloat16(dst_b, src_b, nelems);
}
}
void bwd_f32_accum_for_bf16_t::cvt_to_bf16_slice_3d(int ithr, bfloat16_t *dst,
memory_desc_wrapper const &dst_d, dim_t n, dim_t b_c,
dim_t ur_bc) const {
assert(wsp_ && jpp_.ndims == 5
&& (jpp_.tag_kind == jit_memory_tag_kind_t::nspc
|| jpp_.tag_kind == jit_memory_tag_kind_t::blocked));
if (jpp_.tag_kind == jit_memory_tag_kind_t::blocked) {
assert(ur_bc == 1);
const size_t nelems = jpp_.id * jpp_.ih * jpp_.iw * jpp_.c_block;
const auto *src_b = blk_data(ithr);
auto *dst_b = dst + dst_d.blk_off(n, b_c);
cvt_float_to_bfloat16(dst_b, src_b, nelems);
} else if (jpp_.tag_kind == jit_memory_tag_kind_t::nspc) {
if (jpp_.tmp_md.dims[1] == jpp_.c && b_c == 0
&& jpp_.c == ur_bc * jpp_.c_block) {
const size_t nelems = jpp_.id * jpp_.ih * jpp_.iw * jpp_.c;
cvt_float_to_bfloat16(
dst + dst_d.blk_off(n), blk_data(ithr), nelems);
} else {
const auto c_b = jpp_.c_block * b_c;
const auto c_e = nstl::min(
static_cast<dim_t>(jpp_.c), jpp_.c_block * (b_c + ur_bc));
if (c_b >= c_e) return;
const size_t nelems = c_e - c_b;
for (dim_t id = 0; id < jpp_.id; ++id) {
for (dim_t h = 0; h < jpp_.ih; ++h) {
for (dim_t w = 0; w < jpp_.iw; ++w) {
const auto *cur_src = blk_data(ithr, 0, id, h, w);
auto *cur_dst = dst + dst_d.blk_off(n, c_b, id, h, w);
cvt_float_to_bfloat16(cur_dst, cur_src, nelems);
}
}
}
}
}
}
}
template <cpu_isa_t isa, impl::data_type_t d_type>
jit_uni_pooling_fwd_t<isa, d_type>::jit_uni_pooling_fwd_t(const pd_t *apd)
: primitive_t(apd), kernel_(nullptr), trans_ctx_(nullptr) {}
template <cpu_isa_t isa, impl::data_type_t d_type>
status_t jit_uni_pooling_fwd_t<isa, d_type>::init(engine_t *engine) {
CHECK(safe_ptr_assign(kernel_,
new jit_uni_pool_kernel_t<isa>(
pd()->jpp_, pd()->invariant_dst_md())));
if (pd()->jpp_.tag_kind == jit_memory_tag_kind_t::ncsp)
CHECK(init_ncsp_trans_ctx());
return kernel_->create_kernel();
}
template <cpu_isa_t isa, data_type_t d_type>
status_t jit_uni_pooling_fwd_t<isa, d_type>::init_ncsp_trans_ctx() {
using namespace dnnl::impl;
using namespace jit_uni_pooling_utils;
const auto &jpp = pd()->jpp_;
trans_ctx_ = utils::make_unique<trans_context_t>();
const dim_t src_sp = static_cast<dim_t>(jpp.id) * jpp.ih * jpp.iw;
const dim_t dst_sp = static_cast<dim_t>(jpp.od) * jpp.oh * jpp.ow;
const auto res = std::div(jpp.c_without_padding, jpp.c_block);
const dim_t &nb_c = res.quot;
const dim_t &c_tail = res.rem;
const memory_desc_wrapper indices_d = pd()->workspace_md();
const bool have_indices = indices_d.data_type() != data_type::undef;
if (nb_c) {
trans_ctx_->src_trans_ = utils::make_unique<trans_wrapper_t>(d_type,
src_sp, data_type::f32, jpp.c_block, jpp.c_block, src_sp);
trans_ctx_->dst_trans_
= utils::make_unique<trans_wrapper_t>(data_type::f32,
jpp.c_block, d_type, dst_sp, dst_sp, jpp.c_block);
if (have_indices)
trans_ctx_->ind_trans_ = utils::make_unique<trans_wrapper_t>(
indices_d.data_type(), jpp.c_block, indices_d.data_type(),
dst_sp, dst_sp, jpp.c_block);
}
if (c_tail) {
trans_ctx_->src_tail_trans_ = utils::make_unique<trans_wrapper_t>(
d_type, src_sp, data_type::f32, jpp.c_block, c_tail, src_sp);
trans_ctx_->dst_tail_trans_ = utils::make_unique<trans_wrapper_t>(
data_type::f32, jpp.c_block, d_type, dst_sp, dst_sp, c_tail);
if (have_indices)
trans_ctx_->ind_tail_trans_ = utils::make_unique<trans_wrapper_t>(
indices_d.data_type(), jpp.c_block, indices_d.data_type(),
dst_sp, dst_sp, c_tail);
}
return trans_ctx_->create_kernel();
}
template <cpu_isa_t isa, impl::data_type_t d_type>
jit_uni_pooling_fwd_t<isa, d_type>::~jit_uni_pooling_fwd_t() = default;
template <cpu_isa_t isa, data_type_t d_type>
void jit_uni_pooling_fwd_t<isa, d_type>::execute_forward(
const exec_ctx_t &ctx) const {
auto src = CTX_IN_MEM(const data_t *, DNNL_ARG_SRC);
auto dst = CTX_OUT_MEM(data_t *, DNNL_ARG_DST);
auto indices = CTX_OUT_MEM(char *, DNNL_ARG_WORKSPACE);
const memory_desc_wrapper src_d = pd()->src_md();
const memory_desc_wrapper dst_d = pd()->dst_md();
const memory_desc_wrapper indices_d = pd()->workspace_md();
const auto ind_dt_size
= indices ? types::data_type_size(indices_d.data_type()) : 0;
const auto &jpp = pd()->jpp_;
const auto post_ops_binary_rhs_arg_vec
= binary_injector::prepare_binary_args(jpp.post_ops, ctx);
using namespace jit_uni_pooling_utils;
auto transpose_facade = std::make_shared<
fwd_pooling_transpose_facade_t<data_t, float, d_type>>(jpp,
trans_ctx_.get(), src_d, dst_d, indices_d, data_type::f32, src, dst,
indices, ctx);
const auto trans_src = transpose_facade->should_transpose_src();
const auto trans_dst = transpose_facade->should_transpose_dst();
const auto ker = [= COMPAT_THIS_CAPTURE](std::size_t ithr, int n, int b_c,
int oh, int ur_bc) {
assert(ur_bc == jpp.ur_bc || ur_bc == jpp.ur_bc_tail);
jit_uni_pooling_args_t args;
const int ij = oh * jpp.stride_h;
const int i_t_overflow = nstl::max(0, jpp.t_pad - ij);
const int i_b_overflow
= nstl::max(jpp.ih, ij + jpp.kh - jpp.t_pad) - jpp.ih;
const int ih = nstl::max(ij - jpp.t_pad, 0);
assert(IMPLICATION(pd()->ndims() == 3, utils::everyone_is(0, ih, oh)));
const int c_off
= ((jpp.tag_kind == jit_memory_tag_kind_t::nspc) ? jpp.c_block
: 1)
* b_c;
if (trans_src)
args.src = transpose_facade->get_src_addr(ithr, ih, jpp);
else
args.src = static_cast<const void *>(
&src[src_d.blk_off(n, c_off, ih)]);
args.dst_orig = dst;
if (trans_dst) {
args.dst = transpose_facade->get_dst_addr(ithr, oh, jpp);
if (!types::is_zero_md(&jpp.tmp_md)) {
const memory_desc_wrapper tmp_d
= memory_desc_wrapper(jpp.tmp_md);
const int dt_scale
= sizeof(float) / types::data_type_size(d_type);
const auto blk_off = tmp_d.blk_off(n, c_off, oh) * dt_scale;
args.dst_po_helper = static_cast<const void *>(&dst[blk_off]);
}
} else {
args.dst = static_cast<const void *>(
&dst[dst_d.blk_off(n, c_off, oh)]);
}
if (indices) {
if (trans_dst)
args.indices
= transpose_facade->get_indices_addr(ithr, oh, jpp);
else {
const size_t ind_off = indices_d.blk_off(n, c_off, oh);
args.indices = static_cast<const void *>(
&indices[ind_off * ind_dt_size]);
}
}
args.kh_padding = jpp.kh - i_t_overflow - i_b_overflow;
args.kh_padding_shift = i_t_overflow * jpp.kw;
args.ker_area_h = static_cast<float>(jpp.kh
- nstl::max(0, oh * jpp.stride_h - jpp.t_pad + jpp.kh - jpp.ih)
- nstl::max(0, jpp.t_pad - oh * jpp.stride_h));
args.ur_bc = ur_bc;
args.b_c = b_c;
args.post_ops_binary_rhs_arg_vec = post_ops_binary_rhs_arg_vec.data();
(*kernel_)(&args);
};
const int nthr = jpp.nthr;
if (jpp.tag_kind == jit_memory_tag_kind_t::nspc) {
const auto nb2_c = utils::div_up(jpp.nb_c, jpp.ur_bc);
parallel_nd(jpp.mb, jpp.oh, nb2_c, [=](dim_t n, dim_t oh, dim_t b2_c) {
const auto b_c = b2_c * jpp.ur_bc;
const auto ur_bc = nstl::min(dim_t(jpp.ur_bc), jpp.nb_c - b_c);
ker(0, n, b_c, oh, ur_bc);
});
} else {
if (trans_src || trans_dst) {
parallel_nd_ext(nthr, jpp.mb, jpp.nb_c,
[=](dim_t ithr, dim_t nthr, dim_t n, dim_t b_c) {
if (trans_src)
transpose_facade->execute_transpose_input(ithr, n, b_c);
for (dim_t oh = 0; oh < jpp.oh; ++oh)
ker(ithr, n, b_c, oh, 1);
if (trans_dst)
transpose_facade->execute_transpose_output(ithr, n, b_c);
});
} else {
parallel(nthr, [=](dim_t ithr, dim_t nthr) {
dim_t work_amount
= static_cast<dim_t>(jpp.mb) * jpp.nb_c * jpp.oh;
if (ithr >= work_amount) return;
dim_t start {0}, end {0};
dim_t n {0}, b_c {0}, oh {0};
balance211(work_amount, nthr, ithr, start, end);
utils::nd_iterator_init(
start, n, jpp.mb, b_c, jpp.nb_c, oh, jpp.oh);
for (dim_t iwork = start; iwork < end; ++iwork) {
ker(ithr, n, b_c, oh, 1);
utils::nd_iterator_step(
n, jpp.mb, b_c, jpp.nb_c, oh, jpp.oh);
}
});
}
}
}
template <cpu_isa_t isa, data_type_t d_type>
void jit_uni_pooling_fwd_t<isa, d_type>::execute_forward_3d(
const exec_ctx_t &ctx) const {
auto src = CTX_IN_MEM(const data_t *, DNNL_ARG_SRC);
auto dst = CTX_OUT_MEM(data_t *, DNNL_ARG_DST);
auto indices = CTX_OUT_MEM(char *, DNNL_ARG_WORKSPACE);
const auto &jpp = pd()->jpp_;
const memory_desc_wrapper src_d(pd()->src_md());
const memory_desc_wrapper dst_d(pd()->dst_md());
const memory_desc_wrapper indices_d(pd()->workspace_md());
const size_t ind_dt_size
= indices ? types::data_type_size(indices_d.data_type()) : 0;
const auto post_ops_binary_rhs_arg_vec
= binary_injector::prepare_binary_args(jpp.post_ops, ctx);
using namespace jit_uni_pooling_utils;
static constexpr int first_ithr = 0;
auto transpose_facade = std::make_shared<
fwd_pooling_transpose_facade_t<data_t, float, d_type>>(jpp,
trans_ctx_.get(), src_d, dst_d, indices_d, data_type::f32, src, dst,
indices, ctx);
const auto trans_src = transpose_facade->should_transpose_src();
const auto trans_dst = transpose_facade->should_transpose_dst();
auto ker
= [= COMPAT_THIS_CAPTURE](int n, int b_c, int od, int oh, int id,
int d_t_overflow, int d_b_overflow, int ur_bc, int ithr) {
assert(ur_bc == jpp.ur_bc || ur_bc == jpp.ur_bc_tail);
jit_uni_pooling_args_t args;
const int ij = oh * jpp.stride_h;
const int i_t_overflow = nstl::max(0, jpp.t_pad - ij);
const int i_b_overflow
= nstl::max(jpp.ih, ij + jpp.kh - jpp.t_pad) - jpp.ih;
const int ih = nstl::max(ij - jpp.t_pad, 0);
const int c_off
= ((jpp.tag_kind == jit_memory_tag_kind_t::nspc) ? jpp.c_block
: 1)
* b_c;
if (trans_src)
args.src = transpose_facade->get_src_addr_3d(ithr, id, ih, jpp);
else
args.src = &src[src_d.blk_off(n, c_off, id, ih)];
args.dst_orig = dst;
if (trans_dst) {
args.dst = transpose_facade->get_dst_addr_3d(ithr, od, oh, jpp);
if (!types::is_zero_md(&jpp.tmp_md)) {
const memory_desc_wrapper tmp_d
= memory_desc_wrapper(jpp.tmp_md);
const int dt_scale
= sizeof(float) / types::data_type_size(d_type);
const auto blk_off = tmp_d.blk_off(n, c_off, od, oh) * dt_scale;
args.dst_po_helper = static_cast<const void *>(&dst[blk_off]);
}
} else {
args.dst = &dst[dst_d.blk_off(n, c_off, od, oh)];
}
if (indices) {
if (trans_dst) {
args.indices = transpose_facade->get_indices_addr_3d(
ithr, od, oh, jpp);
} else {
const size_t ind_off = indices_d.blk_off(n, c_off, od, oh);
args.indices = &indices[ind_off * ind_dt_size];
}
}
args.kd_padding = jpp.kd - d_t_overflow - d_b_overflow;
args.kh_padding = jpp.kh - i_t_overflow - i_b_overflow;
args.kh_padding_shift
= i_t_overflow * jpp.kw + d_t_overflow * jpp.kw * jpp.kh;
args.kd_padding_shift = (i_t_overflow + i_b_overflow) * jpp.kw;
args.ker_area_h = (float)(jpp.kh
- nstl::max(0,
oh * jpp.stride_h - jpp.t_pad + jpp.kh
- jpp.ih)
- nstl::max(0, jpp.t_pad - oh * jpp.stride_h))
* (jpp.kd
- nstl::max(0,
od * jpp.stride_d - jpp.f_pad + jpp.kd - jpp.id)
- nstl::max(0, jpp.f_pad - od * jpp.stride_d));
args.ur_bc = ur_bc;
args.b_c = b_c;
args.post_ops_binary_rhs_arg_vec = post_ops_binary_rhs_arg_vec.data();
(*kernel_)(&args);
};
const int nthr = jpp.nthr;
if (jpp.tag_kind == jit_memory_tag_kind_t::nspc) {
const auto nb2_c = utils::div_up(jpp.nb_c, jpp.ur_bc);
parallel_nd(jpp.mb, jpp.od, nb2_c, [=](dim_t n, dim_t od, dim_t b2_c) {
const dim_t b_c = b2_c * jpp.ur_bc;
const dim_t ur_bc = nstl::min(dim_t(jpp.ur_bc), jpp.nb_c - b_c);
const dim_t ik = od * jpp.stride_d;
const dim_t d_t_overflow = nstl::max(dim_t(0), jpp.f_pad - ik);
const dim_t d_b_overflow
= nstl::max(dim_t(jpp.id), ik + jpp.kd - jpp.f_pad)
- jpp.id;
const dim_t id = nstl::max(ik - jpp.f_pad, dim_t(0));
for (dim_t oh = 0; oh < jpp.oh; ++oh) {
ker(n, b_c, od, oh, id, d_t_overflow, d_b_overflow, ur_bc,
first_ithr);
}
});
} else {
if (trans_src || trans_dst) {
parallel_nd_ext(nthr, jpp.mb, jpp.nb_c,
[=](dim_t ithr, dim_t nthr, dim_t n, dim_t b_c) {
if (trans_src)
transpose_facade->execute_transpose_input(ithr, n, b_c);
for (int od = 0; od < jpp.od; ++od) {
const int ik = od * jpp.stride_d;
const int d_t_overflow = nstl::max(0, jpp.f_pad - ik);
const int d_b_overflow
= nstl::max(jpp.id, ik + jpp.kd - jpp.f_pad)
- jpp.id;
const int id = nstl::max(ik - jpp.f_pad, 0);
for (int oh = 0; oh < jpp.oh; ++oh) {
ker(n, b_c, od, oh, id, d_t_overflow, d_b_overflow, 1,
ithr);
}
}
if (trans_dst)
transpose_facade->execute_transpose_output(ithr, n, b_c);
});
} else {
parallel_nd(jpp.mb, jpp.nb_c, jpp.od,
[=](dim_t n, dim_t b_c, dim_t od) {
const int ik = od * jpp.stride_d;
const int d_t_overflow = nstl::max(0, jpp.f_pad - ik);
const int d_b_overflow
= nstl::max(jpp.id, ik + jpp.kd - jpp.f_pad) - jpp.id;
const int id = nstl::max(ik - jpp.f_pad, 0);
for (int oh = 0; oh < jpp.oh; ++oh) {
ker(n, b_c, od, oh, id, d_t_overflow, d_b_overflow, 1,
first_ithr);
}
});
}
}
}
template <cpu_isa_t isa, data_type_t d_type>
jit_uni_pooling_bwd_t<isa, d_type>::jit_uni_pooling_bwd_t(const pd_t *apd)
: primitive_t(apd), kernel_(nullptr), trans_ctx_(nullptr) {}
template <cpu_isa_t isa, data_type_t d_type>
jit_uni_pooling_bwd_t<isa, d_type>::~jit_uni_pooling_bwd_t() = default;
template <cpu_isa_t isa, data_type_t d_type>
status_t jit_uni_pooling_bwd_t<isa, d_type>::init_ncsp_trans_ctx() {
using namespace dnnl::impl;
using namespace jit_uni_pooling_utils;
const auto &jpp = pd()->jpp_;
trans_ctx_ = utils::make_unique<trans_context_t>();
const dim_t diff_src_sp = static_cast<dim_t>(jpp.id) * jpp.ih * jpp.iw;
const dim_t diff_dst_sp = static_cast<dim_t>(jpp.od) * jpp.oh * jpp.ow;
const auto res = std::div(jpp.c_without_padding, jpp.c_block);
const dim_t &nb_c = res.quot;
const dim_t &c_tail = res.rem;
const memory_desc_wrapper indices_d = pd()->workspace_md();
const bool have_indices = indices_d.data_type() != data_type::undef;
if (nb_c) {
trans_ctx_->dst_trans_
= utils::make_unique<trans_wrapper_t>(d_type, diff_dst_sp,
data_type::f32, jpp.c_block, jpp.c_block, diff_dst_sp);
trans_ctx_->src_trans_ = utils::make_unique<trans_wrapper_t>(
data_type::f32, jpp.c_block, d_type, diff_src_sp, diff_src_sp,
jpp.c_block);
if (have_indices)
trans_ctx_->ind_trans_ = utils::make_unique<trans_wrapper_t>(
indices_d.data_type(), diff_dst_sp, indices_d.data_type(),
jpp.c_block, jpp.c_block, diff_dst_sp);
}
if (c_tail) {
trans_ctx_->dst_tail_trans_
= utils::make_unique<trans_wrapper_t>(d_type, diff_dst_sp,
data_type::f32, jpp.c_block, c_tail, diff_dst_sp);
trans_ctx_->src_tail_trans_
= utils::make_unique<trans_wrapper_t>(data_type::f32,
jpp.c_block, d_type, diff_src_sp, diff_src_sp, c_tail);
if (have_indices)
trans_ctx_->ind_tail_trans_ = utils::make_unique<trans_wrapper_t>(
indices_d.data_type(), diff_dst_sp, indices_d.data_type(),
jpp.c_block, c_tail, diff_dst_sp);
}
return trans_ctx_->create_kernel();
}
template <cpu_isa_t isa, data_type_t d_type>
status_t jit_uni_pooling_bwd_t<isa, d_type>::init(engine_t *engine) {
CHECK(safe_ptr_assign(kernel_,
new jit_uni_pool_kernel_t<isa>(
pd()->jpp_, pd()->invariant_dst_md())));
if (pd()->jpp_.tag_kind == jit_memory_tag_kind_t::ncsp)
CHECK(init_ncsp_trans_ctx());
return kernel_->create_kernel();
}
template <cpu_isa_t isa, data_type_t d_type>
void jit_uni_pooling_bwd_t<isa, d_type>::execute_backward(
const data_t *diff_dst, const char *indices, data_t *diff_src,
const exec_ctx_t &ctx) const {
using namespace jit_uni_pooling_utils;
const memory_desc_wrapper diff_src_d(pd()->diff_src_md());
const memory_desc_wrapper diff_dst_d(pd()->diff_dst_md());
const memory_desc_wrapper indices_d(pd()->workspace_md());
const size_t ind_dt_size
= indices ? types::data_type_size(indices_d.data_type()) : 0;
const auto &jpp = pd()->jpp_;
auto transpose_facade = std::make_shared<
bwd_pooling_transpose_facade_t<data_t, float, d_type>>(jpp,
trans_ctx_.get(), diff_src_d, diff_dst_d, indices_d, data_type::f32,
diff_src, diff_dst, indices, ctx);
auto f32_accum = std::make_shared<bwd_f32_accum_for_bf16_t>(jpp, ctx);
auto get_first_ih = [=](int oh) {
return nstl::min(nstl::max(oh * jpp.stride_h - jpp.t_pad, 0), jpp.ih);
};
auto get_last_ih = [=](int oh) {
return nstl::min(
nstl::max(oh * jpp.stride_h - jpp.t_pad + jpp.kh, 0), jpp.ih);
};
const auto ker = [= COMPAT_THIS_CAPTURE](
int ithr, int n, int b_c, int oh, int ur_bc) {
jit_uni_pooling_args_t args;
const int ih = get_first_ih(oh);
assert(IMPLICATION(pd()->ndims() == 3, utils::everyone_is(0, ih, oh)));
assert(pd()->ndims() != 3 || utils::everyone_is(0, ih, oh));
const auto c_off = jpp.is_plain() ? b_c * jpp.c_block : b_c;
if (transpose_facade->should_transpose_src())
args.src = transpose_facade->get_src_addr(ithr, ih, jpp);
else if (jpp.needs_f32_accum_for_bf16)
args.src = f32_accum->get_addr_2d(ithr, ih);
else
args.src = &diff_src[diff_src_d.blk_off(n, c_off, ih)];
if (transpose_facade->should_transpose_dst())
args.dst = transpose_facade->get_dst_addr(ithr, oh, jpp);
else
args.dst = &diff_dst[diff_dst_d.blk_off(n, c_off, oh)];
if (indices) {
if (transpose_facade->should_transpose_dst())
args.indices
= transpose_facade->get_indices_addr(ithr, oh, jpp);
else {
const size_t ind_off = indices_d.blk_off(n, c_off, oh);
args.indices = &indices[ind_off * ind_dt_size];
}
}
const int zero_ih_start = (oh == 0) ? 0 : get_last_ih(oh - 1);
const int zero_ih_end = (oh == jpp.oh - 1) ? jpp.ih : get_last_ih(oh);
args.zero_id = 1;
args.zero_ih = zero_ih_end - zero_ih_start;
if (transpose_facade->should_transpose_src())
args.zero_ptr
= transpose_facade->get_src_addr(ithr, zero_ih_start, jpp);
else if (jpp.needs_f32_accum_for_bf16)
args.zero_ptr = f32_accum->get_addr_2d(ithr, zero_ih_start);
else
args.zero_ptr
= &diff_src[diff_src_d.blk_off(n, c_off, zero_ih_start, 0)];
const int i_t_overflow = nstl::max(0, jpp.t_pad - oh * jpp.stride_h);
const int i_b_overflow
= nstl::max(jpp.ih, oh * jpp.stride_h + jpp.kh - jpp.t_pad)
- jpp.ih;
args.kh_padding = jpp.kh - i_t_overflow - i_b_overflow;
args.kh_padding_shift = i_t_overflow * jpp.kw;
args.ker_area_h = static_cast<float>(jpp.kh
- nstl::max(0, oh * jpp.stride_h - jpp.t_pad + jpp.kh - jpp.ih)
- nstl::max(0, jpp.t_pad - oh * jpp.stride_h));
args.ur_bc = ur_bc;
args.b_c = b_c;
(*kernel_)(&args);
};
auto process_block = [=](int ithr, int n, int b_c, int ur_bc) {
if (transpose_facade->should_transpose_dst())
transpose_facade->execute_transpose_input(ithr, n, b_c);
for (int oh = 0; oh < jpp.oh; ++oh)
ker(ithr, n, b_c, oh, ur_bc);
if (transpose_facade->should_transpose_src())
transpose_facade->execute_transpose_output(ithr, n, b_c);
if (jpp.needs_f32_accum_for_bf16)
f32_accum->cvt_to_bf16_slice_2d(
ithr, (bfloat16_t *)diff_src, diff_src_d, n, b_c, ur_bc);
};
const int nthr = jpp.nthr;
parallel(nthr, [=](int ithr, int nthr) {
const auto nb2_c = utils::div_up(jpp.nb_c, jpp.ur_bc);
const std::size_t work_amount
= static_cast<std::size_t>(jpp.mb) * nb2_c;
if (static_cast<std::size_t>(ithr) >= work_amount) return;
if (transpose_facade->should_fill_input_c_tail_with_zeros())
transpose_facade->fill_input_c_tail_with_zeros(ithr, jpp);
std::size_t start {0}, end {0};
balance211(work_amount, nthr, ithr, start, end);
int n {0}, b2_c {0};
utils::nd_iterator_init(start, n, jpp.mb, b2_c, nb2_c);
for (size_t iwork = start; iwork < end; ++iwork) {
const auto b_c = b2_c * jpp.ur_bc;
const auto ur_bc = nstl::min(jpp.ur_bc, jpp.nb_c - b_c);
process_block(ithr, n, b_c, ur_bc);
utils::nd_iterator_step(n, jpp.mb, b2_c, nb2_c);
}
});
}
template <cpu_isa_t isa, data_type_t d_type>
void jit_uni_pooling_bwd_t<isa, d_type>::execute_backward_3d(
const data_t *diff_dst, const char *indices, data_t *diff_src,
const exec_ctx_t &ctx) const {
const memory_desc_wrapper diff_src_d(pd()->diff_src_md());
const memory_desc_wrapper diff_dst_d(pd()->diff_dst_md());
const memory_desc_wrapper indices_d(pd()->workspace_md());
const size_t ind_dt_size
= indices ? types::data_type_size(indices_d.data_type()) : 0;
const auto &jpp = pd()->jpp_;
using namespace jit_uni_pooling_utils;
static constexpr int first_ithr = 0;
auto transpose_facade = std::make_shared<
bwd_pooling_transpose_facade_t<data_t, float, d_type>>(jpp,
trans_ctx_.get(), diff_src_d, diff_dst_d, indices_d, data_type::f32,
diff_src, diff_dst, indices, ctx);
const auto trans_src = transpose_facade->should_transpose_src();
const auto trans_dst = transpose_facade->should_transpose_dst();
auto f32_accum = std::make_shared<bwd_f32_accum_for_bf16_t>(jpp, ctx);
const size_t input_dt_size = jpp.needs_f32_accum_for_bf16
? sizeof(bwd_f32_accum_for_bf16_t::value_type)
: jpp.dt_size;
auto get_last_ih = [=](int oh) {
return nstl::min(
nstl::max(oh * jpp.stride_h - jpp.t_pad + jpp.kh, 0), jpp.ih);
};
auto get_last_id = [=](int od) {
return nstl::min(
nstl::max(od * jpp.stride_d - jpp.f_pad + jpp.kd, 0), jpp.id);
};
auto ker = [= COMPAT_THIS_CAPTURE](int n, int b_c, int od, int oh, int id,
int d_t_overflow, int d_b_overflow, bool zero_inp,
int kd, int ur_bc, int ithr) {
jit_uni_pooling_args_t args;
const int ij = oh * jpp.stride_h;
const int i_t_overflow = nstl::max(0, jpp.t_pad - ij);
const int i_b_overflow
= nstl::max(jpp.ih, ij + jpp.kh - jpp.t_pad) - jpp.ih;
const int ih = nstl::max(ij - jpp.t_pad, 0);
const int c_off
= ((jpp.tag_kind == jit_memory_tag_kind_t::nspc) ? jpp.c_block
: 1)
* b_c;
if (trans_src)
args.src
= transpose_facade->get_src_addr_3d(ithr, id + kd, ih, jpp);
else if (jpp.needs_f32_accum_for_bf16)
args.src = f32_accum->get_addr_3d(ithr, id + kd, ih);
else
args.src = (const void *)&diff_src[diff_src_d.blk_off(
n, c_off, id + kd, ih)];
if (trans_dst)
args.dst = transpose_facade->get_dst_addr_3d(ithr, od, oh, jpp);
else
args.dst = (const void
*)&diff_dst[diff_dst_d.blk_off(n, c_off, od, oh)];
if (indices) {
if (trans_dst) {
args.indices = transpose_facade->get_indices_addr_3d(
ithr, od, oh, jpp);
} else {
const size_t ind_off = indices_d.blk_off(n, c_off, od, oh);
args.indices = (const void *)&indices[ind_off * ind_dt_size];
}
}
if (zero_inp) {
const int zero_id_start = (od == 0) ? 0 : get_last_id(od - 1);
const int zero_id_end
= (od == jpp.od - 1) ? jpp.id : get_last_id(od);
args.zero_id = zero_id_end - zero_id_start;
const int zero_ih_start = (oh == 0) ? 0 : get_last_ih(oh - 1);
const int zero_ih_end
= (oh == jpp.oh - 1) ? jpp.ih : get_last_ih(oh);
args.zero_ih = zero_ih_end - zero_ih_start;
if (trans_src)
args.zero_ptr = transpose_facade->get_src_addr_3d(
ithr, zero_id_start, zero_ih_start, jpp);
else if (jpp.needs_f32_accum_for_bf16)
args.zero_ptr = f32_accum->get_addr_3d(
ithr, zero_id_start, zero_ih_start);
else
args.zero_ptr = &diff_src[diff_src_d.blk_off(
n, c_off, zero_id_start, zero_ih_start, 0)];
} else {
args.zero_id = 0;
args.zero_ih = 0;
}
args.kd_padding = jpp.kd - d_t_overflow - d_b_overflow;
args.kh_padding = jpp.kh - i_t_overflow - i_b_overflow;
args.kh_padding_shift = i_t_overflow * jpp.kw
+ d_t_overflow * jpp.kw * jpp.kh + kd * jpp.kw * jpp.kh;
args.kd_padding_shift = (i_t_overflow + i_b_overflow) * jpp.kw;
args.ker_area_h = (float)(jpp.kh
- nstl::max(0,
oh * jpp.stride_h - jpp.t_pad + jpp.kh
- jpp.ih)
- nstl::max(0, jpp.t_pad - oh * jpp.stride_h))
* (jpp.kd
- nstl::max(0,
od * jpp.stride_d - jpp.f_pad + jpp.kd - jpp.id)
- nstl::max(0, jpp.f_pad - od * jpp.stride_d));
args.ur_bc = ur_bc;
args.b_c = b_c;
(*kernel_)(&args);
};
auto process_simple = [=](int n, int b_c, int od, int ur_bc, int ithr) {
const int ik = od * jpp.stride_d;
const int d_t_overflow = nstl::max(0, jpp.f_pad - ik);
const int d_b_overflow
= nstl::max(jpp.id, ik + jpp.kd - jpp.f_pad) - jpp.id;
const int id = nstl::max(ik - jpp.f_pad, 0);
for (int oh = 0; oh < jpp.oh; ++oh) {
ker(n, b_c, od, oh, id, d_t_overflow, d_b_overflow, true, 0, ur_bc,
ithr);
}
};
const int nthr = jpp.nthr;
if (jpp.simple_alg) {
const dim_t nb2_c = utils::div_up(jpp.nb_c, jpp.ur_bc);
if (jpp.tag_kind == jit_memory_tag_kind_t::nspc) {
if (!jpp.needs_f32_accum_for_bf16) {
parallel_nd(jpp.mb, jpp.od, nb2_c,
[=](dim_t n, dim_t od, dim_t b2_c) {
const dim_t b_c = b2_c * jpp.ur_bc;
const dim_t ur_bc
= nstl::min(dim_t(jpp.ur_bc), jpp.nb_c - b_c);
process_simple(n, b_c, od, ur_bc, first_ithr);
});
} else {
parallel_nd_ext(nthr, jpp.mb, nb2_c,
[=](dim_t ithr, dim_t nthr, dim_t n, dim_t b2_c) {
const dim_t b_c = b2_c * jpp.ur_bc;
const dim_t ur_bc
= nstl::min(dim_t(jpp.ur_bc), jpp.nb_c - b_c);
for (int od = 0; od < jpp.od; ++od) {
process_simple(n, b_c, od, ur_bc, ithr);
}
f32_accum->cvt_to_bf16_slice_3d(ithr,
(bfloat16_t *)diff_src, diff_src_d, n, b_c, ur_bc);
});
}
} else {
assert(jpp.ur_bc == 1);
if (trans_src || trans_dst || jpp.needs_f32_accum_for_bf16) {
parallel_nd_ext(nthr, jpp.mb, jpp.nb_c,
[=](dim_t ithr, dim_t nthr, dim_t n, dim_t b_c) {
if (trans_src)
transpose_facade->execute_transpose_input(ithr, n, b_c);
for (int od = 0; od < jpp.od; ++od) {
process_simple(n, b_c, od, 1, ithr);
}
if (trans_dst)
transpose_facade->execute_transpose_output(
ithr, n, b_c);
if (jpp.needs_f32_accum_for_bf16)
f32_accum->cvt_to_bf16_slice_3d(ithr,
(bfloat16_t *)diff_src, diff_src_d, n, b_c, 1);
});
} else {
parallel_nd(jpp.mb, jpp.nb_c, jpp.od,
[=](dim_t n, dim_t b_c, dim_t od) {
process_simple(n, b_c, od, 1, first_ithr);
});
}
}
} else {
const data_t zero_val = 0;
if (!jpp.needs_f32_accum_for_bf16) {
if (jpp.tag_kind == jit_memory_tag_kind_t::nspc) {
const size_t chunk_size = (size_t)jpp.ih * jpp.iw * jpp.c;
parallel_nd(jpp.mb, jpp.id, [=](dim_t n, dim_t id) {
const size_t offset
= ((size_t)n * jpp.id + id) * chunk_size;
PRAGMA_OMP_SIMD()
for (size_t idx = 0; idx < chunk_size; ++idx)
diff_src[offset + idx] = zero_val;
});
} else {
if (!trans_src) {
const size_t chunk_size
= (size_t)jpp.id * jpp.ih * jpp.iw * jpp.c_block;
parallel_nd_ext(nthr, jpp.mb, jpp.nb_c,
[=](dim_t ithr, dim_t nthr, dim_t n, dim_t b_c) {
const size_t offset
= ((size_t)n * jpp.nb_c + b_c) * chunk_size;
PRAGMA_OMP_SIMD()
for (size_t idx = 0; idx < chunk_size; ++idx)
diff_src[offset + idx] = zero_val;
});
}
}
}
const auto nb2_c = utils::div_up(jpp.nb_c, jpp.ur_bc);
if (trans_src || trans_dst || jpp.needs_f32_accum_for_bf16) {
parallel_nd_ext(nthr, jpp.mb, nb2_c,
[=](dim_t ithr, dim_t nthr, dim_t n, dim_t b2_c) {
const dim_t b_c = b2_c * jpp.ur_bc;
if (trans_dst) {
transpose_facade->execute_transpose_input(ithr, n, b_c);
size_t block_size = jpp.c_block * jpp.id * jpp.ih * jpp.iw
* input_dt_size;
const void *src = transpose_facade->get_src_addr_3d(
ithr, 0, 0, jpp);
std::memset((void *)src, zero_val, block_size);
}
if (jpp.needs_f32_accum_for_bf16) f32_accum->zero_data(ithr);
const dim_t ur_bc = nstl::min(dim_t(jpp.ur_bc), jpp.nb_c - b_c);
for (dim_t kd = 0; kd < jpp.kd; ++kd) {
for (int od = 0; od < jpp.od; ++od) {
const dim_t ik = static_cast<dim_t>(od) * jpp.stride_d;
const dim_t d_t_overflow
= nstl::max(dim_t(0), jpp.f_pad - ik);
const dim_t d_b_overflow
= nstl::max(dim_t(jpp.id),
ik + jpp.kd - jpp.f_pad)
- jpp.id;
if (kd >= jpp.kd - d_t_overflow - d_b_overflow)
continue;
const dim_t id = nstl::max(ik - jpp.f_pad, dim_t(0));
for (dim_t oh = 0; oh < jpp.oh; ++oh) {
ker(n, b_c, od, oh, id, d_t_overflow, d_b_overflow,
false, kd, ur_bc, ithr);
}
}
}
if (trans_src)
transpose_facade->execute_transpose_output(ithr, n, b_c);
if (jpp.needs_f32_accum_for_bf16)
f32_accum->cvt_to_bf16_slice_3d(ithr,
(bfloat16_t *)diff_src, diff_src_d, n, b_c, ur_bc);
});
} else {
for (dim_t kd = 0; kd < jpp.kd; ++kd) {
parallel_nd(jpp.mb, nb2_c, [=](dim_t n, dim_t b2_c) {
const dim_t b_c = b2_c * jpp.ur_bc;
const dim_t ur_bc
= nstl::min(dim_t(jpp.ur_bc), jpp.nb_c - b_c);
for (int od = 0; od < jpp.od; ++od) {
const dim_t ik = static_cast<dim_t>(od) * jpp.stride_d;
const dim_t d_t_overflow
= nstl::max(dim_t(0), jpp.f_pad - ik);
const dim_t d_b_overflow
= nstl::max(dim_t(jpp.id),
ik + jpp.kd - jpp.f_pad)
- jpp.id;
if (kd >= jpp.kd - d_t_overflow - d_b_overflow)
continue;
const dim_t id = nstl::max(ik - jpp.f_pad, dim_t(0));
for (dim_t oh = 0; oh < jpp.oh; ++oh) {
ker(n, b_c, od, oh, id, d_t_overflow, d_b_overflow,
false, kd, ur_bc, first_ithr);
}
}
});
}
}
}
}
template struct jit_uni_pooling_fwd_t<sse41, data_type::f32>;
template struct jit_uni_pooling_bwd_t<sse41, data_type::f32>;
template struct jit_uni_pooling_fwd_t<avx, data_type::f32>;
template struct jit_uni_pooling_bwd_t<avx, data_type::f32>;
template struct jit_uni_pooling_fwd_t<avx2, data_type::f32>;
template struct jit_uni_pooling_fwd_t<avx2_vnni_2, data_type::bf16>;
template struct jit_uni_pooling_fwd_t<avx2_vnni_2, data_type::f16>;
template struct jit_uni_pooling_bwd_t<avx2, data_type::f32>;
template struct jit_uni_pooling_fwd_t<avx512_core, data_type::f32>;
template struct jit_uni_pooling_bwd_t<avx512_core, data_type::f32>;
template struct jit_uni_pooling_fwd_t<avx512_core, data_type::bf16>;
template struct jit_uni_pooling_bwd_t<avx512_core, data_type::bf16>;
template struct jit_uni_pooling_fwd_t<avx512_core_fp16, data_type::f16>;
template struct jit_uni_pooling_bwd_t<avx512_core_fp16, data_type::f16>;
template struct jit_uni_pooling_fwd_t<avx512_core_fp16, data_type::f8_e5m2>;
template struct jit_uni_pooling_fwd_t<avx512_core_fp16, data_type::f8_e4m3>;
} } } }