#include "cpu/aarch64/matmul/acl_matmul_utils.hpp"
#include "cpu/aarch64/acl_utils.hpp"
#include "cpu/matmul/gemm_based_common.hpp"
#include "cpu/matmul/matmul_utils.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace aarch64 {
bool batch_dims_have_default_order(const memory_desc_wrapper &mdw) {
assert(mdw.is_blocking_desc());
if (mdw.ndims() <= 2) { return true; }
const auto &dims = mdw.dims();
const auto ndims = mdw.ndims();
const auto &strides = mdw.strides();
int prod = dims[ndims - 1] * dims[ndims - 2];
for (int i = ndims - 3; i >= 0; --i) {
if (strides[i] != prod) { return false; }
prod *= dims[i];
}
return true;
}
namespace acl_matmul_utils {
template <bool IsFixedFormat>
status_t init_conf_matmul(acl_matmul_conf_t &, memory_desc_t &src_md,
memory_desc_t &wei_md, memory_desc_t &dst_md, const matmul_desc_t &md,
const primitive_attr_t &attr) {
const memory_desc_wrapper src_d(&src_md);
const memory_desc_wrapper wei_d(&wei_md);
const memory_desc_wrapper dst_d(&dst_md);
cpu::matmul::matmul_helper_t helper(src_d, wei_d, dst_d);
const dim_t M = helper.M();
const dim_t N = helper.N();
const dim_t K = helper.K();
const dim_t dst_batch = helper.batch();
const dim_t src_batch = helper.src_batch();
const dim_t wei_batch = helper.wei_batch();
ACL_CHECK_SUPPORT(
src_d.ndims() > 4 || wei_d.ndims() > 4 || dst_d.ndims() > 4,
"ACL does not support more than 4 dimensions");
bool batch_ok = IMPLICATION(src_batch > 1, wei_batch == 1)
&& IMPLICATION(wei_batch > 1, src_batch == 1);
ACL_CHECK_SUPPORT(src_d.ndims() == 4 && src_batch != wei_batch && !batch_ok,
"matmul broadcast supported only for 3D shapes and 4D shapes when "
"ab is 1x1");
if (src_d.ndims() == 4 && src_batch == wei_batch
&& src_d.dims()[0] != wei_d.dims()[0]) { if (src_d.dims()[0] == 1 && wei_d.dims()[0] != 1) { ACL_CHECK_SUPPORT(
IMPLICATION(src_d.dims()[1] != 1, wei_d.dims()[1] == 1),
"acl only broadcasts one of src or wei at once");
}
if (wei_d.dims()[0] == 1 && src_d.dims()[0] != 1) { ACL_CHECK_SUPPORT(
IMPLICATION(src_d.dims()[1] == 1, wei_d.dims()[1] != 1),
"acl only broadcasts one of src or wei at once");
}
}
bool with_bias = md.bias_desc.format_kind != format_kind::undef;
ACL_CHECK_SUPPORT(with_bias, "ACL does not support bias for matmul");
ACL_CHECK_SUPPORT(!matmul::gemm_based::check_gemm_input_format(src_md),
"at least one innermost dimension must be contiguous");
ACL_CHECK_SUPPORT(!matmul::gemm_based::check_gemm_output_format(dst_md),
"innermost dst dimension must be contiguous");
if (!IsFixedFormat) {
ACL_CHECK_SUPPORT(!matmul::gemm_based::check_gemm_input_format(wei_md),
"at least one innermost dimension must be contiguous");
}
ACL_CHECK_SUPPORT(!src_d.is_dense(), "src tensor is not dense");
ACL_CHECK_SUPPORT(!dst_d.is_dense(), "dst tensor is not dense");
if (!IsFixedFormat) {
ACL_CHECK_SUPPORT(!wei_d.is_dense(), "wei tensor is not dense");
}
ACL_CHECK_SUPPORT(!batch_dims_have_default_order(src_d),
"src batch dimensions must be in order");
ACL_CHECK_SUPPORT(!batch_dims_have_default_order(wei_d),
"wei batch dimensions must be in order");
ACL_CHECK_SUPPORT(!batch_dims_have_default_order(dst_d),
"dst batch dimensions must be in order");
amp.is_transA = helper.transA() == 'T';
amp.is_transB = IsFixedFormat ? false : helper.transB() == 'T';
amp.do_transC = amp.is_transA && amp.is_transB && M * N <= K * (M + N);
auto acl_src_data_t = acl_utils::get_acl_data_t(src_md.data_type);
auto acl_wei_data_t = acl_utils::get_acl_data_t(wei_md.data_type);
auto acl_dst_data_t = acl_utils::get_acl_data_t(dst_md.data_type);
if (amp.is_transA && !amp.do_transC) {
amp.src_acc_info = arm_compute::TensorInfo(
arm_compute::TensorShape(M, K, 1, src_batch), 1,
acl_src_data_t);
}
if (amp.is_transB && !amp.do_transC) {
amp.wei_acc_info = arm_compute::TensorInfo(
arm_compute::TensorShape(K, N, wei_batch), 1, acl_wei_data_t);
}
if (amp.do_transC) {
amp.dst_acc_info = arm_compute::TensorInfo(
arm_compute::TensorShape(M, N, 1, dst_batch), 1,
acl_dst_data_t);
amp.src_tensor_info = arm_compute::TensorInfo(
arm_compute::TensorShape(M, K, src_batch), 1, acl_src_data_t);
amp.src_tensor_info.set_are_values_constant(false);
amp.wei_tensor_info = arm_compute::TensorInfo(
arm_compute::TensorShape(K, N, 1, wei_batch), 1,
acl_wei_data_t);
} else {
amp.src_tensor_info = arm_compute::TensorInfo(
arm_compute::TensorShape(K, M, 1, src_batch), 1,
acl_src_data_t);
amp.wei_tensor_info = arm_compute::TensorInfo(
arm_compute::TensorShape(N, K, wei_batch), 1, acl_wei_data_t);
amp.wei_tensor_info.set_are_values_constant(false);
}
amp.dst_tensor_info = arm_compute::TensorInfo(
arm_compute::TensorShape(N, M, 1, dst_batch), 1, acl_dst_data_t);
if (amp.is_transA && !amp.do_transC)
ACL_CHECK_VALID(arm_compute::experimental::op::CpuTranspose::validate(
&.src_acc_info, &.src_tensor_info));
if (amp.is_transB && !amp.do_transC)
ACL_CHECK_VALID(arm_compute::experimental::op::CpuTranspose::validate(
&.wei_acc_info, &.wei_tensor_info));
if (amp.do_transC)
ACL_CHECK_VALID(arm_compute::experimental::op::CpuTranspose::validate(
&.dst_acc_info, &.dst_tensor_info));
bool is_fastmath_enabled = utils::one_of(
attr.fpmath_.mode_, fpmath_mode::bf16, fpmath_mode::any);
amp.gemm_info.set_fast_math(is_fastmath_enabled);
if (IsFixedFormat) {
amp.gemm_info.set_fixed_format(true);
amp.gemm_info.set_weight_format(arm_compute::WeightFormat::ANY);
arm_compute::WeightFormat expected_weight_format;
ACL_CHECK_VALID(
arm_compute::experimental::op::ll::CpuGemmAssemblyDispatch::
has_opt_impl(expected_weight_format,
&.src_tensor_info, &.wei_tensor_info,
nullptr, &.dst_tensor_info, amp.gemm_info));
amp.gemm_info.set_weight_format(expected_weight_format);
amp.gemm_info.set_fast_math(
arm_compute::is_fixed_format_fast_math(expected_weight_format));
dim_t innermost_dim = wei_md.ndims - 1;
dim_t N_dim = innermost_dim;
dim_t K_dim = innermost_dim - 1;
std::vector<dim_t> batch_dims = {};
for (dim_t i = K_dim - 1; i >= 0; --i)
batch_dims.push_back(i);
CHECK(acl_utils::reorder_to_weight_format(amp.wei_tensor_info, wei_md,
expected_weight_format, K_dim, N_dim, {}, batch_dims));
}
return status::success;
}
status_t init_scratchpad(memory_tracking::registrar_t &scratchpad,
const acl_matmul_conf_t &, const memory_desc_t &src_md,
const memory_desc_t &weights_md, const memory_desc_t &dst_md,
const arm_compute::experimental::MemoryRequirements &aux_mem_req) {
if (amp.use_dst_acc_for_sum) {
const memory_desc_wrapper dst_d(&dst_md);
scratchpad.book(memory_tracking::names::key_matmul_dst_in_acc_dt,
dst_d.nelems(), dst_d.data_type_size());
}
if (!aux_mem_req.empty()) {
for (const auto &key : matmul_keys) {
const auto id = key.first;
if (aux_mem_req[id].size > 0) {
scratchpad.book(key.second, aux_mem_req[id].size, 1,
aux_mem_req[id].alignment, aux_mem_req[id].alignment);
}
}
}
if (amp.is_transA) {
const memory_desc_wrapper src_d(&src_md);
scratchpad.book(memory_tracking::names::key_matmul_src_trans,
src_d.nelems(), src_d.data_type_size());
}
if (amp.is_transB) {
const memory_desc_wrapper wei_d(&weights_md);
scratchpad.book(memory_tracking::names::key_matmul_wei_trans,
wei_d.nelems(), wei_d.data_type_size());
}
if (amp.do_transC) {
const memory_desc_wrapper dst_d(&dst_md);
scratchpad.book(memory_tracking::names::key_matmul_dst_trans,
dst_d.nelems(), dst_d.data_type_size());
}
return status::success;
}
template status_t init_conf_matmul<true>(acl_matmul_conf_t &,
memory_desc_t &src_md, memory_desc_t &wei_md, memory_desc_t &dst_md,
const matmul_desc_t &md, const primitive_attr_t &attr);
template status_t init_conf_matmul<false>(acl_matmul_conf_t &,
memory_desc_t &src_md, memory_desc_t &wei_md, memory_desc_t &dst_md,
const matmul_desc_t &md, const primitive_attr_t &attr);
}
} } } }