#ifndef CPU_MATMUL_GEMM_BASED_COMMON_HPP
#define CPU_MATMUL_GEMM_BASED_COMMON_HPP
#include <assert.h>
#include "common/c_types_map.hpp"
#include "common/dnnl_thread.hpp"
#include "common/primitive_attr.hpp"
#include "common/type_helpers.hpp"
#include "cpu/matmul/cpu_matmul_pd.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace matmul {
namespace gemm_based {
struct params_t {
bool dst_is_acc_;
bool gemm_applies_output_scales_ = false;
bool skip_sum_ = false;
float gemm_beta_ = 0.f;
bool has_pp_kernel_ = false;
bool use_single_gemm_call_optimization_ = false;
float default_pp_scales_ = 1.0f;
primitive_attr_t pp_attr_;
float get_gemm_alpha(const float *primitive_scales) const {
return gemm_applies_output_scales_ ? primitive_scales[0] : 1.f;
}
const float *get_post_processing_scales(
const float *primitive_scales) const {
return gemm_applies_output_scales_ ? &default_pp_scales_
: primitive_scales;
}
};
inline bool check_gemm_input_format(const memory_desc_t &md) {
memory_desc_wrapper mdw(md);
if (!mdw.is_plain()) return false;
const int ndims = mdw.ndims();
const dims_t &strides = mdw.blocking_desc().strides;
for (int dim = 0; dim < ndims; ++dim)
if (strides[dim] == 0) return false;
return utils::one_of(1, strides[ndims - 1], strides[ndims - 2]);
}
inline bool check_gemm_output_format(const memory_desc_t &md) {
const memory_desc_wrapper mdw(md);
const int ndims = mdw.ndims();
return mdw.is_plain() && mdw.blocking_desc().strides[ndims - 1] == 1;
}
inline bool check_gemm_compatible_formats(const matmul_pd_t &pd) {
return check_gemm_input_format(*(pd.src_md()))
&& check_gemm_input_format(*(pd.weights_md()))
&& check_gemm_output_format(*(pd.dst_md()));
}
inline bool check_gemm_binary_per_oc_compatible_formats(const matmul_pd_t &pd) {
const memory_desc_wrapper dst_d(pd.dst_md());
const dims_t &strides = dst_d.blocking_desc().strides;
const dims_t &dims = dst_d.dims();
const int ndims = dst_d.ndims();
for (auto d : dims)
if (is_runtime_value(d)) return false;
bool ok = true;
for (int i = 2; i < ndims - 1; i++)
ok = ok && strides[i] == strides[i + 1] * dims[i + 1];
return ok && (strides[ndims - 1] == 1 || strides[1] == 1);
}
inline size_t get_scratchpad_block_elements(const dim_t batch, dim_t M,
const dim_t N, const bool use_single_gemm_call_optimization,
const int nthr) {
assert(batch > 0);
assert(M > 0);
assert(N > 0);
size_t buffer_size;
if (use_single_gemm_call_optimization) {
buffer_size = (size_t)batch * M * N;
} else {
const size_t work_per_thr = utils::div_up((size_t)batch * M * N, nthr);
if (work_per_thr >= (size_t)N) {
buffer_size = nstl::min<size_t>(
(size_t)M * N, utils::rnd_dn(work_per_thr, N));
} else {
buffer_size = work_per_thr;
}
}
return utils::rnd_up(buffer_size, 64);
}
inline size_t get_scratchpad_num_elements(const dim_t batch, dim_t M,
const dim_t N, const bool use_single_gemm_call_optimization,
const int nthr) {
const int num_scratchpad_blocks
= use_single_gemm_call_optimization ? 1 : nthr;
size_t buf_sz = get_scratchpad_block_elements(batch, M, N,
use_single_gemm_call_optimization, nthr)
* num_scratchpad_blocks;
size_t buf_sz_1thr = get_scratchpad_block_elements(
batch, M, N, use_single_gemm_call_optimization, 1);
return nstl::max(buf_sz_1thr, buf_sz);
}
inline void book_acc_scratchpad(matmul_pd_t &pd, const params_t ¶ms,
size_t sizeof_acc_data, const int nthr) {
if (params.dst_is_acc_) return;
if (pd.has_runtime_dims_or_strides()) return;
const size_t buffer_size = get_scratchpad_num_elements(pd.batch(), pd.M(),
pd.N(), params.use_single_gemm_call_optimization_, nthr);
auto scratchpad = pd.scratchpad_registry().registrar();
scratchpad.book(memory_tracking::names::key_matmul_dst_in_acc_dt,
buffer_size, sizeof_acc_data);
}
} } } } }
#endif