#include "cpu/aarch64/acl_inner_product.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace aarch64 {
status_t acl_inner_product_fwd_t::init(engine_t *engine) {
auto aip = pd()->aip_;
inner_product_op_ = std::make_unique<
arm_compute::experimental::op::CpuFullyConnected>();
inner_product_op_->configure(&aip.src_tensor_info, &aip.wei_tensor_info,
aip.with_bias ? &aip.bia_tensor_info : nullptr,
&aip.dst_tensor_info, aip.fc_info, aip.weights_info);
return status::success;
}
status_t acl_inner_product_fwd_t::execute(const exec_ctx_t &ctx) const {
return execute_forward(ctx);
}
status_t acl_inner_product_fwd_t::execute_forward(const exec_ctx_t &ctx) const {
std::lock_guard<std::mutex> _lock {this->mtx};
bool with_bias = pd()->aip_.with_bias;
bool use_dst_acc_for_sum = pd()->aip_.use_dst_acc_for_sum;
auto src_base = CTX_IN_MEM(const void *, DNNL_ARG_SRC);
auto wei_base = CTX_IN_MEM(const void *, DNNL_ARG_WEIGHTS);
const auto &scratchpad = ctx.get_scratchpad_grantor();
auto dst_base = use_dst_acc_for_sum
? scratchpad.get<void>(memory_tracking::names::key_generic_acc)
: CTX_OUT_MEM(void *, DNNL_ARG_DST);
auto aip = pd()->aip_;
arm_compute::Tensor src_tensor;
arm_compute::Tensor wei_tensor;
arm_compute::Tensor bia_tensor = nullptr;
arm_compute::Tensor dst_tensor;
src_tensor.allocator()->init(aip.src_tensor_info);
src_tensor.allocator()->import_memory(const_cast<void *>(src_base));
wei_tensor.allocator()->init(aip.wei_tensor_info);
wei_tensor.allocator()->import_memory(const_cast<void *>(wei_base));
dst_tensor.allocator()->init(aip.dst_tensor_info);
dst_tensor.allocator()->import_memory((dst_base));
if (with_bias) {
auto bia_base = CTX_IN_MEM(const void *, DNNL_ARG_BIAS);
bia_tensor.allocator()->init(aip.bia_tensor_info);
bia_tensor.allocator()->import_memory(const_cast<void *>(bia_base));
}
arm_compute::ITensorPack run_pack {
{arm_compute::TensorType::ACL_SRC_0, &src_tensor},
{arm_compute::TensorType::ACL_SRC_1, &wei_tensor},
{arm_compute::TensorType::ACL_BIAS, &bia_tensor},
{arm_compute::TensorType::ACL_DST, &dst_tensor}};
inner_product_op_->run(run_pack);
void *dst = dst_tensor.buffer();
pd()->post_ops.execute(ctx, dst);
return status::success;
}
status_t acl_inner_product_fwd_t::pd_t::init(engine_t *engine) {
using namespace data_type;
using smask_t = primitive_attr_t::skip_mask_t;
const format_kind_t weights_format_kind_received = weights_md_.format_kind;
const bool is_fp16_ok = expect_data_types(f16, f16, f16, f16, undef)
&& attr()->has_default_values(smask_t::post_ops, f16);
const bool is_fp32_ok = expect_data_types(f32, f32, f32, f32, undef)
&& attr()->has_default_values(
smask_t::post_ops | smask_t::fpmath_mode, f32);
const bool is_bf16_ok = expect_data_types(bf16, bf16, bf16, bf16, undef)
&& attr()->has_default_values(smask_t::post_ops, bf16);
const bool is_weights_md_format_ok
= utils::one_of(weights_format_kind_received, format_kind::any,
format_kind::blocked);
const bool ok = is_fwd() && !has_zero_dim_memory()
&& utils::one_of(true, is_fp16_ok, is_fp32_ok, is_bf16_ok)
&& is_weights_md_format_ok
&& set_default_params(true) == status::success;
VDISPATCH_INNER_PRODUCT(ok, VERBOSE_SKIP_PRIMITIVE_IMPL);
CHECK(init_conf_ip(engine, weights_format_kind_received));
if (aip_.use_dst_acc_for_sum) {
const memory_desc_wrapper dst_d(&dst_md_);
auto scratchpad = scratchpad_registry().registrar();
scratchpad.book(memory_tracking::names::key_generic_acc, dst_d.nelems(),
dst_d.data_type_size());
}
return status::success;
}
status_t acl_inner_product_fwd_t::pd_t::init_conf_ip(
engine_t *engine, format_kind_t weights_format_kind_received) {
const int ndims = src_md()->ndims;
const bool is_2d = (ndims == 2);
const bool is_4d = (ndims == 4);
ACL_CHECK_SUPPORT(!(is_2d || is_4d), "ACL supports only 2d or 4d cases");
using namespace format_tag;
auto src_tag = memory_desc_matches_one_of_tag(src_md_, nhwc, nchw, nc);
auto dst_tag = memory_desc_matches_one_of_tag(dst_md_, nc);
ACL_CHECK_SUPPORT(utils::one_of(format_tag::undef, src_tag, dst_tag),
"unsupported memory layout");
ACL_CHECK_SUPPORT(
is_2d && src_tag != dst_tag, "for src and dst layouts must match");
const dim_t ic_total = IC_total();
const dim_t n = MB();
const dim_t oc = OC();
aip_.src_tensor_info
= arm_compute::TensorInfo(arm_compute::TensorShape(ic_total, n), 1,
acl_utils::get_acl_data_t(src_md()->data_type));
aip_.wei_tensor_info
= arm_compute::TensorInfo(arm_compute::TensorShape(oc, ic_total), 1,
acl_utils::get_acl_data_t(weights_md(0)->data_type));
auto acl_dst_data_t = acl_utils::get_acl_data_t(dst_md()->data_type);
aip_.dst_tensor_info = arm_compute::TensorInfo(
arm_compute::TensorShape(oc, n), 1, acl_dst_data_t);
aip_.with_bias = desc()->bias_desc.format_kind != format_kind::undef;
auto acl_bia_data_t = aip_.with_bias
? acl_utils::get_acl_data_t(weights_md(1)->data_type)
: acl_dst_data_t;
aip_.bia_tensor_info = arm_compute::TensorInfo(aip_.with_bias
? arm_compute::TensorShape(oc)
: arm_compute::TensorShape(),
1, acl_bia_data_t);
aip_.fc_info.transpose_weights = false;
aip_.fc_info.enable_fast_math = utils::one_of(
attr()->fpmath_.mode_, fpmath_mode::bf16, fpmath_mode::any);
CHECK(post_ops.init(
engine, attr_.post_ops_, dst_md_, aip_.fc_info.activation_info));
aip_.use_dst_acc_for_sum = post_ops.has_sum();
aip_.weights_info = arm_compute::WeightsInfo(
false, 1, 1, ic_total, false, arm_compute::WeightFormat::ANY);
arm_compute::WeightFormat expected_weight_format;
ACL_CHECK_VALID(
arm_compute::experimental::op::CpuFullyConnected::has_opt_impl(
expected_weight_format, &aip_.src_tensor_info,
&aip_.wei_tensor_info,
aip_.with_bias ? &aip_.bia_tensor_info : nullptr,
&aip_.dst_tensor_info, aip_.fc_info, aip_.weights_info));
aip_.weights_info.set_weight_format(expected_weight_format);
aip_.fc_info.enable_fast_math
= arm_compute::is_fixed_format_fast_math(expected_weight_format);
dim_t o_dim = 0;
dim_t inner_dim;
std::vector<dim_t> remaining_dims = {};
if (src_tag == nchw) {
inner_dim = 3; remaining_dims = {2, 1}; } else if (src_tag == nhwc) {
inner_dim = 1; remaining_dims = {3, 2}; } else if (src_tag == nc) { inner_dim = 1; remaining_dims = {}; } else {
assert(false);
}
int block_by = arm_compute::block_by(expected_weight_format);
bool is_bf16 = src_md()->data_type == data_type::bf16
&& weights_md()->data_type == data_type::bf16
&& dst_md()->data_type == data_type::bf16;
if (is_4d && weights_md_.dims[inner_dim] % block_by != 0
&& (aip_.fc_info.enable_fast_math || is_bf16)) {
aip_.fc_info.enable_fast_math = false;
aip_.weights_info.set_weight_format(arm_compute::WeightFormat::ANY);
ACL_CHECK_VALID(
arm_compute::experimental::op::CpuFullyConnected::has_opt_impl(
expected_weight_format, &aip_.src_tensor_info,
&aip_.wei_tensor_info,
aip_.with_bias ? &aip_.bia_tensor_info : nullptr,
&aip_.dst_tensor_info, aip_.fc_info,
aip_.weights_info));
aip_.weights_info.set_weight_format(expected_weight_format);
block_by = arm_compute::block_by(expected_weight_format);
VDISPATCH_INNER_PRODUCT(weights_md_.dims[inner_dim] % block_by == 0,
VERBOSE_SKIP_PRIMITIVE_IMPL);
}
const memory_desc_t weights_md_received = weights_md_;
CHECK(acl_utils::reorder_to_weight_format(aip_.wei_tensor_info, weights_md_,
expected_weight_format, inner_dim, o_dim, remaining_dims, {}));
ACL_CHECK_SUPPORT((weights_format_kind_received == format_kind::blocked)
&& !(dnnl_memory_desc_equal(
&weights_md_received, &weights_md_)),
"specific blocked format not supported by ACL, use "
"format_kind_t::any to find a supported blocked format for "
"your platform");
ACL_CHECK_VALID(arm_compute::experimental::op::CpuFullyConnected::validate(
&aip_.src_tensor_info, &aip_.wei_tensor_info,
aip_.with_bias ? &aip_.bia_tensor_info : nullptr,
&aip_.dst_tensor_info, aip_.fc_info, aip_.weights_info));
return status::success;
}
} } } }