#include <utility>
#include "cpu/x64/ip_convolution.hpp"
#include "cpu/cpu_inner_product_pd.hpp"
#include "cpu/x64/cpu_isa_traits.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
namespace {
status_t reshape_dst(memory_desc_t *o_md, const memory_desc_t *i_md) {
dims_t reduce {};
const int ndims = 2; for (int d = 0; d < ndims; ++d)
reduce[d] = i_md->dims[d];
return memory_desc_reshape(*o_md, *i_md, ndims, reduce);
}
status_t maybe_reshape_weights(memory_desc_t *o_md, const memory_desc_t *i_md,
bool with_groups, bool to_ip = false) {
dims_t reduce {};
const int ndims = i_md->ndims + (to_ip ? -1 : +1) * with_groups;
if (to_ip) {
for (int d = 0; d < ndims; ++d)
reduce[d] = i_md->dims[d + with_groups];
} else {
if (with_groups) reduce[0] = 1;
for (int d = 0; d < ndims; ++d)
reduce[d + with_groups] = i_md->dims[d];
}
return memory_desc_reshape(*o_md, *i_md, ndims, reduce);
}
status_t check_conv_ip(convolution_pd_t *self) {
const bool is_ip_applicable = true
&& utils::everyone_is(0, self->KDD(), self->KDH(), self->KDW())
&& utils::everyone_is(
0, self->padFront(), self->padT(), self->padL())
&& utils::everyone_is(
0, self->padBack(), self->padB(), self->padR())
&& utils::everyone_is(
1, self->G(), self->OD(), self->OH(), self->OW())
&& utils::everyone_is(1, self->KSD(), self->KSH(), self->KSW());
if (!is_ip_applicable) return status::unimplemented;
const dim_t ks = self->KD() * self->KH() * self->KW();
const dim_t ks_threshold = 27; const bool is_performant
= 1 < self->MB() && ks > ks_threshold && mayiuse(avx512_core);
if (!is_performant) return status::unimplemented;
return status::success;
}
status_t check_tag(memory_desc_t &md, const format_tag_t tag) {
const memory_desc_wrapper mdw(&md);
if (mdw.matches_one_of_tag(tag) == format_tag::undef)
return status::unimplemented;
return status::success;
}
status_t set_and_or_check_formats(const convolution_desc_t &desc,
memory_desc_t &src_md, memory_desc_t &weights_md, memory_desc_t &dst_md,
memory_desc_t &bias_md, primitive_attr_t &attr) {
using namespace format_tag;
auto atag = utils::pick(src_md.ndims - 3, nwc, nhwc, ndhwc);
const bool is_fwd = utils::one_of(desc.prop_kind,
prop_kind::forward_training, prop_kind::forward_inference);
const bool with_bias = desc.prop_kind != prop_kind::backward_data;
const auto wei_dt = weights_md.data_type;
const bool is_set_allowed = false
|| (utils::one_of(wei_dt, data_type::f32, data_type::bf16)
&& mayiuse(avx512_core))
|| (is_fwd && wei_dt == data_type::s8)
|| (wei_dt == data_type::f16 && mayiuse(avx512_core_fp16));
if (is_set_allowed && src_md.format_kind == format_kind::any)
CHECK(memory_desc_init_by_tag(src_md, atag));
else
CHECK(check_tag(src_md, atag));
if (is_set_allowed && dst_md.format_kind == format_kind::any)
CHECK(memory_desc_init_by_tag(dst_md, atag));
else
CHECK(check_tag(dst_md, atag));
if (with_bias && bias_md.format_kind != format_kind::undef) {
auto btag = x;
if (bias_md.format_kind == format_kind::any)
CHECK(memory_desc_init_by_tag(bias_md, btag));
else
CHECK(check_tag(bias_md, btag));
}
return attr.set_default_formats(&dst_md);
}
}
status_t ip_convolution_fwd_t::pd_t::init(engine_t *engine) {
using namespace format_tag;
using smask_t = primitive_attr_t::skip_mask_t;
VDISPATCH_CONV(is_fwd(), VERBOSE_BAD_PROPKIND);
VDISPATCH_CONV(set_default_alg_kind(alg_kind::convolution_direct),
VERBOSE_BAD_ALGORITHM);
VDISPATCH_CONV(
attr()->has_default_values(smask_t::scales | smask_t::post_ops),
VERBOSE_UNSUPPORTED_ATTR);
CHECK(check_conv_ip(this));
CHECK(set_and_or_check_formats(
*desc(), src_md_, weights_md_, dst_md_, bias_md_, attr_));
CHECK(init_ip(engine));
if (weights_md_.format_kind == format_kind::any)
CHECK(maybe_reshape_weights(
&weights_md_, ip_pd_->weights_md(), with_groups()));
init_name();
init_scratchpad();
return status::success;
}
status_t ip_convolution_fwd_t::pd_t::ip_desc_create(inner_product_desc_t *ipd) {
const bool to_ip = true;
memory_desc_t ip_dst_d;
CHECK(reshape_dst(&ip_dst_d, &dst_md_));
memory_desc_t ip_weights_d;
CHECK(maybe_reshape_weights(
&ip_weights_d, &weights_md_, with_groups(), to_ip));
return ip_desc_init(ipd, desc()->prop_kind, &src_md_, &ip_weights_d,
&bias_md_, &ip_dst_d);
}
status_t ip_convolution_fwd_t::execute(const exec_ctx_t &ctx) const {
using namespace memory_tracking::names;
exec_args_t ip_args = ctx.args();
exec_ctx_t conv_ctx(ctx, std::move(ip_args));
auto *nested_grantor = create_nested_grantor(ctx.get_scratchpad_grantor(),
key_nested, ip_p_->pd()->scratchpad_registry());
conv_ctx.set_scratchpad_grantor(nested_grantor);
return ip_p_->execute(conv_ctx);
}
status_t ip_convolution_bwd_data_t::pd_t::init(engine_t *engine) {
using namespace format_tag;
VDISPATCH_CONV(desc()->prop_kind == prop_kind::backward_data,
VERBOSE_BAD_PROPKIND);
VDISPATCH_CONV(set_default_alg_kind(alg_kind::convolution_direct),
VERBOSE_BAD_ALGORITHM);
VDISPATCH_CONV(attr()->has_default_values(), VERBOSE_UNSUPPORTED_ATTR);
CHECK(check_conv_ip(this));
CHECK(set_and_or_check_formats(
*desc(), diff_src_md_, weights_md_, diff_dst_md_, bias_md_, attr_));
CHECK(init_ip(engine));
if (weights_md_.format_kind == format_kind::any)
CHECK(maybe_reshape_weights(
&weights_md_, ip_pd_->weights_md(), with_groups()));
init_name();
init_scratchpad();
return status::success;
}
status_t ip_convolution_bwd_data_t::pd_t::ip_desc_create(
inner_product_desc_t *ipd) {
const bool to_ip = true;
memory_desc_t ip_diff_dst_d;
CHECK(reshape_dst(&ip_diff_dst_d, &diff_dst_md_));
memory_desc_t ip_weights_d;
CHECK(maybe_reshape_weights(
&ip_weights_d, &weights_md_, with_groups(), to_ip));
return ip_desc_init(ipd, desc()->prop_kind, &diff_src_md_, &ip_weights_d,
nullptr, &ip_diff_dst_d);
}
status_t ip_convolution_bwd_data_t::execute(const exec_ctx_t &ctx) const {
using namespace memory_tracking::names;
exec_args_t ip_args = ctx.args();
exec_ctx_t conv_ctx(ctx, std::move(ip_args));
auto *nested_grantor = create_nested_grantor(ctx.get_scratchpad_grantor(),
key_nested, ip_p_->pd()->scratchpad_registry());
conv_ctx.set_scratchpad_grantor(nested_grantor);
return ip_p_->execute(conv_ctx);
}
status_t ip_convolution_bwd_weights_t::pd_t::init(engine_t *engine) {
using namespace format_tag;
VDISPATCH_CONV(desc()->prop_kind == prop_kind::backward_weights,
VERBOSE_BAD_PROPKIND);
VDISPATCH_CONV(set_default_alg_kind(alg_kind::convolution_direct),
VERBOSE_BAD_ALGORITHM);
VDISPATCH_CONV(attr()->has_default_values(), VERBOSE_UNSUPPORTED_ATTR);
CHECK(check_conv_ip(this));
CHECK(set_and_or_check_formats(*desc(), src_md_, diff_weights_md_,
diff_dst_md_, diff_bias_md_, attr_));
CHECK(init_ip(engine));
if (diff_weights_md_.format_kind == format_kind::any)
CHECK(maybe_reshape_weights(
&diff_weights_md_, ip_pd_->diff_weights_md(), with_groups()));
init_name();
init_scratchpad();
return status::success;
}
status_t ip_convolution_bwd_weights_t::pd_t::ip_desc_create(
inner_product_desc_t *ipd) {
const bool to_ip = true;
memory_desc_t ip_diff_dst_d;
CHECK(reshape_dst(&ip_diff_dst_d, &diff_dst_md_));
memory_desc_t ip_diff_weights_d;
CHECK(maybe_reshape_weights(
&ip_diff_weights_d, &diff_weights_md_, with_groups(), to_ip));
return ip_desc_init(ipd, desc()->prop_kind, &src_md_, &ip_diff_weights_d,
&diff_bias_md_, &ip_diff_dst_d);
}
status_t ip_convolution_bwd_weights_t::execute(const exec_ctx_t &ctx) const {
using namespace memory_tracking::names;
exec_args_t ip_args = ctx.args();
exec_ctx_t conv_ctx(ctx, std::move(ip_args));
auto *nested_grantor = create_nested_grantor(ctx.get_scratchpad_grantor(),
key_nested, ip_p_->pd()->scratchpad_registry());
conv_ctx.set_scratchpad_grantor(nested_grantor);
return ip_p_->execute(conv_ctx);
}
} } } }