#include "cpu/matmul/ref_grouped_gemm.hpp"
#if DNNL_EXPERIMENTAL_GROUPED_MEMORY
#include <algorithm>
#include <atomic>
#include "common/c_types_map.hpp"
#include "common/dnnl_thread.hpp"
#include "common/memory_desc_wrapper.hpp"
#include "common/type_helpers.hpp"
#include "cpu/ref_io_helper.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace matmul {
status_t ref_grouped_t::execute(const exec_ctx_t &ctx) const {
const memory_desc_wrapper src_d(pd()->src_md());
const memory_desc_wrapper wei_d(pd()->weights_md(0));
const memory_desc_wrapper dst_d(pd()->dst_md());
const auto &src_grouped = src_d.sparse_desc().grouped_desc;
const dim_t group_count = src_grouped.group_count;
const dim_t K = wei_d.dims()[wei_d.ndims() - 2];
const dim_t N = wei_d.dims()[wei_d.ndims() - 1];
const dim_t total_M = src_d.dims()[0];
const void *src_data = CTX_IN_MEM(const void *, DNNL_ARG_SRC, 0);
const int32_t *src_offsets = CTX_IN_MEM(const int32_t *, DNNL_ARG_SRC, 1);
const void *wei_data = CTX_IN_MEM(const void *, DNNL_ARG_WEIGHTS);
void *dst_data = CTX_OUT_MEM(void *, DNNL_ARG_DST, 0);
const int32_t *dst_offsets = CTX_OUT_MEM(const int32_t *, DNNL_ARG_DST, 1);
const auto src_dt = src_d.data_type();
const auto wei_dt = wei_d.data_type();
const auto dst_dt = pd()->dst_md()->data_type;
const bool with_bias = pd()->with_bias();
const void *bias_data
= with_bias ? CTX_IN_MEM(const void *, DNNL_ARG_BIAS) : nullptr;
const auto bia_dt
= with_bias ? pd()->weights_md(1)->data_type : data_type::undef;
const auto &attr_scales = pd()->attr()->scales_;
const bool with_src_scales = !attr_scales.has_default_values(DNNL_ARG_SRC);
const auto src_scale_dt = attr_scales.get_data_type(DNNL_ARG_SRC);
const auto src_scale_group_k = attr_scales.get_group(DNNL_ARG_SRC, -1);
const auto src_scale_ngroups_k
= src_scale_group_k > 1 ? K / src_scale_group_k : 1;
const void *src_scales
= CTX_IN_MEM(const void *, DNNL_ARG_ATTR_SCALES | DNNL_ARG_SRC);
const bool with_wei_scales
= !attr_scales.has_default_values(DNNL_ARG_WEIGHTS);
const auto wei_scale_dt = attr_scales.get_data_type(DNNL_ARG_WEIGHTS);
const auto wei_scale_group_k = attr_scales.get_group(DNNL_ARG_WEIGHTS, -2);
const auto wei_scale_ngroups_k
= wei_scale_group_k > 1 ? K / wei_scale_group_k : 1;
const void *wei_scales
= CTX_IN_MEM(const void *, DNNL_ARG_ATTR_SCALES | DNNL_ARG_WEIGHTS);
const auto &attr_zps = pd()->attr()->zero_points_;
const bool with_wei_zps = !attr_zps.has_default_values(DNNL_ARG_WEIGHTS);
const auto wei_zp_dt = attr_zps.get_data_type(DNNL_ARG_WEIGHTS);
const auto wei_zp_group_k = attr_zps.get_group(DNNL_ARG_WEIGHTS, -2);
const dim_t wei_zp_ngroups_k = wei_zp_group_k > 1 ? K / wei_zp_group_k : 1;
const void *wei_zps = CTX_IN_MEM(
const void *, DNNL_ARG_ATTR_ZERO_POINTS | DNNL_ARG_WEIGHTS);
const dim_t n_k_groups = std::max(
{src_scale_ngroups_k, wei_scale_ngroups_k, wei_zp_ngroups_k});
const dim_t k_group_size = K / n_k_groups;
const bool use_int_arithmetic
= utils::one_of(src_dt, data_type::s8, data_type::u8)
&& utils::one_of(wei_dt, data_type::s8, data_type::u8,
data_type::s4, data_type::u4);
const bool use_woq = utils::one_of(src_dt, data_type::f32, data_type::bf16,
data_type::f16)
&& utils::one_of(wei_dt, data_type::s8, data_type::u8,
data_type::s4, data_type::u4)
&& pd()->attr()->fpmath_.apply_to_int_;
const bool with_post_ops = !pd()->attr()->post_ops_.has_default_values();
std::atomic<status_t> st(status::success);
parallel_nd(group_count, [&](dim_t group_id) {
const dim_t src_offset_start
= (group_id == 0) ? 0 : src_offsets[group_id - 1];
const dim_t src_offset_end = src_offsets[group_id];
const dim_t dst_offset_start
= (group_id == 0) ? 0 : dst_offsets[group_id - 1];
const dim_t dst_offset_end = dst_offsets[group_id];
if (src_offset_start < 0 || src_offset_end > total_M
|| src_offset_end < src_offset_start || dst_offset_start < 0
|| dst_offset_end > total_M
|| dst_offset_end < dst_offset_start) {
st = status::invalid_arguments;
return;
}
const dim_t M = src_offset_end - src_offset_start;
if (M == 0) return;
const dim_t src_base_idx = src_offset_start * K;
const dim_t dst_base_idx = dst_offset_start * N;
const dim_t wei_group_base = group_id * K * N;
const dim_t wei_stride_k
= wei_d.blocking_desc().strides[wei_d.ndims() - 2];
const dim_t wei_stride_n
= wei_d.blocking_desc().strides[wei_d.ndims() - 1];
for (dim_t m = 0; m < M; ++m) {
for (dim_t n = 0; n < N; ++n) {
float result = 0.0f;
if (use_int_arithmetic || use_woq) {
for (dim_t i_group = 0; i_group < n_k_groups; i_group++) {
float wei_scale = 1.0f;
int wei_zp_val = 0;
if (with_wei_scales) {
const dim_t wei_k_group = i_group
* wei_scale_ngroups_k / n_k_groups;
const dim_t idx = group_id * wei_scale_ngroups_k * N
+ wei_k_group * N + n;
wei_scale = io::load_float_value(
wei_scale_dt, wei_scales, idx);
}
if (with_wei_zps) {
const dim_t wei_k_group
= i_group * wei_zp_ngroups_k / n_k_groups;
const dim_t idx = group_id * wei_zp_ngroups_k * N
+ wei_k_group * N + n;
wei_zp_val = io::load_int_value(
wei_zp_dt, wei_zps, idx);
}
float acc = 0.0f;
if (use_int_arithmetic) {
int acc_int = 0;
for (dim_t k = 0; k < k_group_size; ++k) {
const dim_t k_abs = k + i_group * k_group_size;
const dim_t src_idx
= src_base_idx + m * K + k_abs;
const dim_t wei_idx = wei_group_base
+ k_abs * wei_stride_k
+ n * wei_stride_n;
const int s = io::load_int_value(
src_dt, src_data, src_idx);
const int w = io::load_int_value(
wei_dt, wei_data, wei_idx);
acc_int += s * (w - wei_zp_val);
}
acc = static_cast<float>(acc_int);
} else {
for (dim_t k = 0; k < k_group_size; ++k) {
const dim_t k_abs = k + i_group * k_group_size;
const dim_t src_idx
= src_base_idx + m * K + k_abs;
const dim_t wei_idx = wei_group_base
+ k_abs * wei_stride_k
+ n * wei_stride_n;
const float s = io::load_float_value(
src_dt, src_data, src_idx);
const int w_int = io::load_int_value(
wei_dt, wei_data, wei_idx);
acc += s
* static_cast<float>(
w_int - wei_zp_val);
}
}
if (with_src_scales) {
const dim_t src_k_group = i_group
* src_scale_ngroups_k / n_k_groups;
const dim_t idx = (src_offset_start + m)
* src_scale_ngroups_k
+ src_k_group;
const float src_scale = io::load_float_value(
src_scale_dt, src_scales, idx);
acc *= src_scale;
}
result += acc * wei_scale;
}
} else {
for (dim_t i_group = 0; i_group < n_k_groups; i_group++) {
float acc = 0.0f;
for (dim_t k = 0; k < k_group_size; ++k) {
const dim_t k_abs = k + i_group * k_group_size;
const dim_t src_idx = src_base_idx + m * K + k_abs;
const dim_t wei_idx = wei_group_base
+ k_abs * wei_stride_k + n * wei_stride_n;
const float s = io::load_float_value(
src_dt, src_data, src_idx);
const float w = io::load_float_value(
wei_dt, wei_data, wei_idx);
acc += s * w;
}
if (with_src_scales) {
const dim_t src_k_group = i_group
* src_scale_ngroups_k / n_k_groups;
const dim_t idx = (src_offset_start + m)
* src_scale_ngroups_k
+ src_k_group;
const float src_scale = io::load_float_value(
src_scale_dt, src_scales, idx);
acc *= src_scale;
}
if (with_wei_scales) {
const dim_t wei_k_group = i_group
* wei_scale_ngroups_k / n_k_groups;
const dim_t idx = group_id * wei_scale_ngroups_k * N
+ wei_k_group * N + n;
const float wei_scale = io::load_float_value(
wei_scale_dt, wei_scales, idx);
acc *= wei_scale;
}
result += acc;
}
}
if (with_bias) {
const dim_t bias_idx = group_id * N + n;
result += io::load_float_value(bia_dt, bias_data, bias_idx);
}
const dim_t dst_idx = dst_base_idx + m * N + n;
if (with_post_ops) {
ref_post_ops_t::args_t args;
args.dst_val
= io::load_float_value(dst_dt, dst_data, dst_idx);
args.ctx = &ctx;
args.l_offset = dst_idx;
args.dst_md = pd()->dst_md();
ref_post_ops->execute(result, args);
}
io::store_float_value(dst_dt, result, dst_data, dst_idx);
}
}
});
return st;
}
} } } }
#endif