#ifndef GPU_GENERIC_SYCL_LRN_KERNELS_HPP
#define GPU_GENERIC_SYCL_LRN_KERNELS_HPP
#include "common/c_types_map.hpp"
#include "common/primitive_exec_types.hpp"
#include "gpu/generic/sycl/sycl_io_helper.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 {
struct lrn_fwd_kernel_vec_t {
lrn_fwd_kernel_vec_t(const sycl_lrn_conf_t &conf, ::sycl::handler &cgh,
const exec_ctx_t &ctx, const format_tag_t &tag)
: conf_(conf)
, src_(CTX_IN_SYCL_KERNEL_MEMORY(DNNL_ARG_SRC))
, dst_(CTX_OUT_SYCL_KERNEL_MEMORY(DNNL_ARG_DST))
, tag_(tag) {}
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);
auto data_off = [&](dim_t &mb, dim_t &c, dim_t &d, dim_t &h, dim_t &w) {
switch (tag_) {
case format_tag::nchw:
return mb * conf_.stride_mb + c * conf_.h * conf_.w
+ h * conf_.w + w;
case format_tag::nhwc:
return mb * conf_.stride_mb + h * conf_.w * conf_.c
+ w * conf_.c + c;
default:
if (conf_.ndims >= 5) return src_md().off(mb, c, d, h, w);
if (conf_.ndims >= 4) return src_md().off(mb, c, h, w);
if (conf_.ndims >= 3) return src_md().off(mb, c, w);
return src_md().off(mb, c);
}
};
auto ker = [&](dim_t mb, dim_t oc, dim_t od, dim_t oh, dim_t ow) {
float sum = 0;
const dim_t half_size = (conf_.size - 1) / 2;
if (conf_.alg_kind == alg_kind::lrn_across_channels) {
const dim_t c_st = nstl::max(oc - half_size + 0, (dim_t)0);
const dim_t c_en = nstl::min(oc + half_size + 1, conf_.c);
for (dim_t c = c_st; c < c_en; ++c) {
const auto s_off = data_off(mb, c, od, oh, ow);
const auto s = src_mem.load(s_off);
sum += s * s;
}
} else {
dim_t d_st = nstl::max(od - half_size + 0, (dim_t)0);
dim_t d_en = nstl::min(od + half_size + 1, conf_.d);
dim_t h_st = nstl::max(oh - half_size + 0, (dim_t)0);
dim_t h_en = nstl::min(oh + half_size + 1, conf_.h);
dim_t w_st = nstl::max(ow - half_size + 0, (dim_t)0);
dim_t w_en = nstl::min(ow + half_size + 1, conf_.w);
for_(dim_t d = d_st; d < d_en; ++d)
for_(dim_t h = h_st; h < h_en; ++h)
for (dim_t w = w_st; w < w_en; ++w) {
const auto s_off = data_off(mb, oc, d, h, w);
const auto s = src_mem.load(s_off);
sum += s * s;
}
}
sum = conf_.k + conf_.alpha * sum / conf_.compute_n_summands;
const auto s_off = data_off(mb, oc, od, oh, ow);
const auto s = src_mem.load(s_off);
return (s * fast_negative_powf(sum, conf_.beta));
};
auto operation
= [&](dim_t &mb, dim_t &c, dim_t &d, dim_t &h, dim_t &w) {
if (format_tag::nhwc == tag_) {
const dim_t off = mb * conf_.stride_mb + h * conf_.w * conf_.c
+ w * conf_.c + c;
auto val = ker(mb, c, 0, h, w);
dst_mem.store(val, off);
} else {
const dim_t off = data_off(mb, c, d, h, w);
auto val = ker(mb, c, d, h, w);
dst_mem.store(val, off);
}
};
for (int idx = item.get_global_id(0); idx < conf_.wk_size;
idx += item.get_global_range(0)) {
dim_t N = conf_.mb;
dim_t C = conf_.c;
dim_t D = conf_.d;
dim_t H = conf_.h;
dim_t W = conf_.w;
dim_t n = (idx / (C * D * H * W)) % N;
dim_t c = (idx / (D * H * W)) % C;
dim_t d = (idx / (H * W)) % D;
dim_t h = (idx / (W)) % H;
dim_t w = (idx / (1)) % W;
operation(n, c, d, h, w);
}
}
inline float fast_negative_powf(float omega, float beta) const {
float Y;
if (beta == 0.75f) {
Y = ::sycl::sqrt(1.0f / (::sycl::sqrt(omega) * omega));
} else {
Y = 1.0f / ::sycl::pow(omega, beta);
}
return Y;
}
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; }
sycl_lrn_conf_t conf_;
xpu::sycl::in_memory_arg_t src_;
xpu::sycl::out_memory_arg_t dst_;
format_tag_t tag_;
};
struct lrn_bwd_kernel_vec_t {
lrn_bwd_kernel_vec_t(const sycl_lrn_conf_t &conf, ::sycl::handler &cgh,
const exec_ctx_t &ctx, const format_tag_t &tag)
: conf_(conf)
, src_(CTX_IN_SYCL_KERNEL_MEMORY(DNNL_ARG_SRC))
, diff_dst_(CTX_IN_SYCL_KERNEL_MEMORY(DNNL_ARG_DIFF_DST))
, diff_src_(CTX_OUT_SYCL_KERNEL_MEMORY(DNNL_ARG_DIFF_SRC))
, tag_(tag) {}
void operator()(::sycl::nd_item<1> item) const {
memory_tensor_t src_mem(src_, conf_.src_md);
memory_tensor_t diff_src_mem(diff_src_, conf_.diff_src_md);
memory_tensor_t diff_dst_mem(diff_dst_, conf_.diff_dst_md);
auto data_off = [&](dim_t &mb, dim_t &c, dim_t &d, dim_t &h,
dim_t &w) -> dim_t {
switch (tag_) {
case format_tag::nchw:
return mb * conf_.stride_mb + c * conf_.h * conf_.w
+ h * conf_.w + w;
case format_tag::nhwc:
return mb * conf_.stride_mb + h * conf_.w * conf_.c
+ w * conf_.c + c;
default:
if (conf_.ndims >= 5) return src_md().off(mb, c, d, h, w);
if (conf_.ndims >= 4) return src_md().off(mb, c, h, w);
if (conf_.ndims >= 3) return src_md().off(mb, c, w);
return src_md().off(mb, c);
}
};
auto get_omega
= [&](dim_t &mb, dim_t &oc, dim_t &od, dim_t &oh, dim_t &ow) {
auto sum = 0;
const dim_t half_size = (conf_.size - 1) / 2;
if (conf_.alg_kind == alg_kind::lrn_across_channels) {
const dim_t c_st = nstl::max(oc - half_size + 0, (dim_t)0);
const dim_t c_en = nstl::min(oc + half_size + 1, conf_.c);
for (dim_t c = c_st; c < c_en; ++c) {
const auto s_off = data_off(mb, c, od, oh, ow);
const auto s = src_mem.load(s_off);
sum += s * s;
}
} else {
dim_t d_st = nstl::max(od - half_size + 0, (dim_t)0);
dim_t d_en = nstl::min(od + half_size + 1, conf_.d);
dim_t h_st = nstl::max(oh - half_size + 0, (dim_t)0);
dim_t h_en = nstl::min(oh + half_size + 1, conf_.h);
dim_t w_st = nstl::max(ow - half_size + 0, (dim_t)0);
dim_t w_en = nstl::min(ow + half_size + 1, conf_.w);
for_(dim_t d = d_st; d < d_en; ++d)
for_(dim_t h = h_st; h < h_en; ++h)
for (dim_t w = w_st; w < w_en; ++w) {
const auto s_off = data_off(mb, oc, d, h, w);
const auto s = src_mem.load(s_off);
sum += s * s;
}
}
return (conf_.k + conf_.alpha * sum / conf_.compute_n_summands);
};
auto ker = [&](dim_t mb, dim_t oc, dim_t od, dim_t oh, dim_t ow) {
float A = 0, B = 0;
const dim_t half_size = (conf_.size - 1) / 2;
if (conf_.alg_kind == alg_kind::lrn_across_channels) {
const dim_t c_st = nstl::max(oc - half_size + 0, (dim_t)0);
const dim_t c_en = nstl::min(oc + half_size + 1, conf_.c);
for (dim_t c = c_st; c < c_en; c++) {
const auto off = data_off(mb, c, od, oh, ow);
const auto omega = get_omega(mb, c, od, oh, ow);
const auto omega_in_beta
= fast_negative_powf(omega, conf_.beta);
const auto dst_val = diff_dst_mem.load(off);
const auto tmp = omega_in_beta * dst_val;
if (c == oc) A = tmp;
const auto src_val = src_mem.load(off);
B += (src_val * tmp / omega);
}
} else {
dim_t d_st = nstl::max(od - half_size + 0, (dim_t)0);
dim_t d_en = nstl::min(od + half_size + 1, conf_.d);
dim_t h_st = nstl::max(oh - half_size + 0, (dim_t)0);
dim_t h_en = nstl::min(oh + half_size + 1, conf_.h);
dim_t w_st = nstl::max(ow - half_size + 0, (dim_t)0);
dim_t w_en = nstl::min(ow + half_size + 1, conf_.w);
for_(dim_t d = d_st; d < d_en; ++d)
for_(dim_t h = h_st; h < h_en; ++h)
for (dim_t w = w_st; w < w_en; ++w) {
const auto off = data_off(mb, oc, d, h, w);
const auto omega = get_omega(mb, oc, d, h, w);
const auto omega_in_beta
= fast_negative_powf(omega, conf_.beta);
const auto dst_val = diff_dst_mem.load(off);
const auto tmp = omega_in_beta * dst_val;
if (d == od && h == oh && w == ow) A = tmp;
const auto src_val = src_mem.load(off);
B += (src_val * tmp / omega);
}
}
const auto off = data_off(mb, oc, od, oh, ow);
const auto src_val = src_mem.load(off);
B *= (2.0f * conf_.alpha * conf_.beta * src_val
/ conf_.compute_n_summands);
return (A - B);
};
auto operation
= [&](dim_t &mb, dim_t &c, dim_t &d, dim_t &h, dim_t &w) {
if (format_tag::nhwc == tag_) {
const dim_t off = mb * conf_.stride_mb + h * conf_.w * conf_.c
+ w * conf_.c + c;
auto val = ker(mb, c, 0, h, w);
diff_src_mem.store(val, off);
} else {
const dim_t off = data_off(mb, c, d, h, w);
auto val = ker(mb, c, d, h, w);
diff_src_mem.store(val, off);
}
};
for (int idx = item.get_global_id(0); idx < conf_.wk_size;
idx += item.get_global_range(0)) {
dim_t N = conf_.mb;
dim_t C = conf_.c;
dim_t D = conf_.d;
dim_t H = conf_.h;
dim_t W = conf_.w;
dim_t n = (idx / (C * D * H * W)) % N;
dim_t c = (idx / (D * H * W)) % C;
dim_t d = (idx / (H * W)) % D;
dim_t h = (idx / (W)) % H;
dim_t w = (idx / (1)) % W;
operation(n, c, d, h, w);
}
}
inline float fast_negative_powf(float omega, float beta) const {
float Y;
if (beta == 0.75f) {
Y = ::sycl::sqrt(1.0f / (::sycl::sqrt(omega) * omega));
} else {
Y = 1.0f / ::sycl::pow(omega, beta);
}
return Y;
}
private:
const xpu::sycl::md_t &src_md() const { return conf_.src_md; }
const xpu::sycl::md_t &diff_dst_md() const { return conf_.diff_dst_md; }
const xpu::sycl::md_t &diff_src_md() const { return conf_.diff_src_md; }
sycl_lrn_conf_t conf_;
xpu::sycl::in_memory_arg_t src_;
xpu::sycl::in_memory_arg_t diff_dst_;
xpu::sycl::out_memory_arg_t diff_src_;
format_tag_t tag_;
};
} } } } }
#endif