#ifndef GPU_INTEL_SOFTMAX_XE_HPP
#define GPU_INTEL_SOFTMAX_XE_HPP
#include "common/c_types_map.hpp"
#include "common/primitive.hpp"
#include "gpu/gpu_softmax_pd.hpp"
#include "gpu/intel/compute/utils.hpp"
#include "gpu/intel/primitive.hpp"
#include "gpu/intel/primitive_conf.hpp"
#include "gpu/intel/softmax/config.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace softmax {
struct xe_fwd_t : public primitive_t {
using primitive_t::primitive_t;
struct pd_t : public fwd_pd_t {
using fwd_pd_t::fwd_pd_t;
DECLARE_COMMON_PD_T("ocl:xe", xe_fwd_t);
status_t init(impl::engine_t *engine) {
using namespace dnnl::impl::format_tag;
auto *intel_engine = utils::downcast<intel::engine_t *>(engine);
auto arch = intel_engine->device_info()->gpu_arch();
const memory_desc_wrapper src_d(src_md());
const memory_desc_wrapper dst_d(dst_md());
const auto src_dt = src_d.data_type();
const auto dst_dt = dst_d.data_type();
using namespace data_type;
using skip_mask_t = primitive_attr_t::skip_mask_t;
is_nhwc = (src_d.matches_one_of_tag(nwc, nhwc, ndhwc)
!= format_tag::undef);
is_blocked = (src_d.matches_one_of_tag(nCw16c, nChw16c, nCdhw16c)
!= format_tag::undef);
VDISPATCH_SOFTMAX(is_fwd(), VERBOSE_BAD_PROPKIND);
VDISPATCH_SOFTMAX(
IMPLICATION(is_blocked, axis_size() % buffer_size == 0),
VERBOSE_BAD_AXIS);
VDISPATCH_SOFTMAX(memory_desc_ndims_ok(src_md(), dst_md()),
VERBOSE_INCONSISTENT_NDIMS_WITH_VALS, "src", "dst",
src_md()->ndims, dst_md()->ndims);
VDISPATCH_SOFTMAX(axis() == src_d.ndims() - 1, VERBOSE_BAD_AXIS);
VDISPATCH_SOFTMAX((src_d.is_plain() || is_blocked || is_nhwc),
VERBOSE_UNSUPPORTED_TAG);
VDISPATCH_SOFTMAX(
utils::one_of(src_dt, f64, f32, f16, bf16, u8, s8),
VERBOSE_UNSUPPORTED_DT);
VDISPATCH_SOFTMAX(
utils::one_of(dst_dt, f64, f32, f16, bf16, u8, s8),
VERBOSE_UNSUPPORTED_DT);
VDISPATCH_SOFTMAX(IMPLICATION(utils::one_of(f16, src_dt, dst_dt),
intel_engine->mayiuse(
compute::device_ext_t::khr_fp16)),
VERBOSE_UNSUPPORTED_DT_CFG);
VDISPATCH_SOFTMAX(IMPLICATION(utils::one_of(data_type::f64,
dst_md()->data_type,
src_md()->data_type),
intel_engine->mayiuse(
compute::device_ext_t::khr_fp64)),
VERBOSE_UNSUPPORTED_DT_CFG);
VDISPATCH_SOFTMAX(attr()->has_default_values(skip_mask_t::scales),
VERBOSE_UNSUPPORTED_ATTR);
VDISPATCH_SOFTMAX(attr_scales_ok(), VERBOSE_UNSUPPORTED_ATTR);
VDISPATCH_SOFTMAX_SC(
set_default_formats(), VERBOSE_UNSUPPORTED_TAG);
VDISPATCH_SOFTMAX(intel_engine->mayiuse_sub_group(subgroup_size),
VERBOSE_UNSUPPORTED_DEVICE_FEATURE, "subgroup_size");
VDISPATCH_SOFTMAX(
!(is_blocked && src_md()->dims[1] % subgroup_size != 0),
VERBOSE_UNSUPPORTED_MEM_STRIDE);
int max_lws = (arch >= compute::gpu_arch_t::xe_hp) ? 1024 : 512;
if (is_nhwc) {
dim_t axis_padded = utils::rnd_up(axis_size(), subgroup_size);
group_size = subgroup_size
* utils::div_up(axis_padded, buffer_size);
if (group_size > (size_t)max_lws) {
int old_group_size = (int)group_size;
group_size = max_lws;
int lws_ratio = old_group_size / (int)group_size;
int rem_threads = old_group_size % max_lws;
int rem_reads = rem_threads * thread_buffer;
subgroups_repeated = rem_threads / subgroup_size;
thread_reads = thread_buffer * lws_ratio;
buffer_size *= lws_ratio;
repeated_subgrp_buffer = thread_buffer = thread_reads;
if (lws_ratio >= 2 && subgroups_repeated > 0) {
subgroups_repeated = rem_reads / buffer_size;
if (rem_reads % buffer_size != 0) {
int tail_reads = rem_reads / subgroup_size;
repeated_subgrp_buffer = tail_reads % thread_buffer;
subgroups_repeated++;
}
}
}
} else {
bool avoid_large_spatial
= (src_md()->dims[0] * src_md()->dims[1] > 128)
&& (axis() > 1);
if (!is_blocked
&& (axis_size() % buffer_size != 0
|| avoid_large_spatial)) {
group_size = subgroup_size;
} else {
group_size = subgroup_size
* utils::div_up(axis_size(), buffer_size);
}
VDISPATCH_SOFTMAX(group_size <= (size_t)max_lws,
"unsupported group_size, %d <= %d", (int)group_size,
max_lws);
}
lws[0] = group_size;
gws[0] = utils::array_product(&src_md()->dims[0], ndims() - 1)
* group_size;
if ((axis_size() * types::data_type_size(src_dt))
% byte_alignment_read
== 0)
is_read_aligned = true;
if ((axis_size() * types::data_type_size(dst_dt))
% byte_alignment_write
== 0)
is_write_aligned = true;
return status::success;
}
bool is_nhwc = false;
bool is_blocked = false;
bool is_write_aligned = false;
bool is_read_aligned = false;
compute::range_t gws = compute::range_t::empty(1);
compute::range_t lws = compute::range_t::empty(1);
size_t group_size = 0;
const int subgroup_size = 16;
const int byte_alignment_read = 4;
const int byte_alignment_write = 16;
int thread_reads = 0;
int thread_buffer = 8;
int subgroups_repeated = 0;
int repeated_subgrp_buffer = 8;
int buffer_size = 128;
};
status_t init(impl::engine_t *engine) override {
if (pd()->has_zero_dim_memory()) return status::success;
compute::kernel_ctx_t kernel_ctx;
kernel_ctx.define_int("SOFTMAX_AXIS_IDX", pd()->axis());
kernel_ctx.define_int("SOFTMAX_AXIS_SIZE", pd()->axis_size());
kernel_ctx.define_int("SOFTMAX_BUF", pd()->buffer_size);
kernel_ctx.define_int("GROUP_SIZE", pd()->group_size);
kernel_ctx.define_int("SUB_GROUP_SIZE", pd()->subgroup_size);
kernel_ctx.define_int("THREAD_BUF_SIZE", pd()->thread_buffer);
kernel_ctx.define_int("SUBGROUPS_REPEATED", pd()->subgroups_repeated);
kernel_ctx.define_int(
"REPEAT_SUBGRP_BUF_SIZE", pd()->repeated_subgrp_buffer);
kernel_ctx.define_int(
"CHANNELS_PADDED", pd()->src_md()->padded_dims[1]);
kernel_ctx.define_int("CHANNELS",
pd()->is_blocked ? pd()->subgroup_size
: pd()->src_md(0)->padded_dims[1]);
kernel_ctx.define_int("IS_NHWC", pd()->is_nhwc);
kernel_ctx.define_int("IS_BLOCKED", pd()->is_blocked);
kernel_ctx.define_int("IS_READ_ALIGNED", pd()->is_read_aligned);
kernel_ctx.define_int("IS_WRITE_ALIGNED", pd()->is_write_aligned);
kernel_ctx.define_int("IS_FWD", 1);
kernel_ctx.add_option("-cl-std=CL2.0");
kernel_ctx.define_int("SOFTMAX_INF_AS_ZERO",
pd()->alg_kind() == alg_kind::softmax_accurate_inf_as_zero);
kernel_ctx.define_int("LOGSOFTMAX", pd()->is_logsoftmax());
kernel_ctx.define_int("WITH_SRC_SCALES",
!pd()->attr()->scales_.has_default_values(DNNL_ARG_SRC));
kernel_ctx.define_int("WITH_DST_SCALES",
!pd()->attr()->scales_.has_default_values(DNNL_ARG_DST));
const memory_desc_wrapper dst_mdw(pd()->dst_md());
const memory_desc_wrapper src_mdw(pd()->src_md());
const auto dst_md_info = memory_desc_info_t::create(dst_mdw);
const auto src_md_info = memory_desc_info_t::create(src_mdw);
def_memory_desc_info(kernel_ctx, dst_md_info, "DST");
def_memory_desc_info(kernel_ctx, src_md_info, "SRC");
kernel_ctx.set_data_type(dst_mdw.data_type());
kernel_ctx.require_stateless_addressing(pd()->has_large_buffers());
set_offsets(kernel_ctx, pd()->dst_md(), "DATA");
CHECK(create_kernel(engine, &kernel_, "xe_softmax_fwd", kernel_ctx));
if (!kernel_) return status::runtime_error;
return status::success;
}
status_t execute(const exec_ctx_t &ctx) const override {
return execute_generic(ctx);
}
protected:
status_t execute_generic(const exec_ctx_t &ctx) const;
const pd_t *pd() const { return (const pd_t *)primitive_t::pd().get(); }
compute::kernel_t kernel_;
};
struct xe_bwd_t : public primitive_t {
using primitive_t::primitive_t;
struct pd_t : public bwd_pd_t {
using bwd_pd_t::bwd_pd_t;
DECLARE_COMMON_PD_T("ocl:xe", xe_bwd_t);
status_t init(impl::engine_t *engine) {
using namespace dnnl::impl::format_tag;
auto *intel_engine = utils::downcast<intel::engine_t *>(engine);
const memory_desc_wrapper diff_src_d(diff_src_md());
const memory_desc_wrapper diff_dst_d(diff_dst_md());
const memory_desc_wrapper dst_d(dst_md());
using namespace data_type;
VDISPATCH_SOFTMAX(!is_fwd(), VERBOSE_BAD_PROPKIND);
VDISPATCH_SOFTMAX(axis_size() % buffer_size == 0, VERBOSE_BAD_AXIS);
VDISPATCH_SOFTMAX(memory_desc_ndims_ok(
dst_md(), diff_src_md(), diff_dst_md()),
VERBOSE_INCONSISTENT_NDIMS_WITH_VALS, "dst, diff_src",
"diff_dst", dst_md()->ndims, diff_dst_md()->ndims);
VDISPATCH_SOFTMAX(
axis() == diff_src_d.ndims() - 1, VERBOSE_BAD_AXIS);
VDISPATCH_SOFTMAX(
utils::one_of(diff_src_d.data_type(), f64, f32, bf16, f16),
VERBOSE_UNSUPPORTED_DT);
VDISPATCH_SOFTMAX(
utils::one_of(diff_dst_d.data_type(), f64, f32, bf16, f16),
VERBOSE_UNSUPPORTED_DT);
VDISPATCH_SOFTMAX(intel_engine->mayiuse_sub_group(subgroup_size),
VERBOSE_UNSUPPORTED_DEVICE_FEATURE, "subgroup_size");
VDISPATCH_SOFTMAX(IMPLICATION(utils::one_of(data_type::f64,
diff_dst_md()->data_type,
diff_src_md()->data_type),
intel_engine->mayiuse(
compute::device_ext_t::khr_fp64)),
VERBOSE_UNSUPPORTED_DT_CFG);
VDISPATCH_SOFTMAX(IMPLICATION(utils::one_of(data_type::f16,
diff_dst_md()->data_type,
diff_src_md()->data_type),
intel_engine->mayiuse(
compute::device_ext_t::khr_fp16)),
VERBOSE_UNSUPPORTED_DT_CFG);
VDISPATCH_SOFTMAX(
attr()->has_default_values(), VERBOSE_UNSUPPORTED_ATTR);
VDISPATCH_SOFTMAX_SC(
set_default_formats(), VERBOSE_UNSUPPORTED_TAG);
VDISPATCH_SOFTMAX(diff_dst_d.data_type() == dst_d.data_type(),
VERBOSE_INCONSISTENT_DT, "diff_dst", "dst");
is_nhwc = (diff_src_d.matches_one_of_tag(nwc, nhwc, ndhwc)
!= format_tag::undef);
is_blk = (diff_src_d.matches_one_of_tag(nCw16c, nChw16c, nCdhw16c)
!= format_tag::undef);
if (is_nhwc || is_blk) {
group_size = subgroup_size * (axis_size() / buffer_size);
} else {
group_size = subgroup_size;
}
lws[0] = group_size;
gws[0] = utils::array_product(
&diff_src_md(0)->padded_dims[0], ndims() - 1)
* group_size;
batches = diff_src_md(0)->padded_dims[0]
* diff_src_md(0)->padded_dims[2];
return status::success;
}
compute::range_t gws = compute::range_t::empty(1);
compute::range_t lws = compute::range_t::empty(1);
size_t group_size = 0;
size_t batches = 0;
bool is_nhwc = false;
bool is_blk = false;
const int subgroup_size = 16;
const int buffer_size = 128;
};
status_t init(impl::engine_t *engine) override {
if (pd()->has_zero_dim_memory()) return status::success;
compute::kernel_ctx_t kernel_ctx;
kernel_ctx.define_int("SOFTMAX_AXIS_IDX", pd()->axis());
kernel_ctx.define_int("SOFTMAX_AXIS_SIZE", pd()->axis_size());
kernel_ctx.define_int("SOFTMAX_BUF", pd()->buffer_size);
kernel_ctx.define_int("SUB_GROUP_SIZE", pd()->subgroup_size);
kernel_ctx.define_int("GROUP_SIZE", pd()->group_size);
kernel_ctx.define_int("IS_BWD", 1);
kernel_ctx.define_int("IS_16C", pd()->is_blk);
kernel_ctx.define_int("BATCH", pd()->batches);
kernel_ctx.define_int("IC_WO_PADDING", pd()->diff_src_md(0)->dims[1]);
kernel_ctx.define_int(
"IC_PADDED", pd()->diff_src_md(0)->padded_dims[1]);
kernel_ctx.define_int("IC",
pd()->is_blk ? pd()->subgroup_size
: pd()->diff_src_md(0)->padded_dims[1]);
kernel_ctx.define_int("IS_NHWC", pd()->is_nhwc);
kernel_ctx.add_option("-cl-std=CL2.0");
kernel_ctx.define_int("LOGSOFTMAX", pd()->is_logsoftmax());
const memory_desc_wrapper diff_src_mdw(pd()->diff_src_md());
const memory_desc_wrapper diff_dst_mdw(pd()->diff_dst_md());
const auto diff_src_md_info = memory_desc_info_t::create(diff_src_mdw);
const auto diff_dst_md_info = memory_desc_info_t::create(diff_dst_mdw);
def_memory_desc_info(kernel_ctx, diff_src_md_info, "SRC");
def_memory_desc_info(kernel_ctx, diff_dst_md_info, "DST");
kernel_ctx.set_data_type(pd()->diff_src_md()->data_type);
kernel_ctx.register_buffer_size(*pd()->dst_md());
kernel_ctx.require_stateless_addressing(pd()->has_large_buffers());
set_offsets(kernel_ctx, *pd()->diff_src_md(), "DATA");
CHECK(create_kernel(engine, &kernel_, "xe_softmax_bwd", kernel_ctx));
if (!kernel_) return status::runtime_error;
return status::success;
}
status_t execute(const exec_ctx_t &ctx) const override {
return execute_generic(ctx);
}
protected:
status_t execute_generic(const exec_ctx_t &ctx) const;
const pd_t *pd() const { return (const pd_t *)primitive_t::pd().get(); }
compute::kernel_t kernel_;
};
} } } } }
#endif