#ifndef GPU_GENERIC_SYCL_POOLING_KERNELS_HPP
#define GPU_GENERIC_SYCL_POOLING_KERNELS_HPP
#include "common/dnnl_thread.hpp"
#include "common/dnnl_traits.hpp"
#include "common/nstl.hpp"
#include "common/primitive_exec_types.hpp"
#include "common/utils.hpp"
#include "gpu/generic/sycl/sycl_io_helper.hpp"
#include "gpu/generic/sycl/sycl_post_ops.hpp"
#include "gpu/generic/sycl/sycl_primitive_conf.hpp"
#include "xpu/sycl/memory_storage_base.hpp"
#include "xpu/sycl/types.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace generic {
namespace sycl {
using namespace nstl;
struct pooling_fwd_kernel_vec_t {
pooling_fwd_kernel_vec_t(const sycl_pooling_fwd_conf_t &conf,
::sycl::handler &cgh, const exec_ctx_t &ctx)
: conf_(conf)
, src_(CTX_IN_SYCL_KERNEL_MEMORY(DNNL_ARG_SRC))
, dst_(CTX_OUT_SYCL_KERNEL_MEMORY(DNNL_ARG_DST))
, ws_(CTX_OUT_SYCL_KERNEL_MEMORY(DNNL_ARG_WORKSPACE))
, po_args_(cgh, ctx, conf_.post_ops) {}
void operator()(::sycl::nd_item<1> item) const {
memory_tensor_t src_mem(src_, conf_.src_md);
memory_tensor_t dst_mem(dst_, conf_.dst_md);
size_t ithr = item.get_global_id(0);
const bool is_max_pool = conf_.alg == alg_kind::pooling_max;
float base_res = is_max_pool ? data_conv() : 0.f;
dim_t MB = conf_.MB;
dim_t OC = conf_.OC;
dim_t OD = conf_.OD;
dim_t OH = conf_.OH;
dim_t OW = conf_.OW;
const dim_t work_amount = MB * OC * OD * OH * OW;
if (work_amount == 0) return;
dims_t dst_dims;
for (int i = 0; i < xpu::sycl::md_t::max_dims; i++) {
if (i < dst_mem.md().ndims()) {
dst_dims[i] = dst_mem.md().dims()[i];
} else {
dst_dims[i] = 1;
}
}
dim_t start {0}, end {0};
balance211(work_amount, conf_.n_thr, ithr, start, end);
dim_t mb {0}, oc {0}, od {0}, oh {0}, ow {0};
utils::nd_iterator_init(start, mb, MB, oc, OC, od, OD, oh, OH, ow, OW);
for (dim_t iwork = start; iwork < end; ++iwork) {
auto data_p_off = get_offset(dst_md(), mb, oc, od, oh, ow);
auto data_l_off = (((mb * OC + oc) * OD + od) * OH + oh) * OW + ow;
float res = base_res;
if (is_max_pool) {
ker_max(src_mem, res, mb, oc, od, oh, ow);
} else {
ker_avg(src_mem, res, mb, oc, od, oh, ow);
}
dims_t off;
utils::l_dims_by_l_offset(
off, data_l_off, dst_dims, dst_md().ndims());
res = conf_.post_ops.apply(res, po_args_, off);
dst_mem.store(res, data_p_off);
utils::nd_iterator_step(mb, MB, oc, OC, od, OD, oh, OH, ow, OW);
}
}
private:
const xpu::sycl::md_t &src_md() const { return conf_.src_md; }
const xpu::sycl::md_t &dst_md() const { return conf_.dst_md; }
const xpu::sycl::md_t &ws_md() const { return conf_.ws_md; }
void *gen_ptr(xpu::sycl::in_memory_arg_t gen_) const {
return gen_.get_pointer();
}
void *ws_ptr() const { return ws_.get_pointer(); }
static dim_t get_offset(const xpu::sycl::md_t &mdw, dim_t n, dim_t c,
dim_t d, dim_t h, dim_t w) {
switch (mdw.ndims()) {
case 3: return mdw.off(n, c, w);
case 4: return mdw.off(n, c, h, w);
case 5: return mdw.off(n, c, d, h, w);
default: return 0;
}
return 0;
}
float data_conv() const {
switch (dst_md().data_type()) {
case data_type::bf16:
return (float)
std::numeric_limits<xpu::sycl::bfloat16_t>::lowest();
case data_type::s8:
return (float)numeric_limits<typename xpu::sycl::prec_traits_t<
data_type::s8>::type>::lowest();
case data_type::f16:
return (float)
std::numeric_limits<typename xpu::sycl::prec_traits_t<
data_type::f16>::type>::lowest();
case data_type::s32:
return (float)numeric_limits<typename xpu::sycl::prec_traits_t<
data_type::s32>::type>::lowest();
case data_type::u8:
return (float)numeric_limits<typename xpu::sycl::prec_traits_t<
data_type::u8>::type>::lowest();
default:
return (float)numeric_limits<typename xpu::sycl::prec_traits_t<
data_type::f32>::type>::lowest();
}
}
void set_ws(dim_t mb, dim_t oc, dim_t od, dim_t oh, dim_t ow,
dim_t value) const {
if (ws_ptr()) {
memory_tensor_t ws_mem(ws_, conf_.ws_md);
const auto off = get_offset(ws_md(), mb, oc, od, oh, ow);
ws_mem.store(value, off);
}
}
void ker_max(const in_memory_tensor_t &src_mem, float &d, dim_t mb,
dim_t oc, dim_t od, dim_t oh, dim_t ow) const {
set_ws(mb, oc, od, oh, ow, 0);
for (dim_t kd = 0; kd < conf_.KD; ++kd) {
const dim_t id = od * conf_.SD - conf_.padF + kd * (conf_.DD + 1);
if (id < 0 || id >= conf_.ID) continue;
for (dim_t kh = 0; kh < conf_.KH; ++kh) {
const dim_t ih
= oh * conf_.SH - conf_.padT + kh * (conf_.DH + 1);
if (ih < 0 || ih >= conf_.IH) continue;
for (dim_t kw = 0; kw < conf_.KW; ++kw) {
const dim_t iw
= ow * conf_.SW - conf_.padL + kw * (conf_.DW + 1);
if (iw < 0 || iw >= conf_.IW) continue;
const auto off = get_offset(src_md(), mb, oc, id, ih, iw);
auto s = src_mem.load(off);
if (s > d) {
d = s;
set_ws(mb, oc, od, oh, ow,
(kd * conf_.KH + kh) * conf_.KW + kw);
}
}
}
}
}
void ker_avg(const in_memory_tensor_t &src_mem, float &d, dim_t mb,
dim_t oc, dim_t od, dim_t oh, dim_t ow) const {
for (dim_t kd = 0; kd < conf_.KD; ++kd) {
const dim_t id = od * conf_.SD - conf_.padF + kd * (conf_.DD + 1);
if (id < 0 || id >= conf_.ID) continue;
for (dim_t kh = 0; kh < conf_.KH; ++kh) {
const dim_t ih
= oh * conf_.SH - conf_.padT + kh * (conf_.DH + 1);
if (ih < 0 || ih >= conf_.IH) continue;
for (dim_t kw = 0; kw < conf_.KW; ++kw) {
const dim_t iw
= ow * conf_.SW - conf_.padL + kw * (conf_.DW + 1);
if (iw < 0 || iw >= conf_.IW) continue;
const auto off = get_offset(src_md(), mb, oc, id, ih, iw);
float s = src_mem.load(off);
d += s;
}
}
}
int num_summands;
if (conf_.alg == alg_kind::pooling_avg_include_padding)
num_summands = conf_.KW * conf_.KH * conf_.KD;
else {
auto id_start = od * conf_.SD - conf_.padF;
auto ih_start = oh * conf_.SH - conf_.padT;
auto iw_start = ow * conf_.SW - conf_.padL;
auto id_end = od * conf_.SD - conf_.padF + (conf_.KD - 1) * conf_.DD
+ conf_.KD;
auto ih_end = oh * conf_.SH - conf_.padT + (conf_.KH - 1) * conf_.DH
+ conf_.KH;
auto iw_end = ow * conf_.SW - conf_.padL + (conf_.KW - 1) * conf_.DW
+ conf_.KW;
auto id_start_excluded = id_start < 0
? (0 - id_start - 1) / (conf_.DD + 1) + 1
: 0;
auto ih_start_excluded = ih_start < 0
? (0 - ih_start - 1) / (conf_.DH + 1) + 1
: 0;
auto iw_start_excluded = iw_start < 0
? (0 - iw_start - 1) / (conf_.DW + 1) + 1
: 0;
auto id_end_excluded = id_end > conf_.ID
? (id_end - conf_.ID - 1) / (conf_.DD + 1) + 1
: 0;
auto ih_end_excluded = ih_end > conf_.IH
? (ih_end - conf_.IH - 1) / (conf_.DH + 1) + 1
: 0;
auto iw_end_excluded = iw_end > conf_.IW
? (iw_end - conf_.IW - 1) / (conf_.DW + 1) + 1
: 0;
num_summands = (conf_.KD - id_start_excluded - id_end_excluded)
* (conf_.KH - ih_start_excluded - ih_end_excluded)
* (conf_.KW - iw_start_excluded - iw_end_excluded);
}
d /= num_summands;
}
sycl_pooling_fwd_conf_t conf_;
xpu::sycl::in_memory_arg_t src_;
xpu::sycl::out_memory_arg_t dst_;
xpu::sycl::out_memory_arg_t ws_;
post_op_input_args po_args_;
};
struct pooling_bwd_kernel_vec_t {
pooling_bwd_kernel_vec_t(const sycl_pooling_bwd_conf_t &conf,
::sycl::handler &cgh, const exec_ctx_t &ctx)
: conf_(conf)
, diff_dst_(CTX_IN_SYCL_KERNEL_MEMORY(DNNL_ARG_DIFF_DST))
, diff_src_(CTX_OUT_SYCL_KERNEL_MEMORY(DNNL_ARG_DIFF_SRC))
, ws_(CTX_IN_SYCL_KERNEL_MEMORY(DNNL_ARG_WORKSPACE)) {}
void operator()(::sycl::nd_item<1> item) const {
memory_tensor_t diff_src_mem(diff_src_, conf_.diff_src_md);
memory_tensor_t diff_dst_mem(diff_dst_, conf_.diff_dst_md);
size_t ithr = item.get_global_id(0);
int denom = 1;
const bool is_max_pool = conf_.alg == alg_kind::pooling_max;
const bool is_avg_incl_pad
= conf_.alg == alg_kind::pooling_avg_include_padding;
const bool is_avg_excl_pad
= conf_.alg == alg_kind::pooling_avg_exclude_padding;
if (is_avg_incl_pad) denom = conf_.KW * conf_.KH * conf_.KD;
const dim_t work_amount
= conf_.MB * conf_.OC * conf_.ID * conf_.IH * conf_.IW;
if (work_amount == 0) return;
dim_t start {0}, end {0};
balance211(work_amount, conf_.n_thr, ithr, start, end);
dim_t mb {0}, oc {0}, id {0}, ih {0}, iw {0};
utils::nd_iterator_init(start, mb, conf_.MB, oc, conf_.OC, id, conf_.ID,
ih, conf_.IH, iw, conf_.IW);
for (dim_t iwork = start; iwork < end; ++iwork) {
float s = 0;
for (dim_t kd = 0; kd < conf_.KD; ++kd) {
dim_t _od = id + conf_.padF - kd * (conf_.DD + 1);
if (_od % conf_.SD != 0) continue;
dim_t od = _od / conf_.SD;
if (od < 0 || od >= conf_.OD) continue;
for (dim_t kh = 0; kh < conf_.KH; ++kh) {
dim_t _oh = ih + conf_.padT - kh * (conf_.DH + 1);
if (_oh % conf_.SH != 0) continue;
dim_t oh = _oh / conf_.SH;
if (oh < 0 || oh >= conf_.OH) continue;
for (dim_t kw = 0; kw < conf_.KW; ++kw) {
dim_t _ow = iw + conf_.padL - kw * (conf_.DW + 1);
if (_ow % conf_.SW != 0) continue;
dim_t ow = _ow / conf_.SW;
if (ow < 0 || ow >= conf_.OW) continue;
const auto dst_off
= get_offset(diff_dst_md(), mb, oc, od, oh, ow);
if (is_max_pool) {
memory_tensor_t ws_mem(ws_, conf_.ws_md);
const int index = ws_mem.load(dst_off);
const dim_t hw = index % (conf_.KW * conf_.KH);
const dim_t w_kd = index / (conf_.KW * conf_.KH);
const dim_t w_kw = hw % conf_.KW;
const dim_t w_kh = hw / conf_.KW;
if (w_kd != kd || w_kh != kh || w_kw != kw)
continue;
}
if (is_avg_excl_pad) {
ker_avg_excl_pad(od, oh, ow, diff_dst_mem, dst_off,
denom, s);
}
if (is_max_pool || is_avg_incl_pad)
s = s + diff_dst_mem.load(dst_off);
}
}
}
const auto diff_src_offset
= get_offset(diff_src_md(), mb, oc, id, ih, iw);
if (is_max_pool || is_avg_incl_pad) s = s / denom;
diff_src_mem.store(s, diff_src_offset);
utils::nd_iterator_step(mb, conf_.MB, oc, conf_.OC, id, conf_.ID,
ih, conf_.IH, iw, conf_.IW);
}
}
private:
const xpu::sycl::md_t &diff_src_md() const { return conf_.diff_src_md; }
const xpu::sycl::md_t &diff_dst_md() const { return conf_.diff_dst_md; }
const xpu::sycl::md_t &ws_md() const { return conf_.ws_md; }
static dim_t get_offset(const xpu::sycl::md_t &mdw, dim_t n, dim_t c,
dim_t d, dim_t h, dim_t w) {
switch (mdw.ndims()) {
case 3: return mdw.off(n, c, w);
case 4: return mdw.off(n, c, h, w);
case 5: return mdw.off(n, c, d, h, w);
default: return 0;
}
return 0;
}
void ker_avg_excl_pad(dim_t od, dim_t oh, dim_t ow,
const in_memory_tensor_t &diff_dst_mem, dim_t dst_off, int &denom,
float &s) const {
const auto id_start = od * conf_.SD - conf_.padF;
const auto ih_start = oh * conf_.SH - conf_.padT;
const auto iw_start = ow * conf_.SW - conf_.padL;
const auto id_end = od * conf_.SD - conf_.padF
+ (conf_.KD - 1) * conf_.DD + conf_.KD;
const auto ih_end = oh * conf_.SH - conf_.padT
+ (conf_.KH - 1) * conf_.DH + conf_.KH;
const auto iw_end = ow * conf_.SW - conf_.padL
+ (conf_.KW - 1) * conf_.DW + conf_.KW;
const auto id_start_excluded
= id_start < 0 ? (0 - id_start - 1) / (conf_.DD + 1) + 1 : 0;
const auto ih_start_excluded
= ih_start < 0 ? (0 - ih_start - 1) / (conf_.DH + 1) + 1 : 0;
const auto iw_start_excluded
= iw_start < 0 ? (0 - iw_start - 1) / (conf_.DW + 1) + 1 : 0;
const auto id_end_excluded = id_end > conf_.ID
? (id_end - conf_.ID - 1) / (conf_.DD + 1) + 1
: 0;
const auto ih_end_excluded = ih_end > conf_.IH
? (ih_end - conf_.IH - 1) / (conf_.DH + 1) + 1
: 0;
const auto iw_end_excluded = iw_end > conf_.IW
? (iw_end - conf_.IW - 1) / (conf_.DW + 1) + 1
: 0;
denom = (conf_.KD - id_start_excluded - id_end_excluded)
* (conf_.KH - ih_start_excluded - ih_end_excluded)
* (conf_.KW - iw_start_excluded - iw_end_excluded);
s = s + (diff_dst_mem.load(dst_off) / denom);
}
sycl_pooling_bwd_conf_t conf_;
xpu::sycl::in_memory_arg_t diff_dst_;
xpu::sycl::out_memory_arg_t diff_src_;
xpu::sycl::in_memory_arg_t ws_;
};
} } } } }
#endif