#ifndef CPU_AARCH64_ACL_BATCH_NORMALIZATION_HPP
#define CPU_AARCH64_ACL_BATCH_NORMALIZATION_HPP
#include "cpu/cpu_batch_normalization_pd.hpp"
#include "cpu/aarch64/acl_post_ops.hpp"
#include "cpu/aarch64/acl_utils.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace aarch64 {
struct acl_batch_normalization_obj_t {
arm_compute::NEBatchNormalizationLayer bnorm;
arm_compute::Tensor src_tensor;
arm_compute::Tensor dst_tensor;
arm_compute::Tensor mean_tensor;
arm_compute::Tensor var_tensor;
arm_compute::Tensor beta_tensor; arm_compute::Tensor gamma_tensor; };
struct acl_batch_normalization_conf_t {
arm_compute::TensorInfo data_info;
arm_compute::TensorInfo stats_info;
arm_compute::ActivationLayerInfo act_info;
};
struct acl_batch_normalization_resource_t : public resource_t {
acl_batch_normalization_resource_t()
: acl_obj_(utils::make_unique<acl_batch_normalization_obj_t>()) {}
status_t configure(const acl_batch_normalization_conf_t &abp,
const batch_normalization_pd_t *pd) {
if (!acl_obj_) return status::out_of_memory;
acl_obj_->src_tensor.allocator()->init(abp.data_info);
acl_obj_->dst_tensor.allocator()->init(abp.data_info);
acl_obj_->mean_tensor.allocator()->init(abp.stats_info);
acl_obj_->var_tensor.allocator()->init(abp.stats_info);
if (pd->use_shift())
acl_obj_->beta_tensor.allocator()->init(abp.stats_info);
if (pd->use_scale())
acl_obj_->gamma_tensor.allocator()->init(abp.stats_info);
acl_obj_->bnorm.configure(
&acl_obj_->src_tensor,
&acl_obj_->dst_tensor,
&acl_obj_->mean_tensor,
&acl_obj_->var_tensor,
pd->use_shift() ? &acl_obj_->beta_tensor : nullptr,
pd->use_scale() ? &acl_obj_->gamma_tensor : nullptr,
pd->desc()->batch_norm_epsilon,
abp.act_info);
return status::success;
}
acl_batch_normalization_obj_t &get_acl_obj() const { return *acl_obj_; }
DNNL_DISALLOW_COPY_AND_ASSIGN(acl_batch_normalization_resource_t);
private:
std::unique_ptr<acl_batch_normalization_obj_t> acl_obj_;
};
struct acl_batch_normalization_fwd_t : public primitive_t {
struct pd_t : public cpu_batch_normalization_fwd_pd_t {
using cpu_batch_normalization_fwd_pd_t::
cpu_batch_normalization_fwd_pd_t;
DECLARE_COMMON_PD_T("acl", acl_batch_normalization_fwd_t);
status_t init(engine_t *engine) {
using namespace format_tag;
const memory_desc_wrapper src_d(src_md_);
const memory_desc_wrapper stat_d(stat_md_);
using smask_t = primitive_attr_t::skip_mask_t;
ACL_CHECK_SUPPORT(!attr()->has_default_values(smask_t::post_ops),
"attr must have default values");
ACL_CHECK_SUPPORT(!set_default_formats_common(),
"Failed to set default formats");
ACL_CHECK_SUPPORT(src_d != memory_desc_wrapper(dst_md()),
"Source and destination must have the same layout");
ACL_CHECK_SUPPORT(!use_global_stats(),
"stats must already have been computed (use global stats)");
ACL_CHECK_SUPPORT(!is_fwd(), "must be forward mode");
ACL_CHECK_SUPPORT(!utils::one_of(src_d.data_type(), data_type::f32,
data_type::f16),
"data type must be f32/f16");
ACL_CHECK_SUPPORT((!src_d.is_plain() || !stat_d.is_plain()),
"tensors must be plain");
ACL_CHECK_SUPPORT((!src_d.is_dense() || !stat_d.is_dense()),
"tensors must be dense");
ACL_CHECK_SUPPORT(src_d.is_zero() || src_d.has_zero_dim(),
"zero sized src tensor");
ACL_CHECK_SUPPORT(stat_d.is_zero() || stat_d.has_zero_dim(),
"zero sized stats tensor");
auto acl_stats_dt = acl_utils::get_acl_data_t(stat_d.data_type());
abp.stats_info
= arm_compute::TensorInfo(arm_compute::TensorShape(C()), 1,
acl_stats_dt, arm_compute::DataLayout::NHWC);
auto channel_stride = src_d.blocking_desc().strides[1];
if (channel_stride == 1) {
auto elems = src_d.nelems();
dim_t w = elems / C();
bool use_acl_threads = elems > 20000;
int max_threads = dnnl_get_max_threads();
int acl_threads
= use_acl_threads ? std::min((int)w, max_threads) : 1;
int nspc_threads = std::min((int)MB(), max_threads);
double acl_ref_time_diff = 33
+ elems * (0.11 / acl_threads - 0.48 / nspc_threads);
if (use_acl_threads) {
acl_ref_time_diff += 1500.0 + 130.0 * (acl_threads - 1);
}
if (acl_ref_time_diff > 0) return status::unimplemented;
auto data_shape = use_acl_threads
? arm_compute::TensorShape(C(), w, 1, 1)
: arm_compute::TensorShape(C(), 1, w, 1);
auto acl_data_dt = acl_utils::get_acl_data_t(src_d.data_type());
abp.data_info = arm_compute::TensorInfo(data_shape, 1,
acl_data_dt, arm_compute::DataLayout::NHWC);
} else {
return status::unimplemented;
}
ACL_CHECK_SUPPORT(fuse_norm_relu()
&& desc()->prop_kind == prop_kind::forward_training,
"forward training with fused ReLU is not supported");
for (int i = 0; i < attr()->post_ops_.len(); ++i)
if (!attr()->post_ops_.entry_[i].is_eltwise())
return status::unimplemented;
if (fuse_norm_relu()) {
abp.act_info = arm_compute::ActivationLayerInfo(arm_compute::
ActivationLayerInfo::ActivationFunction::RELU);
CHECK(validate(abp.act_info));
CHECK(post_ops.init(engine, attr_.post_ops_, src_md_));
} else {
arm_compute::ActivationLayerInfo act_info;
CHECK(post_ops.init(
engine, attr_.post_ops_, src_md_, act_info));
if (validate(act_info) == status::success) {
abp.act_info = act_info;
} else {
CHECK(validate());
CHECK(post_ops.init(engine, attr_.post_ops_, src_md_));
}
}
return status::success;
}
status_t validate() { return validate(abp.act_info); }
status_t validate(arm_compute::ActivationLayerInfo &act_info) {
ACL_CHECK_VALID(arm_compute::NEBatchNormalizationLayer::validate(
&abp.data_info, &abp.data_info, &abp.stats_info,
&abp.stats_info, use_shift() ? &abp.stats_info : nullptr,
use_scale() ? &abp.stats_info : nullptr,
desc()->batch_norm_epsilon, act_info));
return status::success;
}
acl_batch_normalization_conf_t abp = utils::zero<decltype(abp)>();
acl_post_ops_t post_ops;
};
acl_batch_normalization_fwd_t(const pd_t *apd) : primitive_t(apd) {}
status_t create_resource(
engine_t *engine, resource_mapper_t &mapper) const override {
if (mapper.has_resource(this)) return status::success;
auto r = utils::make_unique<acl_batch_normalization_resource_t>();
if (!r) return status::out_of_memory;
CHECK(r->configure(pd()->abp, pd()));
mapper.add(this, std::move(r));
return status::success;
}
status_t execute(const exec_ctx_t &ctx) const override {
return execute_forward(ctx);
}
private:
mutable std::mutex mtx;
status_t execute_forward(const exec_ctx_t &ctx) const;
const pd_t *pd() const { return (const pd_t *)primitive_t::pd().get(); }
};
} } } }
#endif