#include "dnnl_types.h"
#include "common/bfloat16.hpp"
#include "common/c_types_map.hpp"
#include "common/dnnl_thread.hpp"
#include "common/memory_tracking.hpp"
#include "common/type_helpers.hpp"
#include "common/utils.hpp"
#include "cpu/platform.hpp"
#include "cpu/x64/brgemm/brgemm_utils.hpp"
#include "cpu/x64/cpu_isa_traits.hpp"
#include "cpu/x64/injectors/jit_uni_postops_injector.hpp"
#include "cpu/x64/jit_brgemm_conv_bwd_utils.hpp"
#include "cpu/x64/jit_generator.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
using namespace dnnl::impl::status;
using namespace dnnl::impl::format_tag;
using namespace dnnl::impl::memory_tracking::names;
using namespace dnnl::impl::utils;
using namespace prop_kind;
using namespace data_type;
namespace brgemm_convolution_bwd_utils {
inline status_t init_tag(format_tag_t &tag, memory_desc_t &md,
const memory_desc_wrapper &mdw, const format_tag_t tag_value) {
if (mdw.format_kind() == format_kind::any) {
CHECK(memory_desc_init_by_tag(md, tag_value));
tag = tag_value;
} else {
tag = mdw.mb_stride_relaxed_match(tag_value);
}
VDISPATCH_CONV_IC(tag == tag_value, VERBOSE_UNSUPPORTED_TAG);
return status::success;
}
bool is_amx(cpu_isa_t isa) {
return is_superset(isa, avx512_core_amx);
}
bool post_ops_ok(jit_brgemm_conv_conf_t &jcp, primitive_attr_t &attr,
const memory_desc_wrapper &dst_d, bool use_inversion) {
using namespace injector;
const auto &post_ops = attr.post_ops_;
if (post_ops.len() > 0 && !use_inversion) return false;
return injector::post_ops_ok(post_ops_ok_args_t(jcp.isa,
{sum, eltwise, binary}, post_ops, &dst_d,
false , false ,
false , true ,
{broadcasting_strategy_t::per_oc, broadcasting_strategy_t::scalar,
broadcasting_strategy_t::no_broadcast}));
}
bool is_groups_ok(jit_brgemm_conv_conf_t &jcp) {
return jcp.ngroups > 1
&& IMPLICATION(one_of(jcp.src_dt, u8, s8, bf16, f16),
jcp.oc % 4 == 0 && jcp.ic % 4 == 0);
}
status_t pick_tags(jit_brgemm_conv_conf_t &jcp, memory_desc_t &diff_dst_md,
memory_desc_t &weights_md, memory_desc_t &diff_src_md,
memory_desc_t &bias_md) {
format_tag_t src_tag, dst_tag, wei_tag;
dst_tag = pick(jcp.ndims - 3, nwc, nhwc, ndhwc);
const memory_desc_wrapper diff_dst_d(&diff_dst_md);
const memory_desc_wrapper weights_d(&weights_md);
const memory_desc_wrapper diff_src_d(&diff_src_md);
const memory_desc_wrapper bias_d(&bias_md);
const bool with_groups = weights_d.ndims() == diff_src_d.ndims() + 1;
const bool is_1d = jcp.ndims == 3;
const bool is_2d = jcp.ndims == 4;
const bool is_3d = jcp.ndims == 5;
if (jcp.wei_plain) {
return status::unimplemented;
} else {
jcp.LDB = jcp.ic_block;
const bool no_vnni_format = jcp.wei_dt == f32
|| (jcp.wei_dt == f16 && jcp.isa == avx512_core_fp16)
|| jcp.is_f32_bf16 || jcp.is_f32_f16;
if (jcp.ic_block == 64) {
if (is_3d) {
if (no_vnni_format)
wei_tag = with_groups ? gIdhwo64i : Idhwo64i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIdhwO16o64i4o : IdhwO16o64i4o;
else
wei_tag = with_groups ? gIdhwO64i4o : IdhwO64i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIdhwO16o64i2o : IdhwO16o64i2o;
else
wei_tag = with_groups ? gIdhwO64i2o : IdhwO64i2o;
} else
return status::unimplemented;
} else if (is_1d) {
if (no_vnni_format)
wei_tag = with_groups ? gIwo64i : Iwo64i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIwO16o64i4o : IwO16o64i4o;
else
wei_tag = with_groups ? gIwO64i4o : IwO64i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIwO16o64i2o : IwO16o64i2o;
else
wei_tag = with_groups ? gIwO64i2o : IwO64i2o;
} else
return status::unimplemented;
} else {
assert(is_2d);
UNUSED(is_2d);
if (no_vnni_format)
wei_tag = with_groups ? gIhwo64i : Ihwo64i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIhwO16o64i4o : IhwO16o64i4o;
else
wei_tag = with_groups ? gIhwO64i4o : IhwO64i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIhwO16o64i2o : IhwO16o64i2o;
else
wei_tag = with_groups ? gIhwO64i2o : IhwO64i2o;
} else
return status::unimplemented;
}
} else if (jcp.ic_block == 48) {
if (is_3d) {
if (no_vnni_format)
wei_tag = with_groups ? gIdhwo48i : Idhwo48i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIdhwO16o48i4o : IdhwO16o48i4o;
else
wei_tag = with_groups ? gIdhwO48i4o : IdhwO48i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIdhwO16o48i2o : IdhwO16o48i2o;
else
wei_tag = with_groups ? gIdhwO48i2o : IdhwO48i2o;
} else
return status::unimplemented;
} else if (is_1d) {
if (no_vnni_format)
wei_tag = with_groups ? gIwo48i : Iwo48i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIwO16o48i4o : IwO16o48i4o;
else
wei_tag = with_groups ? gIwO48i4o : IwO48i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIwO16o48i2o : IwO16o48i2o;
else
wei_tag = with_groups ? gIwO48i2o : IwO48i2o;
} else
return status::unimplemented;
} else {
assert(is_2d);
UNUSED(is_2d);
if (no_vnni_format)
wei_tag = with_groups ? gIhwo48i : Ihwo48i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIhwO16o48i4o : IhwO16o48i4o;
else
wei_tag = with_groups ? gIhwO48i4o : IhwO48i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIhwO16o48i2o : IhwO16o48i2o;
else
wei_tag = with_groups ? gIhwO48i2o : IhwO48i2o;
} else
return status::unimplemented;
}
} else if (jcp.ic_block == 32) {
if (is_3d) {
if (no_vnni_format)
wei_tag = with_groups ? gIdhwo32i : Idhwo32i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIdhwO16o32i4o : IdhwO16o32i4o;
else
wei_tag = with_groups ? gIdhwO32i4o : IdhwO32i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIdhwO16o32i2o : IdhwO16o32i2o;
else
wei_tag = with_groups ? gIdhwO32i2o : IdhwO32i2o;
} else
return status::unimplemented;
} else if (is_1d) {
if (no_vnni_format)
wei_tag = with_groups ? gIwo32i : Iwo32i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIwO16o32i4o : IwO16o32i4o;
else
wei_tag = with_groups ? gIwO32i4o : IwO32i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIwO16o32i2o : IwO16o32i2o;
else
wei_tag = with_groups ? gIwO32i2o : IwO32i2o;
} else
return status::unimplemented;
} else {
assert(is_2d);
UNUSED(is_2d);
if (no_vnni_format)
wei_tag = with_groups ? gIhwo32i : Ihwo32i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIhwO16o32i4o : IhwO16o32i4o;
else
wei_tag = with_groups ? gIhwO32i4o : IhwO32i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIhwO16o32i2o : IhwO16o32i2o;
else
wei_tag = with_groups ? gIhwO32i2o : IhwO32i2o;
} else
return status::unimplemented;
}
} else if (jcp.ic_block == 24) {
if (is_3d) {
if (no_vnni_format)
wei_tag = with_groups ? gIdhwo24i : Idhwo24i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3))
wei_tag = with_groups ? gIdhwO24i4o : IdhwO24i4o;
else if (one_of(jcp.wei_dt, bf16, f16))
wei_tag = with_groups ? gIdhwO24i2o : IdhwO24i2o;
else
return status::unimplemented;
} else if (is_1d) {
if (no_vnni_format)
wei_tag = with_groups ? gIwo24i : Iwo24i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3))
wei_tag = with_groups ? gIwO24i4o : IwO24i4o;
else if (one_of(jcp.wei_dt, bf16, f16))
wei_tag = with_groups ? gIwO24i2o : IwO24i2o;
else
return status::unimplemented;
} else {
assert(is_2d);
UNUSED(is_2d);
if (no_vnni_format)
wei_tag = with_groups ? gIhwo24i : Ihwo24i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3))
wei_tag = with_groups ? gIhwO24i4o : IhwO24i4o;
else if (one_of(jcp.wei_dt, bf16, f16))
wei_tag = with_groups ? gIhwO24i2o : IhwO24i2o;
else
return status::unimplemented;
}
} else if (jcp.ic_block == 16) {
if (is_3d) {
if (no_vnni_format)
wei_tag = with_groups ? gIdhwo16i : Idhwo16i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIdhwO16o16i4o : IdhwO16o16i4o;
else
wei_tag = with_groups ? gIdhwO16i4o : IdhwO16i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIdhwO16o16i2o : IdhwO16o16i2o;
else
wei_tag = with_groups ? gIdhwO16i2o : IdhwO16i2o;
} else
return status::unimplemented;
} else if (is_1d) {
if (no_vnni_format)
wei_tag = with_groups ? gIwo16i : Iwo16i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIwO16o16i4o : IwO16o16i4o;
else
wei_tag = with_groups ? gIwO16i4o : IwO16i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIwO16o16i2o : IwO16o16i2o;
else
wei_tag = with_groups ? gIwO16i2o : IwO16i2o;
} else
return status::unimplemented;
} else {
assert(is_2d);
UNUSED(is_2d);
if (no_vnni_format)
wei_tag = with_groups ? gIhwo16i : Ihwo16i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIhwO16o16i4o : IhwO16o16i4o;
else
wei_tag = with_groups ? gIhwO16i4o : IhwO16i4o;
} else if (one_of(jcp.wei_dt, bf16, f16)) {
if (jcp.is_oc_padded)
wei_tag = with_groups ? gIhwO16o16i2o : IhwO16o16i2o;
else
wei_tag = with_groups ? gIhwO16i2o : IhwO16i2o;
} else
return status::unimplemented;
}
} else if (jcp.ic_block == 8) {
if (is_3d) {
if (no_vnni_format)
wei_tag = with_groups ? gIdhwo8i : Idhwo8i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3))
wei_tag = with_groups ? gIdhwO8i4o : IdhwO8i4o;
else if (one_of(jcp.wei_dt, bf16, f16))
wei_tag = with_groups ? gIdhwO8i2o : IdhwO8i2o;
else
return status::unimplemented;
} else if (is_1d) {
if (no_vnni_format)
wei_tag = with_groups ? gIwo8i : Iwo8i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3))
wei_tag = with_groups ? gIwO8i4o : IwO8i4o;
else if (one_of(jcp.wei_dt, bf16, f16))
wei_tag = with_groups ? gIwO8i2o : IwO8i2o;
else
return status::unimplemented;
} else {
assert(is_2d);
UNUSED(is_2d);
if (no_vnni_format)
wei_tag = with_groups ? gIhwo8i : Ihwo8i;
else if (one_of(jcp.wei_dt, s8, f8_e5m2, f8_e4m3))
wei_tag = with_groups ? gIhwO8i4o : IhwO8i4o;
else if (one_of(jcp.wei_dt, bf16, f16))
wei_tag = with_groups ? gIhwO8i2o : IhwO8i2o;
else
return status::unimplemented;
}
} else
return status::unimplemented;
}
src_tag = dst_tag;
CHECK(init_tag(jcp.src_tag, diff_dst_md, diff_dst_d, src_tag));
CHECK(init_tag(jcp.dst_tag, diff_src_md, diff_src_d, dst_tag));
CHECK(init_tag(jcp.wei_tag, weights_md, weights_d, wei_tag));
return status::success;
}
struct brg_blocking_t : public jit_brgemm_conv_conf_t {
struct array_in_loop_t {
dim_t itersize;
float repeatn;
float overlap;
void set(dim_t iter_s, float rpt, float ovlp = 1.f) {
itersize = iter_s;
repeatn = rpt;
overlap = ovlp;
}
};
struct loop_t {
array_in_loop_t src;
array_in_loop_t wei;
array_in_loop_t dst;
};
brg_blocking_t() {
jit_brgemm_conv_conf_t *base
= static_cast<jit_brgemm_conv_conf_t *>(this);
*base = jit_brgemm_conv_conf_t();
init();
}
brg_blocking_t(const jit_brgemm_conv_conf_t &jcp)
: jit_brgemm_conv_conf_t(jcp) {
init();
}
void init() {
ur = 0;
ur_block = 0;
ur_block_tail = 0;
eff = 0.f;
nb_kd = 0;
nb_kh = 0;
nb_kw = 0;
sp = 0;
sp_block = 0;
nb_sp = 0;
eff = 0;
max_regs = isa == isa_undef ? 0 : isa_num_vregs(isa);
bcast_simd = acc_simd_w;
}
int ur, ur_block, ur_block_tail;
int nb_kd, nb_kh, nb_kw;
int max_regs;
int bcast_simd;
float eff;
static unsigned L1;
static unsigned L2;
static constexpr float L1_k = 1.f;
static constexpr float L2_k = 3.f;
static constexpr float L3_k = 15.f;
static constexpr float mem_k = 15.f;
static constexpr int bench_iterations = 1;
int sp, sp_block, nb_sp;
void get_from_jcp(const jit_brgemm_conv_conf_t &jcp) { *this = jcp; }
void save_to_jcp(jit_brgemm_conv_conf_t &jcp) const { jcp = *this; }
status_t estimate_brgemm_ur();
status_t get_brgemm_ur(
const primitive_attr_t *attr, const memory_desc_t &dst_md);
float io_k(dim_t src, dim_t wei, dim_t dst, float n, float pk,
bool is_broadcast, bool is_shared) const;
float io_k(const loop_t loop, const array_in_loop_t arr, float pk,
bool is_broadcast, bool is_shared) const;
void select_oc_block();
void update_blocks();
bool fast_check_ic_block() const;
float est_eff();
void iterate_ker_block(brg_blocking_t &best_brgb, int kd_block,
int kh_block, bool maybe_use_buffer, int max_iw_block_thr);
status_t calc_blocks();
bool fast_check_ic_block_1x1() const;
float est_eff_1x1();
static int get_inp_size(
int max_src_size, int dst_size, int k, int stride, int dilate) {
auto adj_str = nstl::min(k, stride);
const auto res = nstl::min(max_src_size,
calculate_end_padding(0, dst_size, 0, adj_str,
calculate_extended_filter_size(k, dilate)));
return res;
}
static int get_inp_block_size(
int out_size, int stride, int ext_k, int padding) {
const auto res = div_up(out_size + padding % stride, stride)
+ (ext_k - 1 - padding % stride) / stride;
return res;
}
static float squeeze_val(float eff, float koeff) {
if (koeff <= 0) return 1;
if (koeff == 1) return eff;
const auto k = 1.f / koeff;
return (k > 1.f) ? (k - 1 + eff) / k : eff * koeff;
}
static int estimate_ur(int ic_block) {
const auto est_ur = (ic_block == 64)
? 6
: ((ic_block == 48) ? 9 : ((ic_block == 32) ? 14 : 28));
return est_ur;
}
int inp_w(int out_w, int ker_w) const {
return get_inp_size(ow, out_w, ker_w, stride_w, dilate_w);
}
int rnd_simd(int val) const { return rnd_up(val, simd_w); }
int rnd_inp_simd(int out_w, int ker_w, int voc) const {
const auto vsp = inp_w(out_w, ker_w);
return ((stride_w == 1 && voc >= oc) ? rnd_up(vsp * voc, simd_w)
: vsp * rnd_up(voc, simd_w));
}
static constexpr int MAXNLOOPS = 32;
loop_t loop[MAXNLOOPS];
};
unsigned brg_blocking_t::L1;
unsigned brg_blocking_t::L2;
float brg_blocking_t::io_k(dim_t src, dim_t wei, dim_t dst, float n, float pk,
bool is_broadcast, bool is_shared) const {
if (n < 1) return 0;
if (n == 1) return pk;
const auto amount = src * src_dsz + wei * wei_dsz + dst * dst_dsz
+ (use_buffer ? dst * acc_dsz : 0);
const auto amount_L1 = is_broadcast ? src * src_dsz : amount;
const auto k = is_broadcast
? ((amount_L1 < L1) ? L1_k
: ((amount < L2) ? L2_k
: (is_shared ? L3_k : mem_k)))
: ((amount < L2) ? L2_k : (is_shared ? L3_k : mem_k));
const auto cost = pk + k * (n - 1);
return cost / n;
}
float brg_blocking_t::io_k(const loop_t loop, const array_in_loop_t arr,
float pk, bool is_broadcast, bool is_shared) const {
return io_k(loop.src.itersize, loop.wei.itersize, loop.dst.itersize,
arr.repeatn * arr.overlap, pk, is_broadcast, is_shared);
}
void brg_blocking_t::select_oc_block() {
const auto padded_oc = vnni_block * (is_oc_padded ? acc_simd_w : 1);
oc_block = (exec_type == exec_trans ? rnd_up(oc, padded_oc) : oc);
nb_oc = utils::div_up(oc, oc_block);
}
status_t brg_blocking_t::estimate_brgemm_ur() {
if (sp_block <= 0) return status::invalid_arguments;
LDA = exec_type == exec_trans ? oc_block : oc_without_padding * ngroups;
LDB = ic_block;
LDC = use_buffer ? ic_block : stride_w * ic_without_padding;
const auto padded_oc = vnni_block * (is_oc_padded ? acc_simd_w : 1);
ocp = rnd_up(oc, padded_oc);
const auto adj_sp = div_up(iw_block, stride_w);
M = brgM = adj_sp >= sp_block ? sp_block : 0;
M_tail = brgM_tail = adj_sp % sp_block;
N = ic >= ic_block ? ic_block : 0;
N_tail = ic % ic_block;
K = oc >= oc_block ? oc_block : 0;
K_tail = oc % oc_block;
const auto vK = K > 0 ? K : K_tail;
const auto vM = M > 0 ? M : M_tail;
const auto vN = N > 0 ? N : N_tail;
const float alpha = 1.0;
const float beta = 0.0;
brgemm_desc_t brg;
CHECK(brgemm_utils::init_brgemm_conf(&brg, isa, brgemm_addr, src_dt, wei_dt,
brgemm_row_major, alpha, beta, LDA, LDB, LDC, vM, vN, vK, nullptr,
is_bf32, is_tf32));
CHECK(brgemm_utils::brgemm_blocking(&brg));
ur = brg.bd_block * (brg.bd_block2 > 0 ? brg.bd_block2 : 1);
if (ur == 0) return status::invalid_arguments;
ur_block = brg.bd_block;
if (is_1x1 && is_amx(isa) && M > 0 && M_tail > 0) {
brgemm_desc_t brg_sp_tail;
CHECK(brgemm_utils::init_brgemm_conf(&brg_sp_tail, isa, brgemm_addr,
src_dt, wei_dt, brgemm_row_major, alpha, beta, LDA, LDB, LDC,
M_tail, vN, vK, nullptr, is_bf32, is_tf32));
CHECK(brgemm_utils::brgemm_blocking(&brg_sp_tail));
ur_block_tail = brg_sp_tail.bd_block;
} else {
ur_block_tail = 0;
}
return status::success;
}
status_t brg_blocking_t::get_brgemm_ur(
const primitive_attr_t *attr, const memory_desc_t &dst_md) {
if (sp_block <= 0 || oc_block <= 0 || ic_block <= 0)
return status::invalid_arguments;
CHECK(estimate_brgemm_ur());
LDD = stride_w * ic_without_padding;
const float alpha = 1.0;
const float beta = 1.0;
const float beta_init = 0.0;
for (int i = 0; i < M; i++) {
auto vM = i + 1;
if ((utils::one_of(exec_type, exec_trans, exec_vpad) || is_1x1)
&& vM != M && vM != M_tail)
continue;
for (int i_init = 0; i_init < 2; i_init++) {
for (int i_N = 0; i_N < 2; i_N++) {
for (int i_K = 0; i_K < 2; i_K++) {
auto vbeta = (i_init) ? beta_init : beta;
auto vN = (i_N) ? N_tail : N;
auto vK = (i_K) ? K_tail : K;
if (vN == 0 || vK == 0) continue;
brgemm_desc_t brg;
brgemm_strides_t brg_strides;
brg_strides.stride_a = ngroups * oc_without_padding
* (dilate_w + 1) * src_dsz;
brg_strides.stride_b = rnd_up(oc, vnni_block)
* rnd_up(ic, ic_block) * wei_dsz;
const auto strides_ptr = (brg_type == brgemm_strd)
? &brg_strides
: nullptr;
CHECK(brgemm_utils::init_brgemm_conf(&brg, isa, brg_type,
src_dt, wei_dt, brgemm_row_major, alpha, vbeta, LDA,
LDB, LDC, vM, vN, vK, strides_ptr, is_bf32,
is_tf32));
CHECK(brgemm_utils::brgemm_blocking(&brg));
brgemm_attr_t brgattr;
brgattr.max_bs = max_batch;
const auto max_vpad = (exec_type == exec_vpad)
? nstl::max(l_pad, r_pad)
: 0;
brgattr.max_top_vpad = max_vpad;
brgattr.max_bottom_vpad = max_vpad;
brgattr.fpmath_mode = attr->fpmath_.mode_;
CHECK(brgemm_desc_set_attr(&brg, brgattr));
brg.with_sum = with_sum;
CHECK(brgemm_desc_set_postops(
&brg, attr, &dst_md, LDD, bia_dt));
}
}
}
}
return status::success;
}
void brg_blocking_t::update_blocks() {
if (sp_block <= 0
|| utils::one_of(0, id_block, ih_block, oc_block, ic_block,
kd_block, kh_block, kw_block, is_block, iw_block))
return;
const bool maskrcnn_cond = one_of(isa, avx2_vnni, avx2_vnni_2, avx512_core)
&& IMPLICATION(isa == avx512_core, !has_int8_vnni) && ic == 256
&& oc == 256 && everyone_is(28, ih, iw) && everyone_is(14, oh, ow)
&& everyone_is(2, kh, stride_h, kw, stride_w);
if (maskrcnn_cond) {
ic_block = 64;
iw_block = 28;
ih_block = 14;
}
nb_id = div_up(id, id_block);
nb_ih = div_up(ih, ih_block);
nb_oc = div_up(oc, oc_block);
nb_ic = div_up(ic, ic_block);
nb_kd = div_up(kd, kd_block);
nb_kh = div_up(kh, kh_block);
nb_kw = div_up(kw, kw_block);
nb_iw = div_up(iw, iw_block);
sp = has_uneven_iw ? rnd_up(iw, stride_w) : iw;
sp_block = iw_block;
nb_sp = nb_iw;
ow_block = get_inp_block_size(iw_block, stride_w, ext_kw, l_pad);
oh_block = get_inp_block_size(ih_block, stride_h, ext_kh, t_pad);
od_block = get_inp_block_size(id_block, stride_d, ext_kd, f_pad);
}
bool brg_blocking_t::fast_check_ic_block() const {
if (is_1x1) return fast_check_ic_block_1x1();
const auto rnd_ic = rnd_up(ic, acc_simd_w);
auto res = false;
if (ic_block == 64) {
res = (rnd_ic % ic_block == 0 && rnd_ic * wei_dsz < 192 * 4);
} else if (ic_block == 48) {
const bool big_spatial
= od * oh * ow > 81 * stride_d * stride_h * stride_w;
res = (rnd_ic % ic_block == 0 && rnd_ic * wei_dsz <= 384 * 4
&& big_spatial);
} else
res = true;
return res;
}
float brg_blocking_t::est_eff() {
if (is_1x1) return est_eff_1x1();
const auto icblock = ic_block / acc_simd_w;
const auto brgemm_microkernel_eff
= (static_cast<float>(icblock) * ur) / ((ur + icblock) * max_regs);
const auto ur_eff = static_cast<float>(sp_block) / rnd_up(sp_block, ur);
const auto brgemm_eff = squeeze_val(ur
* (2.f - nstl::min(1.9f, static_cast<float>(ur) / sp_block))
/ 64,
0.5f);
const auto sp_amount = nb_id * nb_ih * nb_sp;
const auto work_amount = mb * ngroups * nb_ic * sp_amount;
const auto sp_eff = (static_cast<float>(sp) / rnd_up(sp, sp_block));
const auto thr_eff = static_cast<float>(work_amount)
/ utils::rnd_up(work_amount, nthr);
const auto ic_block_eff = static_cast<float>(ic) / rnd_up(ic, ic_block);
const auto job = div_up(work_amount, nthr);
auto job_eff = 1.f;
if (job < nthr) {
std::vector<dim_t> thr_jobs(nthr);
for (int ithr = 0; ithr < nthr; ithr++) {
thr_jobs[ithr] = 0;
if (ithr >= work_amount) continue;
dim_t thr_job = 0;
int start {0}, end {0};
balance211(work_amount, nthr, ithr, start, end);
int n {0}, g {0}, icb {0}, idp {0}, ihp {0}, spb {0};
if (loop_order == loop_ndhwgc)
nd_iterator_init(start, n, mb, idp, id, ihp, ih, spb, nb_sp, g,
ngroups, icb, nb_ic);
else if (loop_order == loop_ngcdhw)
nd_iterator_init(start, n, mb, g, ngroups, icb, nb_ic, idp, id,
ihp, ih, spb, nb_sp);
for (auto work = start; work < end; work++) {
const int icp = icb * ic_block;
const auto ic_sz = nstl::min(ic - icp, ic_block);
int sp_sz = 0;
const int spp = spb * sp_block;
sp_sz = nstl::min(sp - spp, sp_block);
thr_job += sp_sz * ic_sz;
nd_iterator_step(n, mb, idp, id, ihp, ih, spb, nb_sp, g,
ngroups, icb, nb_ic);
if (loop_order == loop_ndhwgc)
nd_iterator_step(n, mb, idp, id, ihp, ih, spb, nb_sp, g,
ngroups, icb, nb_ic);
else if (loop_order == loop_ngcdhw)
nd_iterator_step(n, mb, g, ngroups, icb, nb_ic, idp, id,
ihp, ih, spb, nb_sp);
}
thr_jobs[ithr] = thr_job;
}
dim_t max_job = 0;
dim_t sum_job = 0;
for (int ithr = 0; ithr < nthr; ithr++) {
if (thr_jobs[ithr] > max_job) max_job = thr_jobs[ithr];
sum_job += thr_jobs[ithr];
}
job_eff = max_job == 0 ? 1
: static_cast<float>(sum_job) / (max_job * nthr);
} else {
job_eff = thr_eff;
}
const auto oc_blocking_size = oc_block * nb_oc_blocking;
const auto ic_blocking_size = ic_block * oc_blocking_size;
int l = -1;
l++;
const auto inp_ur = inp_w(ur, kw_block);
loop[l].src.set(inp_ur * simd_w, 1, bcast_simd);
loop[l].dst.set(0, 1);
loop[l].wei.set(ic_block, 1);
l++;
auto src_is = rnd_inp_simd(ur, kw_block, oc_blocking_size);
loop[l].src.set(src_is, 1, kw_block);
loop[l].dst.set(0, 1);
loop[l].wei.set(ic_blocking_size, 1);
l++;
loop[l].src.set(src_is, 1);
loop[l].dst.set(0, 1);
auto wei_is = kw_block * ic_blocking_size;
loop[l].wei.set(wei_is, 1);
l++;
const auto nb_ur = div_up(sp_block, ur);
loop[l].src.set(kd_block * kh_block * src_is, 1);
loop[l].dst.set(ur * ic_block, 1);
wei_is = kd_block * kh_block * kw_block * ic_blocking_size;
loop[l].wei.set(wei_is, nb_ur);
l++;
loop[l].src.set(kd_block * kh_block
* rnd_inp_simd(sp_block, kw_block, oc_blocking_size),
1);
loop[l].dst.set(sp_block * ic_block, nb_kd * nb_kh * nb_kw);
loop[l].wei.set(wei_is, 1);
l++;
const auto oc_chunks = div_up(nb_oc, nb_oc_blocking);
loop[l].src.set(kd * kh * rnd_inp_simd(sp_block, kw, oc_blocking_size), 1);
loop[l].dst.set(sp_block * ic_block, oc_chunks);
wei_is = kd * kh * kw * ic_blocking_size;
loop[l].wei.set(wei_is, 1);
const auto dim_ic = (loop_order == loop_ndhwgc) ? 1 : sp_amount;
const auto nb_ic_thr = nstl::min(nb_ic, div_up(job, dim_ic));
const auto ic_thr = nstl::min(ic, nb_ic_thr * ic_block);
const auto nsimd_ic_thr = div_up(ic_thr, simd_w);
const auto dim_sp = (loop_order == loop_ndhwgc) ? ngroups * nb_ic : 1;
const auto nb_sp_thr = nstl::min(nb_sp, div_up(job, dim_sp));
const auto sp_thr = nstl::min(sp, nb_sp_thr * sp_block);
const auto dim_ih = nb_sp * dim_sp;
const int nb_ih_thr = nstl::min(nb_ih, div_up(job, dim_ih));
const int ih_thr = nstl::min(ih, nb_ih_thr * ih_block);
const auto dim_id = nb_ih * dim_ih;
const int nb_id_thr = nstl::min(nb_id, div_up(job, dim_id));
const int id_thr = nstl::min(id, nb_id_thr * id_block);
src_is = kd * kh * rnd_inp_simd(sp_block, kw, oc);
auto wei_op = kd * kh * kw * icblock * oc;
if (loop_order == loop_ndhwgc) {
l++;
loop[l].src.set(src_is, nb_ic_thr);
loop[l].dst.set(sp_block * ic_block, 1);
wei_is = kd * kh * kw * ic_block * oc;
wei_op = kd * kh * kw * nsimd_ic_thr * oc;
loop[l].wei.set(wei_is, 1);
}
l++;
loop[l].src.set(src_is, 1);
const auto rnd_ic_for_sp
= simd_w * ((loop_order == loop_ndhwgc) ? nsimd_ic_thr : icblock);
loop[l].dst.set(sp_block * rnd_ic_for_sp, 1);
loop[l].wei.set(wei_op * simd_w, nb_sp_thr);
l++;
src_is = kd * kh * rnd_inp_simd(sp_thr, kw, oc);
loop[l].src.set(ih_block * src_is, 1);
loop[l].dst.set(sp_thr * rnd_ic_for_sp, 1);
loop[l].wei.set(wei_op * simd_w, nb_ih_thr);
l++;
loop[l].src.set(id_block * ih_thr * src_is, 1);
loop[l].dst.set(ih_thr * sp_thr * rnd_ic_for_sp, 1);
loop[l].wei.set(wei_op * simd_w, nb_id_thr);
if (loop_order != loop_ndhwgc) {
l++;
loop[l].src.set(id_thr * ih_thr * src_is, nb_ic_thr);
loop[l].dst.set(ic_block * id_thr * ih_thr * sp_thr, 1);
loop[l].wei.set(kd * kh * kw * ic_block * oc, 1);
}
l++;
const auto mb_thr = nstl::min(mb, div_up(job, sp_amount * ngroups * nb_ic));
loop[l].src.set(id_thr * ih_thr * src_is, 1);
loop[l].dst.set(id_thr * ih_thr * sp_thr * nsimd_ic_thr * simd_w, 1);
loop[l].wei.set(kd * kh * kw * nsimd_ic_thr * simd_w * oc, mb_thr);
const auto src_op = static_cast<dim_t>(mb_thr) * id_thr * ih_thr * sp_thr
* kd * kh * kw * oc;
const auto dst_op = static_cast<dim_t>(mb_thr) * id_thr * ih_thr * sp_thr
* nsimd_ic_thr;
wei_op = kd * kh * kw * nsimd_ic_thr * oc;
const auto iterations = bench_iterations;
l++;
loop[l].src.set(src_op, iterations);
loop[l].dst.set(dst_op * simd_w, iterations);
loop[l].wei.set(wei_op * simd_w, iterations);
auto src_mem_k = mem_k;
auto dst_mem_k = mem_k;
auto wei_mem_k = mem_k;
float src_rp = 1;
float dst_rp = 1;
float wei_rp = 1;
for (auto il = l; il >= 0; il--) {
src_mem_k = io_k(loop[il], loop[il].src, src_mem_k, true,
loop_order == loop_ndhwgc ? false : true);
dst_mem_k = io_k(loop[il], loop[il].dst, dst_mem_k, false, false);
wei_mem_k = io_k(loop[il], loop[il].wei, wei_mem_k, false,
loop_order == loop_ndhwgc ? true : false);
src_rp *= loop[il].src.repeatn;
dst_rp *= loop[il].dst.repeatn;
wei_rp *= loop[il].wei.repeatn;
}
const auto src_ops = (src_op * src_rp) / iterations;
const auto dst_ops = (dst_op * dst_rp) / iterations;
const auto wei_ops = (wei_op * wei_rp) / iterations;
const auto src_cost = src_mem_k * src_ops;
const auto dst_cost = dst_mem_k * dst_ops;
const auto wei_cost = wei_mem_k * wei_ops;
const auto call_kernel_cost = job * oc_chunks * nb_kd * nb_kh * nb_kw;
const auto cache_eff = (static_cast<dim_t>(mb) * id * ih * sp * oc * ic)
/ (nthr * (src_cost + dst_cost + wei_cost + call_kernel_cost));
const auto res_eff = ic_block_eff * brgemm_microkernel_eff * sp_eff
* job_eff * ur_eff * cache_eff * brgemm_eff;
return res_eff;
}
void brg_blocking_t::iterate_ker_block(brg_blocking_t &best_brgb, int kd_block_,
int kh_block_, bool maybe_use_buffer, int max_iw_block_thr) {
kd_block = kd_block_;
kh_block = kh_block_;
kw_block = kw;
kd_block_pad = kd_block;
kh_block_pad = kh_block;
kw_block_pad = kw_block;
const auto w_block_size = 2 * src_dsz * oc * owp + dst_dsz * iw * ic_block;
const auto other_size = wei_dsz * kd * kh * kw * oc * ic_block
+ acc_dsz * 2 * amx_h * ic_block;
const auto L2_available = nstl::min(static_cast<size_t>(div_up(L2, 2)),
other_size > L2 ? 0 : L2 - other_size);
if (odp * ohp * w_block_size > L2_available) {
id_block = utils::saturate(
1, id, int(L2_available / (ohp * w_block_size)));
if (id_block == 1)
ih_block = utils::saturate(
1, ih, int(L2_available / (w_block_size)));
else
ih_block = ih;
} else {
id_block = 1;
ih_block = ih;
}
if (is_amx(isa)) {
bool L1_fit_res = false;
auto cur_id_block = id_block;
auto cur_ih_block = ih_block;
const auto src_w_block_size
= src_dsz * oc * owp + dst_dsz * iw * ic_block;
if (src_w_block_size < L1) {
cur_id_block = utils::saturate(
1, id, int(L1 / (ohp * src_w_block_size)));
if (cur_id_block == 1)
cur_ih_block
= utils::saturate(1, ih, int(L1 / (src_w_block_size)));
}
for (; cur_id_block > 1; cur_id_block--) {
const auto sp_size = cur_id_block * cur_ih_block * owp;
if ((static_cast<float>(id) / rnd_up(id, cur_id_block)) > 0.9f
&& static_cast<float>(sp_size) / rnd_up(sp, amx_h) > 0.8f) {
L1_fit_res = true;
break;
}
}
if (cur_id_block == 1) {
for (; cur_ih_block > 1; cur_ih_block--) {
const auto sp_size = cur_ih_block * owp;
if ((static_cast<float>(ih) / rnd_up(ih, cur_ih_block)) > 0.9f
&& sp_size > 128) {
L1_fit_res = true;
break;
}
}
}
if (L1_fit_res) {
id_block = cur_id_block;
ih_block = cur_ih_block;
}
}
const auto thr_ic_block
= div_up(nthr, mb * div_up((ic > 32 ? ngroups : 1) * ic, ic_block));
const auto thr_id_block = div_up(id, thr_ic_block);
const auto thr_ih_block
= div_up(ih, thr_ic_block * div_up(id, thr_id_block));
id_block = nstl::min(id_block, thr_id_block);
ih_block = nstl::min(ih_block, thr_ih_block);
while ((id_block % stride_d != 0
|| (id % stride_d == 0
&& id % id_block
!= 0) )
&& id_block < id)
id_block++;
while ((ih_block % stride_h != 0
|| (ih % stride_h == 0
&& ih % ih_block
!= 0) )
&& ih_block < ih)
ih_block++;
const auto max_iw_block_L2 = iw;
auto start_iw_block = nstl::min(max_iw_block_thr, max_iw_block_L2);
sp = has_uneven_iw ? rnd_up(iw, stride_w) : iw;
const auto start_sp_block
= has_uneven_iw ? rnd_up(start_iw_block, stride_w) : start_iw_block;
auto prev_spb = 0;
for (auto ns = 1; ns <= sp; ns++) {
const auto spb = div_up(sp, ns);
if (spb == prev_spb || spb > start_sp_block) continue;
if (spb % stride_w != 0) continue;
if (!has_uneven_iw && iw % spb != 0) continue;
prev_spb = spb;
iw_block = spb;
sp_block = iw_block;
select_oc_block();
use_buffer = maybe_use_buffer;
const status_t st = estimate_brgemm_ur();
if (st != status::success) continue;
is_block = sp_block = iw_block;
update_blocks();
eff = est_eff();
constexpr float min_eff = 0.00001f;
const bool is_f32 = utils::everyone_is(f32, src_dt, wei_dt, dst_dt);
if ((eff > best_brgb.eff || best_brgb.eff == 0)
&& IMPLICATION(is_f32, eff >= min_eff))
best_brgb = *this;
}
}
status_t brg_blocking_t::calc_blocks() {
sp = has_uneven_iw ? rnd_up(iw, stride_w) : iw;
nb_oc_blocking = 1;
const auto maybe_use_buffer = (dst_dt != acc_dt || with_sum);
std::vector<int> kd_blocks(1), kh_blocks(1);
kd_blocks[0] = kd;
kh_blocks[0] = kh;
if (kd != 1) {
kd_blocks.resize(2);
kd_blocks[1] = stride_d;
}
if (kh != 1) {
kh_blocks.resize(2);
kh_blocks[1] = stride_h;
}
const auto thr_eff_threshold = 0.9f;
const auto max_iw_block_thr = utils::saturate(1, sp,
static_cast<int>(ceil(
mb * ngroups * nb_ic * is / (thr_eff_threshold * nthr))));
iw_block = is_block = sp_block = -1;
brg_blocking_t best_brgb = *this;
for (const auto &kd_block : kd_blocks) {
for (const auto &kh_block : kh_blocks) {
iterate_ker_block(best_brgb, kd_block, kh_block, maybe_use_buffer,
max_iw_block_thr);
}
}
*this = best_brgb;
VDISPATCH_CONV_IC(
sp_block > 0, VERBOSE_BLOCKING_FAIL, "bad blocking parameters");
iw_block = is_block = sp_block;
iw_tail = iw % iw_block;
update_blocks();
return status::success;
}
bool brg_blocking_t::fast_check_ic_block_1x1() const {
if (is_1x1 && is_amx(isa)) return true;
const auto rnd_ic = rnd_up(ic, acc_simd_w);
auto res = false;
if (ic_block == 64) {
const auto big_spatial
= id * ih * iw >= 64 * stride_d * stride_h * stride_w;
res = (rnd_ic % ic_block == 0 && big_spatial);
} else if (ic_block == 48) {
const auto ic_block_eff = static_cast<float>(ic) / rnd_up(ic, ic_block);
res = (ic_block_eff >= 0.95f);
} else
res = true;
return res;
}
float brg_blocking_t::est_eff_1x1() {
const auto icblock = ic_block / acc_simd_w;
auto calc_ave_blk = [&](int dim, int block, bool use_ave) -> float {
const int nb = dim / block;
constexpr int max_nb = 2; const int block2 = nstl::min(max_nb, nb);
const int nb2 = nb / block2;
const int nb2_tail = nb % block2;
if (!use_ave) return block2;
return (float(nb2) * block2 + nb2_tail) / div_up(nb, block2);
};
const bool use_ocb_ave = true;
const auto icb_ave = calc_ave_blk(ic_block, acc_simd_w, use_ocb_ave);
const bool use_spb_ave = false;
const auto spb_ave = calc_ave_blk(sp_block, ur_block, use_spb_ave);
const auto M_n_sp_blks = ur_block > 0 ? nstl::max(M, M_tail) / ur_block : 0;
const auto M_tail_n_sp_blks
= ur_block_tail > 0 ? M_tail / ur_block_tail : 0;
const bool maskrcnn_cond = (ic == 1024 && oc == 2048)
|| (ic == 1024 && oc == 512) || (ic == 256 && oc == 1024)
|| (ic == 512 && oc == 1024) || (ic == 512 && oc == 2048);
const auto amx_fac = maskrcnn_cond
? (div_up(M + M_tail, 16) / (M_n_sp_blks + M_tail_n_sp_blks))
: (static_cast<float>(div_up(M + M_tail, 16))
/ (M_n_sp_blks + M_tail_n_sp_blks));
const auto brgemm_microkernel_eff = is_amx(isa)
? amx_fac * (static_cast<float>(icb_ave) * spb_ave)
/ (icb_ave + spb_ave)
: (static_cast<float>(icblock) * ur) / ((ur + icblock) * max_regs);
const auto ur_eff = static_cast<float>(sp_block) / rnd_up(sp_block, ur);
const auto brgemm_eff = squeeze_val(ur
* (2.f - nstl::min(1.9f, static_cast<float>(ur) / sp_block))
/ 64,
0.5f);
const auto sp_amount = nb_id * nb_ih * nb_sp;
const auto work_amount = mb * ngroups * nb_ic * sp_amount;
const auto sp_eff = static_cast<float>(sp) / rnd_up(sp, sp_block);
const auto thr_eff = static_cast<float>(work_amount)
/ utils::rnd_up(work_amount, nthr);
const auto ic_block_eff = static_cast<float>(ic) / rnd_up(ic, ic_block);
const auto job = div_up(work_amount, nthr);
const auto dim_ic = (loop_order == loop_ndhwgc) ? 1 : sp_amount;
const auto nb_ic_thr = nstl::min(nb_ic, div_up(job, dim_ic));
const auto ic_thr = nstl::min(ic, nb_ic_thr * ic_block);
const auto nsimd_ic_thr = div_up(ic_thr, simd_w);
const auto dim_sp = (loop_order == loop_ndhwgc) ? ngroups * nb_ic : 1;
const auto nb_sp_thr = nstl::min(nb_sp, div_up(job, dim_sp));
const auto sp_thr = nstl::min(sp, nb_sp_thr * sp_block);
const auto dim_ih = nb_sp * dim_sp;
const int nb_ih_thr = nstl::min(nb_ih, div_up(job, dim_ih));
const int ih_thr = nstl::min(ih, nb_ih_thr * ih_block);
const auto dim_id = nb_ih * dim_ih;
const int nb_id_thr = nstl::min(nb_id, div_up(job, dim_id));
const int id_thr = nstl::min(id, nb_id_thr * id_block);
auto job_eff = 1.f;
if (job < nthr) {
std::vector<dim_t> thr_jobs(nthr);
for (int ithr = 0; ithr < nthr; ithr++) {
thr_jobs[ithr] = 0;
if (ithr >= work_amount) continue;
dim_t thr_job = 0;
int start {0}, end {0};
balance211(work_amount, nthr, ithr, start, end);
int n {0}, g {0}, icb {0}, idp {0}, ihp {0}, spb {0};
nd_iterator_init(start, n, mb, idp, id, ihp, ih, spb, nb_sp, g,
ngroups, icb, nb_ic);
if (loop_order == loop_ndhwgc) {
nd_iterator_init(start, n, mb, idp, id, ihp, ih, spb, nb_sp, g,
ngroups, icb, nb_ic);
} else if (loop_order == loop_ngcdhw) {
nd_iterator_init(start, n, mb, g, ngroups, icb, nb_ic, idp, id,
ihp, ih, spb, nb_sp);
}
for (auto work = start; work < end; work++) {
const int icp = icb * ic_block;
const auto ic_sz = nstl::min(ic - icp, ic_block);
int sp_sz = 0;
const int spp = spb * sp_block;
sp_sz = nstl::min(sp - spp, sp_block);
thr_job += sp_sz * ic_sz;
nd_iterator_step(n, mb, idp, id, ihp, ih, spb, nb_sp, g,
ngroups, icb, nb_ic);
if (loop_order == loop_ndhwgc) {
nd_iterator_step(n, mb, idp, id, ihp, ih, spb, nb_sp, g,
ngroups, icb, nb_ic);
} else if (loop_order == loop_ngcdhw) {
nd_iterator_step(n, mb, g, ngroups, icb, nb_ic, idp, id,
ihp, ih, spb, nb_sp);
}
}
thr_jobs[ithr] = thr_job;
}
dim_t max_job = 0;
dim_t sum_job = 0;
for (int ithr = 0; ithr < nthr; ithr++) {
if (thr_jobs[ithr] > max_job) max_job = thr_jobs[ithr];
sum_job += thr_jobs[ithr];
}
job_eff = max_job == 0 ? 1
: static_cast<float>(sum_job) / (max_job * nthr);
} else {
job_eff = thr_eff;
}
const auto oc_blocking_size = oc_block * nb_oc_blocking;
const auto ic_blocking_size = ic_block * oc_blocking_size;
int l = -1;
l++;
loop[l].src.set(ur * simd_w, 1, bcast_simd);
loop[l].dst.set(0, 1);
loop[l].wei.set(ic_block, 1);
l++;
const auto nb_ur = div_up(sp_block, ur);
const auto nb_sp_no_tail = sp / sp_block;
const auto sp_block_tail = sp % sp_block;
const auto nb_ur_average
= (nb_sp_no_tail * nb_ur + div_up(sp_block_tail, ur)) / nb_sp;
loop[l].src.set(ur * rnd_simd(oc_blocking_size), 1);
loop[l].dst.set(ur * ic_block, 1);
loop[l].wei.set(ic_blocking_size, is_amx(isa) ? nb_ur_average : nb_ur);
l++;
const auto oc_chunks = div_up(nb_oc, nb_oc_blocking);
loop[l].src.set(sp_block * oc_blocking_size, 1);
loop[l].dst.set(sp_block * ic_block, oc_chunks);
auto wei_is = ic_blocking_size;
auto wei_op = icblock * oc;
loop[l].wei.set(wei_is, 1);
if (loop_order == loop_ndhwgc) {
l++;
loop[l].src.set(sp_block * rnd_simd(ic), nb_ic_thr);
loop[l].dst.set(sp_block * ic_block, 1);
wei_is = ic_block * ic;
wei_op = nsimd_ic_thr * ic;
loop[l].wei.set(wei_is, 1);
}
const auto rnd_ic_for_sp
= simd_w * ((loop_order == loop_ndhwgc) ? nsimd_ic_thr : icblock);
l++;
loop[l].src.set(sp_block * oc_blocking_size, 1);
loop[l].dst.set(sp_block * rnd_ic_for_sp, 1);
loop[l].wei.set(wei_op * simd_w, nb_sp_thr);
l++;
loop[l].src.set(ih_block * sp_thr * rnd_simd(oc_blocking_size), 1);
loop[l].dst.set(ih_block * sp_thr * rnd_ic_for_sp, 1);
loop[l].wei.set(wei_op * simd_w, nb_ih_thr);
l++;
loop[l].src.set(id_block * ih_thr * sp_thr * rnd_simd(oc_blocking_size), 1);
loop[l].dst.set(id_block * ih_thr * sp_thr * rnd_ic_for_sp, 1);
loop[l].wei.set(wei_op * simd_w, nb_id_thr);
if (loop_order != loop_ndhwgc) {
l++;
loop[l].src.set(id_thr * ih_thr * rnd_simd(sp_thr * oc_blocking_size),
nb_ic_thr);
loop[l].dst.set(ic_block * id_thr * ih_thr * sp_thr, 1);
loop[l].wei.set(ic_block * oc, 1);
}
l++;
const auto mb_thr = nstl::min(mb, div_up(job, sp_amount * ngroups * nb_ic));
loop[l].src.set(id_thr * ih_thr * sp_thr * rnd_simd(oc_blocking_size), 1);
loop[l].dst.set(nsimd_ic_thr * simd_w * id_thr * ih_thr * sp_thr, 1);
loop[l].wei.set(nsimd_ic_thr * oc * simd_w, mb_thr);
const auto src_op = static_cast<dim_t>(mb_thr) * id_thr * ih_thr * sp_thr
* oc_blocking_size;
const auto dst_op = static_cast<dim_t>(mb_thr) * nsimd_ic_thr * id_thr
* ih_thr * sp_thr;
wei_op = nsimd_ic_thr * oc;
const auto iterations = bench_iterations;
l++;
loop[l].src.set(src_op, iterations);
loop[l].dst.set(dst_op * simd_w, iterations);
loop[l].wei.set(wei_op * simd_w, iterations);
auto src_mem_k = mem_k;
auto dst_mem_k = mem_k;
auto wei_mem_k = mem_k;
float src_rp = 1;
float dst_rp = 1;
float wei_rp = 1;
for (auto il = l; il >= 0; il--) {
src_mem_k = io_k(loop[il], loop[il].src, src_mem_k, true, false);
dst_mem_k = io_k(loop[il], loop[il].dst, dst_mem_k, false, false);
wei_mem_k = io_k(loop[il], loop[il].wei, wei_mem_k, false, true);
src_rp *= loop[il].src.repeatn;
dst_rp *= loop[il].dst.repeatn;
wei_rp *= loop[il].wei.repeatn;
}
const auto src_ops = (src_op * src_rp) / iterations;
const auto dst_ops = (dst_op * dst_rp) / iterations;
const auto wei_ops = (wei_op * wei_rp) / iterations;
const auto src_cost = src_mem_k * src_ops;
const auto dst_cost = dst_mem_k * dst_ops;
const auto wei_cost = wei_mem_k * wei_ops;
const auto call_kernel_cost = job * oc_chunks;
const auto up_sp_size = id * ih;
const auto cache_eff = (static_cast<dim_t>(mb) * up_sp_size * sp * oc * ic)
/ (nthr * (src_cost + dst_cost + wei_cost + call_kernel_cost));
const auto res_eff = ic_block_eff * brgemm_microkernel_eff * sp_eff
* job_eff * ur_eff * cache_eff * brgemm_eff;
return res_eff;
}
brgemm_broadcast_t get_zp_type(const primitive_attr_t &attr, int arg) {
return attr.zero_points_.has_default_values(arg)
? brgemm_broadcast_t::none
: brgemm_broadcast_t::per_tensor;
}
status_t init_jcp(jit_brgemm_conv_conf_t &jcp, cpu_isa_t isa,
const convolution_desc_t &cd, memory_desc_t &diff_dst_md,
memory_desc_t &weights_md, memory_desc_t &diff_src_md,
memory_desc_t &bias_md, primitive_attr_t &attr, int nthreads) {
using namespace prop_kind;
brg_blocking_t::L1 = platform::get_per_core_cache_size(1);
brg_blocking_t::L2 = platform::get_per_core_cache_size(2);
const memory_desc_wrapper diff_dst_d(&diff_dst_md);
const memory_desc_wrapper weights_d(&weights_md);
const memory_desc_wrapper diff_src_d(&diff_src_md);
const memory_desc_wrapper bias_d(&bias_md);
VDISPATCH_CONV_IC(!has_large_size(cd, diff_src_d, weights_d, diff_dst_d),
VERBOSE_BAD_PARAM, "large size is not supported");
const bool with_groups = weights_d.ndims() == diff_src_d.ndims() + 1;
int ndims = diff_src_d.ndims();
jcp = zero<decltype(jcp)>();
jcp.isa = isa;
jcp.ndims = ndims;
jcp.prop_kind = cd.prop_kind;
jcp.ngroups = with_groups ? weights_d.dims()[0] : 1;
jcp.mb = diff_src_d.dims()[0];
jcp.oc_without_padding = diff_dst_d.dims()[1] / jcp.ngroups;
jcp.oc = jcp.oc_without_padding;
jcp.ic_without_padding = diff_src_d.dims()[1];
jcp.ic = jcp.ic_without_padding / jcp.ngroups;
jcp.id = (ndims == 5) ? diff_src_d.dims()[2] : 1;
jcp.ih = (ndims == 3) ? 1 : diff_src_d.dims()[ndims - 2];
jcp.iw = diff_src_d.dims()[ndims - 1];
jcp.od = (ndims == 5) ? diff_dst_d.dims()[2] : 1;
jcp.oh = (ndims == 3) ? 1 : diff_dst_d.dims()[ndims - 2];
jcp.ow = diff_dst_d.dims()[ndims - 1];
jcp.kd = (ndims == 5) ? weights_d.dims()[with_groups + 2] : 1;
jcp.kh = (ndims == 3) ? 1 : weights_d.dims()[with_groups + ndims - 2];
jcp.kw = weights_d.dims()[with_groups + ndims - 1];
jcp.f_pad = (ndims == 5) ? cd.padding[0][0] : 0;
jcp.t_pad = (ndims == 3) ? 0 : cd.padding[0][ndims - 4];
jcp.l_pad = cd.padding[0][ndims - 3];
jcp.stride_d = (ndims == 5) ? cd.strides[0] : 1;
jcp.stride_h = (ndims == 3) ? 1 : cd.strides[ndims - 4];
jcp.stride_w = cd.strides[ndims - 3];
VDISPATCH_CONV_IC(!everyone_is(1, jcp.stride_d, jcp.stride_h, jcp.stride_w),
VERBOSE_UNSUPPORTED_FEATURE, "unit strides are not supported");
jcp.has_uneven_iw = jcp.iw % jcp.stride_w != 0;
const bool has_uneven_spatial = jcp.id % jcp.stride_d != 0
|| jcp.ih % jcp.stride_h != 0 || jcp.has_uneven_iw;
bool is_deconv_with_uneven_spatial = cd.use_inversion && has_uneven_spatial;
VDISPATCH_CONV_IC(!is_deconv_with_uneven_spatial,
VERBOSE_UNSUPPORTED_FEATURE,
"deconvolution with uneven spatial dimensions is not supported");
jcp.dilate_d = (ndims == 5) ? cd.dilates[0] : 0;
jcp.dilate_h = (ndims == 3) ? 0 : cd.dilates[ndims - 4];
jcp.dilate_w = cd.dilates[ndims - 3];
VDISPATCH_CONV_IC(everyone_is(0, jcp.dilate_d, jcp.dilate_h, jcp.dilate_w),
VERBOSE_UNSUPPORTED_FEATURE, "non-zero dilations are detected");
jcp.is = jcp.id * jcp.ih * jcp.iw;
jcp.ext_kd = calculate_extended_filter_size(jcp.kd, jcp.dilate_d);
jcp.ext_kh = calculate_extended_filter_size(jcp.kh, jcp.dilate_h);
jcp.ext_kw = calculate_extended_filter_size(jcp.kw, jcp.dilate_w);
jcp.back_pad = calculate_end_padding(
jcp.f_pad, jcp.od, jcp.id, jcp.stride_d, jcp.ext_kd);
jcp.b_pad = calculate_end_padding(
jcp.t_pad, jcp.oh, jcp.ih, jcp.stride_h, jcp.ext_kh);
jcp.r_pad = calculate_end_padding(
jcp.l_pad, jcp.ow, jcp.iw, jcp.stride_w, jcp.ext_kw);
jcp.is_1x1 = jcp.f_pad <= 0 && jcp.back_pad <= 0 && jcp.t_pad <= 0
&& jcp.b_pad <= 0 && jcp.l_pad <= 0 && jcp.r_pad <= 0
&& utils::everyone_is(1, jcp.kd, jcp.kh, jcp.kw);
jcp.with_bias = cd.bias_desc.format_kind != format_kind::undef;
jcp.src_dt = diff_dst_md.data_type;
jcp.dst_dt = diff_src_md.data_type;
jcp.wei_dt = weights_md.data_type;
jcp.bia_dt = jcp.with_bias ? bias_md.data_type : data_type::undef;
jcp.acc_dt = types::is_integral_dt(jcp.src_dt) ? s32 : f32;
jcp.src_dsz = types::data_type_size(jcp.src_dt);
jcp.wei_dsz = types::data_type_size(jcp.wei_dt);
jcp.dst_dsz = types::data_type_size(jcp.dst_dt);
jcp.acc_dsz = types::data_type_size(jcp.acc_dt);
jcp.bia_dsz = jcp.with_bias ? types::data_type_size(jcp.bia_dt) : 0;
jcp.simd_w = isa_max_vlen(isa) / jcp.src_dsz;
jcp.acc_simd_w = isa_max_vlen(isa) / jcp.acc_dsz;
jcp.is_bf32 = everyone_is(f32, jcp.src_dt, jcp.wei_dt)
&& one_of(attr.fpmath_.mode_, fpmath_mode::bf16, fpmath_mode::any)
&& isa == avx512_core_amx;
jcp.is_f32_bf16
= everyone_is(f32, jcp.src_dt, jcp.dst_dt) && jcp.wei_dt == bf16;
jcp.is_f32_f16
= everyone_is(f32, jcp.src_dt, jcp.dst_dt) && jcp.wei_dt == f16;
jcp.is_tf32 = everyone_is(f32, jcp.src_dt, jcp.wei_dt)
&& one_of(attr.fpmath_.mode_, fpmath_mode::tf32, fpmath_mode::any)
&& is_superset(isa, avx10_2_amx_2);
VDISPATCH_CONV_IC(!jcp.is_bf32, VERBOSE_UNSUPPORTED_DT);
const auto wei_dt
= jcp.is_f32_f16 || jcp.is_f32_bf16 ? jcp.src_dt : jcp.wei_dt;
const data_type_t last_oc_block_dt
= get_mac_emu_data_type(wei_dt, isa, isa == avx512_core_fp16);
jcp.vnni_block = data_type_vnni_granularity(last_oc_block_dt);
const bool is_grouped_small_oc
= jcp.prop_kind != prop_kind::backward_weights && with_groups
&& jcp.ngroups > 1 && jcp.oc <= jcp.acc_simd_w
&& IMPLICATION(is_amx(jcp.isa),
jcp.oc < 16
&& jcp.ic < 16
&& !jcp.is_1x1)
&& IMPLICATION(with_groups, is_groups_ok(jcp));
VDISPATCH_CONV_IC(!is_grouped_small_oc, VERBOSE_UNSUPPORTED_FEATURE,
"grouped convolutions with small oc");
const auto max_small_shapes_sz = jcp.is_1x1
? static_cast<int32_t>(brg_blocking_t::L1) / 2
: static_cast<int32_t>(brg_blocking_t::L1);
const auto is_small_shape = is_amx(jcp.isa) && jcp.is <= 4 && jcp.oc <= 512
&& jcp.mb * jcp.ngroups * jcp.oc * jcp.ic <= max_small_shapes_sz;
const auto is_3d_small_oc = is_amx(jcp.isa) && jcp.ndims == 5
&& jcp.oc * jcp.ic <= 32 && jcp.id >= 128 && jcp.ih >= 128
&& jcp.iw >= 128;
VDISPATCH_CONV_IC(!(is_small_shape || is_3d_small_oc),
VERBOSE_UNSUPPORTED_FEATURE, "small 3d shapes with small oc");
const bool is_f32
= utils::everyone_is(f32, jcp.src_dt, jcp.wei_dt, jcp.dst_dt);
const auto is_regression_shape = jcp.id == 1 && jcp.od == 1
&& ((jcp.ic == 128 && jcp.oc == 256 && jcp.ih == 101 && jcp.oh == 49
&& jcp.iw == 85 && jcp.ow == 41)
|| (jcp.ic == 3 && jcp.oc == 128 && jcp.ih == 207
&& jcp.oh == 101 && jcp.iw == 175 && jcp.ow == 85)
|| (jcp.ic == 512 && jcp.oc == 1024
&& everyone_is(8, jcp.ih, jcp.iw)
&& everyone_is(4, jcp.oh, jcp.ow)
&& everyone_is(4, jcp.kh, jcp.kw)
&& everyone_is(2, jcp.stride_h, jcp.stride_w))
|| (jcp.ic == 1024 && jcp.oc == 2048
&& everyone_is(4, jcp.ih, jcp.iw)
&& everyone_is(2, jcp.oh, jcp.ow)
&& everyone_is(4, jcp.kh, jcp.kw)
&& everyone_is(2, jcp.stride_h, jcp.stride_w))
|| (jcp.ic == 64 && jcp.oc == 128
&& everyone_is(513, jcp.ih, jcp.iw)
&& everyone_is(256, jcp.oh, jcp.ow)
&& everyone_is(3, jcp.kh, jcp.kw)
&& everyone_is(2, jcp.stride_h, jcp.stride_w))
|| (jcp.ic == 32 && jcp.oc == 64
&& everyone_is(1025, jcp.ih, jcp.iw)
&& everyone_is(512, jcp.oh, jcp.ow)
&& everyone_is(3, jcp.kh, jcp.kw)
&& everyone_is(2, jcp.stride_h, jcp.stride_w))
|| (jcp.ic == 128 && jcp.oc == 256
&& everyone_is(257, jcp.ih, jcp.iw)
&& everyone_is(128, jcp.oh, jcp.ow)
&& everyone_is(3, jcp.kh, jcp.kw)
&& everyone_is(2, jcp.stride_h, jcp.stride_w))
|| (jcp.ic == 256 && jcp.oc == 512 && jcp.ih == 49
&& jcp.iw == 41 && jcp.oh == 23 && jcp.ow == 19
&& everyone_is(5, jcp.kh, jcp.kw)
&& everyone_is(2, jcp.stride_h, jcp.stride_w))
|| (jcp.ic == 64 && jcp.oc == 128
&& everyone_is(14, jcp.ih, jcp.iw)
&& everyone_is(7, jcp.oh, jcp.ow)
&& everyone_is(4, jcp.kh, jcp.kw)
&& everyone_is(2, jcp.stride_h, jcp.stride_w))
|| (jcp.ic == 1 && jcp.oc == 64
&& everyone_is(28, jcp.ih, jcp.iw)
&& everyone_is(14, jcp.oh, jcp.ow)
&& everyone_is(4, jcp.kh, jcp.kw)
&& everyone_is(2, jcp.stride_h, jcp.stride_w)));
VDISPATCH_CONV_IC(!(is_f32 && is_regression_shape),
"implementation skipped due to low performance");
const bool is_signed_input = jcp.src_dt == s8;
jcp.s8s8_compensation_required = is_signed_input && !isa_has_s8s8(jcp.isa);
jcp.has_int8_vnni = isa_has_int8_vnni(jcp.isa);
VDISPATCH_CONV_IC(
IMPLICATION(jcp.wei_dt == s8,
is_superset(jcp.isa, avx512_core)
|| one_of(jcp.isa, avx2_vnni, avx2_vnni_2)),
VERBOSE_ISA_DT_MISMATCH);
VDISPATCH_CONV_IC(IMPLICATION(jcp.wei_dt == bf16 && !jcp.is_f32_bf16,
is_superset(jcp.isa, avx512_core_bf16)
|| is_superset(jcp.isa, avx2_vnni_2)),
VERBOSE_ISA_DT_MISMATCH);
VDISPATCH_CONV_IC(IMPLICATION(jcp.wei_dt == f16 && !jcp.is_f32_f16,
is_superset(jcp.isa, avx512_core_fp16)
|| is_superset(jcp.isa, avx2_vnni_2)),
VERBOSE_ISA_DT_MISMATCH);
VDISPATCH_CONV_IC(
IMPLICATION(is_f32, one_of(isa, avx512_core, avx2) || jcp.is_tf32),
VERBOSE_ISA_DT_MISMATCH);
VDISPATCH_CONV_IC(
IMPLICATION(jcp.is_f32_bf16, one_of(isa, avx512_core, avx2)),
VERBOSE_ISA_DT_MISMATCH);
VDISPATCH_CONV_IC(
IMPLICATION(jcp.is_f32_f16, one_of(isa, avx512_core, avx2)),
VERBOSE_ISA_DT_MISMATCH);
jcp.amx_h = 16;
jcp.amx_w = 64 / jcp.src_dsz;
if (jcp.with_bias) {
if (bias_d.format_kind() == format_kind::any)
CHECK(memory_desc_init_by_tag(bias_md, x));
}
const auto &p = attr.post_ops_;
jcp.with_sum = p.find(primitive_kind::sum) != -1;
const int eltwise_ind = p.find(primitive_kind::eltwise);
jcp.with_eltwise = eltwise_ind != -1;
const int binary_ind = p.find(primitive_kind::binary);
const int prelu_ind = p.find(primitive_kind::prelu);
jcp.with_binary = !everyone_is(-1, binary_ind, prelu_ind);
jcp.src_zero_point
= get_zp_type(
attr, cd.use_inversion ? DNNL_ARG_SRC : DNNL_ARG_DIFF_DST)
!= brgemm_broadcast_t::none;
jcp.dst_zero_point
= get_zp_type(
attr, cd.use_inversion ? DNNL_ARG_DST : DNNL_ARG_DIFF_SRC)
!= brgemm_broadcast_t::none;
const auto &zp = attr.zero_points_;
VDISPATCH_CONV_IC(IMPLICATION(jcp.src_zero_point || jcp.dst_zero_point,
utils::one_of(jcp.src_dt, s8, u8)),
VERBOSE_UNSUPPORTED_ZP_CFG);
VDISPATCH_CONV_IC(IMPLICATION(jcp.src_zero_point,
zp.get_mask(cd.use_inversion ? DNNL_ARG_SRC
: DNNL_ARG_DIFF_DST)
== 0),
VERBOSE_UNSUPPORTED_ZP_CFG);
VDISPATCH_CONV_IC(IMPLICATION(jcp.dst_zero_point,
zp.get_mask(cd.use_inversion ? DNNL_ARG_DST
: DNNL_ARG_DIFF_SRC)
== 0),
VERBOSE_UNSUPPORTED_ZP_CFG);
jcp.nthr = nthreads;
jcp.copy_block_only = false;
jcp.amx_tile_load_xx = false;
jcp.use_M_mask = 0;
jcp.is_is_blocking = false;
jcp.oskip = 0;
jcp.use_uker = false;
jcp.use_interleave_stores = false;
jcp.hint_prefetching = brgemm_kernel_prefetching_t::brgemm_prf_default;
jcp.brgemm_bd_loop_innermost = false;
format_tag_t src_tag = pick(jcp.ndims - 3, nwc, nhwc, ndhwc);
CHECK(init_tag(jcp.src_tag, diff_dst_md, diff_dst_d, src_tag));
CHECK(init_tag(jcp.dst_tag, diff_src_md, diff_src_d, src_tag));
CHECK(attr.set_default_formats(&diff_src_md));
VDISPATCH_CONV_IC(post_ops_ok(jcp, attr, diff_src_d, cd.use_inversion),
VERBOSE_UNSUPPORTED_POSTOP);
return status::success;
}
void set_k_range(int P, int D, int S, dim_t i, dim_t O, int K, int &k_s,
int &k_f, bool is_w) {
int s(0), o_test(0);
while (true) {
o_test = i + P - s * D;
if (o_test % S == 0) break;
s++;
}
k_f = is_w ? K : nstl::min(K, static_cast<int>(div_up(i + P + 1, D)));
k_s = is_w ? 0
: static_cast<int>(
div_up(nstl::max((dim_t)0, i + P - O * S + 1), D));
while (k_s % S != s)
k_s++;
}
void get_iw_range(const jit_brgemm_conv_conf_t &jcp, int iw, int iw_raw, int kw,
int &iw_s, int &M_without_overflow) {
using namespace dnnl::impl::utils;
using namespace nstl;
const auto SW = jcp.stride_w;
const auto LP = jcp.l_pad;
const auto DW = jcp.dilate_w + 1;
const auto IW = jcp.iw;
const auto OW = jcp.ow;
const auto IW_BLOCK = jcp.iw_block;
const auto IW_TAIL = jcp.iw_block;
const bool is_iw_tail = (IW - iw_raw < IW_BLOCK);
const auto M = div_up(is_iw_tail ? IW_TAIL : IW_BLOCK, SW);
auto ow = (iw - kw * DW + LP) / SW;
auto ow_l_ovf = ow;
const auto ow_r_ovf = ow_l_ovf + (M - 1) - OW + 1;
iw_s = iw;
int ker_idx = 0;
if (ow_l_ovf < 0) {
ow_l_ovf = nstl::abs(ow_l_ovf);
ker_idx += ow_l_ovf;
iw_s += ker_idx;
}
if (ow_r_ovf > 0) ker_idx += ow_r_ovf;
int iw_f = iw_s + (M - ker_idx);
iw_s = nstl::min(iw_s, iw + M);
iw_f = nstl::min(nstl::max(iw_f, iw_s), iw + M);
M_without_overflow = iw_f - iw_s;
iw_s = iw;
ow = (iw_s - kw * DW + LP) / SW;
while (ow < 0 && iw_s + SW < IW) {
iw_s += SW;
ow = (iw_s - kw * DW + LP) / SW;
}
}
void get_kw_range(const jit_brgemm_conv_conf_t &jcp, int iw, int iw_raw,
int &kw_s, int &kw_full_s, int &kw_full_f, int &kw_f) {
using namespace dnnl::impl::utils;
const auto SW = jcp.stride_w;
const auto KW = jcp.kw;
const auto LP = jcp.l_pad;
const auto DW = jcp.dilate_w + 1;
const auto IW = jcp.iw;
const auto IW_BLOCK = jcp.iw_block;
const auto IW_TAIL = jcp.iw_tail;
const bool is_iw_tail = (IW - iw_raw < IW_BLOCK);
const auto M = div_up(is_iw_tail ? IW_TAIL : IW_BLOCK, SW);
kw_s = kw_full_s = kw_full_f = kw_f = -1;
for (int kw = 0; kw < KW; kw++) {
int iw_s {0}, iw_f {0}, M_without_overflow {0};
get_iw_range(jcp, iw, iw_raw, kw, iw_s, M_without_overflow);
iw_f = iw_s + M_without_overflow;
if (iw_s < iw_f) {
if (kw_s == -1) kw_s = kw;
kw_f = kw + 1;
if (iw_f - iw_s == M) {
if (kw_full_s == -1) kw_full_s = kw;
kw_full_f = kw + 1;
}
}
}
if (kw_f == -1) {
kw_s = 0;
kw_f = 0;
}
if (kw_full_f == -1) kw_full_s = kw_full_f = kw_f;
int s(0), o_test(0);
while (true) {
o_test = iw + LP - s * DW;
if (o_test % SW == 0) break;
s++;
}
while (kw_s % SW != s)
kw_s++;
if (kw_full_s != -1) {
while (kw_full_s % SW != s)
kw_full_s++;
}
}
dim_t precalculate_comp_pad_kernels(const jit_brgemm_conv_conf_t &jcp,
std::vector<dim_t> *kd_bs, std::vector<dim_t> *kd_es,
std::vector<dim_t> *kh_bs, std::vector<dim_t> *kh_es,
std::vector<dim_t> *kw_bs, std::vector<dim_t> *kw_es) {
using namespace dnnl::impl::utils;
using namespace nstl;
#define ndims_pick(v5, v4, v3) \
((ndims == 5) ? (v5) : (ndims == 4) ? (v4) : (ndims == 3) ? (v3) : 0)
const auto ndims = jcp.ndims;
const auto KD = jcp.kd;
const auto KH = jcp.kh;
const auto KW = jcp.kw;
const auto KW_BLOCK = jcp.kw_block;
const auto ID = ndims_pick(jcp.id, 1, 1);
const auto ID_BLOCK = jcp.id_block;
const auto IH = ndims_pick(jcp.ih, jcp.ih, 1);
const auto IH_BLOCK = jcp.ih_block;
const auto IW_BLOCK = jcp.iw_block;
const auto OD = ndims_pick(jcp.od, 1, 1);
const auto OH = ndims_pick(jcp.oh, jcp.oh, 1);
const auto SD = ndims_pick(jcp.stride_d, 1, 1);
const auto SH = ndims_pick(jcp.stride_h, jcp.stride_h, 1);
const auto SW = jcp.stride_w;
const auto FP = ndims_pick(jcp.f_pad, 0, 0);
const auto TP = ndims_pick(jcp.t_pad, jcp.t_pad, 0);
const auto DD = ndims_pick(jcp.dilate_d, 0, 0) + 1;
const auto DH = ndims_pick(jcp.dilate_h, jcp.dilate_h, 0) + 1;
const bool fill_k_ranges
= !any_null(kd_bs, kd_es, kh_bs, kh_es, kw_bs, kw_es);
std::set<std::vector<int>> unique_kernels;
dim_t k = 0;
if (fill_k_ranges) {
kd_bs->resize(jcp.ker_ranges_size);
kd_es->resize(jcp.ker_ranges_size);
kh_bs->resize(jcp.ker_ranges_size);
kh_es->resize(jcp.ker_ranges_size);
kw_bs->resize(jcp.ker_ranges_size);
kw_es->resize(jcp.ker_ranges_size);
}
const auto update_kernels
= [&](int kd_b, int kd_e, int kh_b, int kh_e, int kw_b, int kw_e) {
unique_kernels.insert({kd_b, kd_e, kh_b, kh_e, kw_b, kw_e});
if (k == static_cast<dim_t>(unique_kernels.size())) return;
if (fill_k_ranges) {
(*kd_bs)[k] = kd_b;
(*kd_es)[k] = kd_e;
(*kh_bs)[k] = kh_b;
(*kh_es)[k] = kh_e;
(*kw_bs)[k] = kw_b;
(*kw_es)[k] = kw_e;
}
k++;
assert(IMPLICATION(fill_k_ranges, k <= jcp.ker_ranges_size));
};
for_(int idb = 0; idb < jcp.nb_id; idb++)
for_(int ihb = 0; ihb < jcp.nb_ih; ihb++)
for (int iwb = 0; iwb < jcp.nb_iw; iwb++) {
auto id_begin = idb * ID_BLOCK;
auto id_end = nstl::min(ID, id_begin + ID_BLOCK);
auto ih_begin = ihb * IH_BLOCK;
auto ih_end = jcp.is_is_blocking ? ih_begin + 1
: nstl::min(IH, ih_begin + IH_BLOCK);
for_(int id = id_begin; id < id_end; id++)
for_(int ih = ih_begin; ih < ih_end; ih++)
for (int sw = 0; sw < SW; sw++) {
const int iw = iwb * IW_BLOCK + sw;
const int iw_raw = iwb * IW_BLOCK;
int kw_s {0}, kw_full_s {0}, kw_f {0}, kw_full_f {0};
int kd_s_(0), kh_s_(0), kd_f_(0), kh_f_(0);
get_kw_range(jcp, iw, iw_raw, kw_s, kw_full_s, kw_full_f, kw_f);
set_k_range(FP, DD, SD, id, OD, KD, kd_s_, kd_f_);
set_k_range(TP, DH, SH, ih, OH, KH, kh_s_, kh_f_);
const auto kh_f = ndims_pick(kh_f_, kh_f_, 1);
const auto kh_s = ndims_pick(kh_s_, kh_s_, 0);
const auto kd_f = ndims_pick(kd_f_, 1, 1);
const auto kd_s = ndims_pick(kd_s_, 0, 0);
if (kd_f > kd_s && kh_f > kh_s && kw_f > kw_s && kw_s < KW) {
if (jcp.exec_type == exec_base) {
if (kw_s < kw_full_s) {
for (auto kw = kw_s; kw < kw_full_s; kw += SW) {
update_kernels(kd_s, kd_f, kh_s, kh_f, kw, kw + 1);
}
}
if (kw_full_s < kw_full_f) {
for (auto kw = kw_full_s; kw < kw_full_f;
kw += KW_BLOCK) {
const auto kw_e
= nstl::min(kw_full_f, kw + KW_BLOCK);
update_kernels(kd_s, kd_f, kh_s, kh_f, kw, kw_e);
}
}
if (kw_full_f < kw_f) {
for (auto kw = kw_full_f; kw < kw_f; kw += SW) {
update_kernels(kd_s, kd_f, kh_s, kh_f, kw, kw + 1);
}
}
} else
update_kernels(kd_s, kd_f, kh_s, kh_f, 0, KW);
} else if (jcp.exec_type == exec_trans && is_amx(jcp.isa))
update_kernels(0, 0, 0, 0, 0, 0);
}
}
return k;
#undef ndims_pick
}
status_t init_conf(jit_brgemm_conv_conf_t &jcp, cpu_isa_t isa,
const convolution_desc_t &cd, memory_desc_t &diff_dst_md,
memory_desc_t &weights_md, memory_desc_t &diff_src_md,
memory_desc_t &bias_md, primitive_attr_t &attr, int nthreads) {
using namespace prop_kind;
if (!mayiuse(isa)) return status::unimplemented;
CHECK(init_jcp(jcp, isa, cd, diff_dst_md, weights_md, diff_src_md, bias_md,
attr, nthreads));
const memory_desc_wrapper diff_dst_d(&diff_dst_md);
const memory_desc_wrapper weights_d(&weights_md);
const memory_desc_wrapper diff_src_d(&diff_src_md);
const memory_desc_wrapper bias_d(&bias_md);
jcp.l_ovf = nstl::max(0, jcp.ext_kw - 1 - jcp.l_pad) / jcp.stride_w;
jcp.r_ovf = nstl::max(0, jcp.ext_kw - 1 - jcp.r_pad) / jcp.stride_w;
jcp.t_ovf = nstl::max(0, jcp.ext_kh - 1 - jcp.t_pad) / jcp.stride_h;
jcp.b_ovf = nstl::max(0, jcp.ext_kh - 1 - jcp.b_pad) / jcp.stride_h;
jcp.f_ovf = nstl::max(0, jcp.ext_kd - 1 - jcp.f_pad) / jcp.stride_d;
jcp.back_ovf = nstl::max(0, jcp.kd - 1 - jcp.back_pad) / jcp.stride_d;
jcp.odp = jcp.od + jcp.f_ovf + jcp.back_ovf;
jcp.ohp = jcp.oh + jcp.t_ovf + jcp.b_ovf;
jcp.owp = jcp.ow + jcp.l_ovf + jcp.r_ovf;
using namespace data_type;
const int min_ic_block = jcp.acc_simd_w;
int selected_ur = 0;
jcp.exec_type = (is_amx(isa) || jcp.has_uneven_iw) ? exec_trans : exec_base;
jcp.brg_type = brgemm_addr;
jcp.max_batch = jcp.kd * jcp.kh * jcp.kw;
jcp.wei_plain = false;
auto bcast_amount
= static_cast<size_t>(jcp.od) * jcp.oh * jcp.ow * jcp.src_dsz;
auto wei_amount = static_cast<size_t>(jcp.ic) * jcp.kd * jcp.kh * jcp.kw
* jcp.wei_dsz;
jcp.loop_order = (bcast_amount < wei_amount && jcp.exec_type != exec_trans)
? loop_ngcdhw
: loop_ndhwgc;
jcp.copy_block_only = true;
const auto oc_padded_block = jcp.acc_simd_w * jcp.vnni_block;
jcp.is_oc_padded = one_of(jcp.wei_dt, bf16, f16, s8)
&& jcp.oc > oc_padded_block && is_amx(isa);
if (is_amx(isa) && jcp.iw < 256) {
jcp.use_M_mask = 0;
jcp.hint_prefetching = brgemm_kernel_prefetching_t::brgemm_prf0;
const auto bd_blocking = 2 * jcp.amx_h;
const auto ld_blocking = 2 * 16;
const auto A_ds = jcp.src_dsz * bd_blocking * jcp.oc * jcp.kd * jcp.kh;
const auto B_ds
= jcp.wei_dsz * ld_blocking * jcp.oc * jcp.kd * jcp.kh * jcp.kw;
const auto C_ds = jcp.acc_dsz * bd_blocking * ld_blocking;
if (A_ds + B_ds + C_ds > brg_blocking_t::L1)
jcp.amx_tile_load_xx = true;
}
auto try_exec_type = [&]() {
brg_blocking_t best_brgb = zero<decltype(best_brgb)>();
best_brgb.ic_block = min_ic_block;
brg_blocking_t cur_brgb = zero<decltype(best_brgb)>();
cur_brgb.get_from_jcp(jcp);
const auto start_icb = nstl::min(div_up(jcp.ic, jcp.acc_simd_w), 4);
auto finish_icb = 1;
for (auto icb = start_icb; icb >= finish_icb; icb--) {
cur_brgb.ic_block = icb * jcp.acc_simd_w;
cur_brgb.nb_ic = utils::div_up(jcp.ic, cur_brgb.ic_block);
if (!cur_brgb.fast_check_ic_block()) continue;
const status_t blocking_ok = cur_brgb.calc_blocks();
if (blocking_ok != status::success) continue;
const status_t st = cur_brgb.get_brgemm_ur(&attr, diff_src_md);
if (st != status::success) continue;
cur_brgb.eff = cur_brgb.est_eff();
if (cur_brgb.eff > best_brgb.eff) best_brgb = cur_brgb;
}
if (best_brgb.oc_block == 0 || best_brgb.ic_block == 0
|| best_brgb.iw_block == 0)
return false;
best_brgb.save_to_jcp(jcp);
selected_ur = best_brgb.ur;
return true;
};
if (!try_exec_type()) return status::unimplemented;
jcp.max_vpad = 0;
VDISPATCH_CONV_IC(
!(jcp.iw_block == 0 || jcp.oc_block == 0 || jcp.ic_block == 0),
VERBOSE_BLOCKING_FAIL, "bad blocking dimensions");
jcp.gemm_batch_size = jcp.nb_oc_blocking
* nstl::max(jcp.kd_block * jcp.kh_block * jcp.kw_block,
jcp.kd_block_pad * jcp.kh_block_pad * jcp.kw_block_pad);
size_t sc_size = sizeof(brgemm_batch_element_t);
jcp.adjusted_batch_size
= div_up(rnd_up(jcp.gemm_batch_size * sc_size, P4K), sc_size);
CHECK(pick_tags(jcp, diff_dst_md, weights_md, diff_src_md, bias_md));
jcp.buffer_size = jcp.LDC * (jcp.M > 0 ? jcp.M : jcp.M_tail);
jcp.nb_id = div_up(jcp.id, jcp.id_block);
jcp.nb_ih = div_up(jcp.ih, jcp.ih_block);
jcp.inp_buffer_size = rnd_up(jcp.odp * jcp.ohp * jcp.owp * jcp.ngroups
* jcp.nb_oc * jcp.oc_block,
P4K);
jcp.inp_buffer_mask_size = rnd_up(static_cast<dim_t>(jcp.nb_id) * jcp.nb_ih
* jcp.nb_iw * jcp.ngroups * jcp.nb_oc,
P4K);
jcp.out_buffer_size
= rnd_up(jcp.iw_block * jcp.stride_w * jcp.ic_without_padding, P4K);
const bool scale_adjust_required
= jcp.s8s8_compensation_required && !jcp.has_int8_vnni;
if (scale_adjust_required) weights_md.extra.scale_adjust = 0.5f;
jcp.scale_adjust_factor = (scale_adjust_required)
? 1 / weights_md.extra.scale_adjust
: 1.0f;
if (cd.use_inversion) {
const auto &src_scales = attr.scales_.get(DNNL_ARG_SRC);
const auto &wei_scales = attr.scales_.get(DNNL_ARG_WEIGHTS);
const auto &dst_scales = attr.scales_.get(DNNL_ARG_DST);
jcp.with_src_scales = !src_scales.has_default_values();
jcp.with_wei_scales = !wei_scales.has_default_values()
|| jcp.scale_adjust_factor != 1.0f;
jcp.is_ic_scale = wei_scales.get_mask() > 0;
jcp.with_dst_scales = !dst_scales.has_default_values();
}
jcp.req_brg_comp_pad = false;
jcp.req_cal_comp_pad = jcp.src_zero_point || jcp.s8s8_compensation_required;
VDISPATCH_CONV_IC(
!(jcp.req_cal_comp_pad && jcp.src_zero_point && is_amx(jcp.isa)
&& jcp.ngroups * jcp.ic * jcp.id * jcp.ih * jcp.iw < 4096
&& jcp.ic <= 4 && jcp.oc <= 64 && jcp.mb <= 64),
VERBOSE_IMPL_HEURISTIC_FAIL,
"skipping amx implementation for given data dimensions");
if (jcp.req_cal_comp_pad) {
VDISPATCH_CONV_IC(!(is_amx(jcp.isa)
&& static_cast<dim_t>(jcp.ngroups) * jcp.nb_ic
* jcp.ic_block * jcp.iw
> 4096),
VERBOSE_IMPL_HEURISTIC_FAIL,
"skipping amx implementation because of buffer size");
const auto comp_buffer_iw = jcp.exec_type == exec_trans ? jcp.iw : 1;
jcp.ker_ranges_size = precalculate_comp_pad_kernels(jcp);
jcp.comp_a_buffer_size = static_cast<dim_t>(jcp.ngroups) * jcp.nb_ic
* jcp.ker_ranges_size * comp_buffer_iw * jcp.ic_block;
jcp.s8s8_comp_buffer_size = jcp.comp_a_buffer_size;
}
return status::success;
}
void init_scratchpad(memory_tracking::registrar_t &scratchpad,
const jit_brgemm_conv_conf_t &jcp) {
if (jcp.brg_type == brgemm_addr || jcp.brg_type == brgemm_offs
|| (jcp.brg_type == brgemm_strd && jcp.exec_type == exec_vpad))
scratchpad.book(key_brgemm_primitive_batch,
static_cast<size_t>(jcp.nthr) * jcp.adjusted_batch_size,
sizeof(brgemm_batch_element_t), 64, P4K);
size_t inp_buffer_size
= static_cast<size_t>(jcp.nthr) * jcp.inp_buffer_size;
scratchpad.book(
key_conv_brgemm_inp_buffer, inp_buffer_size, jcp.src_dsz, 0, P4K);
size_t inp_buffer_mask_size
= static_cast<size_t>(jcp.nthr) * jcp.inp_buffer_mask_size;
scratchpad.book(key_conv_brgemm_inp_buffer_mask, inp_buffer_mask_size,
sizeof(uint8_t), 0, P4K);
if (jcp.exec_type == exec_trans && jcp.has_uneven_iw) {
size_t out_buffer_size
= static_cast<size_t>(jcp.nthr) * jcp.out_buffer_size;
scratchpad.book(key_conv_brgemm_out_buffer, out_buffer_size,
jcp.dst_dsz, 0, P4K);
}
if (jcp.use_buffer) {
scratchpad.book(key_brgemm_primitive_buffer, jcp.nthr * jcp.buffer_size,
jcp.acc_dsz, 0, P4K);
}
if (is_amx(jcp.isa)) {
scratchpad.book(key_conv_amx_tile_buffer, jcp.nthr * 2 * P4K,
sizeof(char), 0, P4K);
}
if (jcp.s8s8_compensation_required && jcp.req_cal_comp_pad) {
scratchpad.book(key_brgemm_primitive_buffer_comp,
jcp.s8s8_comp_buffer_size, sizeof(int32_t), 0, P4K);
}
if (jcp.src_zero_point && jcp.req_cal_comp_pad) {
scratchpad.book(key_brgemm_primitive_zp_comp_a, jcp.comp_a_buffer_size,
sizeof(int32_t), 0, P4K);
}
if (jcp.with_dst_scales) {
scratchpad.book(key_conv_dst_scales,
static_cast<size_t>(jcp.nthr) * sizeof(float), P4K);
}
}
}
} } } }