1 //
2 // Copyright © 2017,2022 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include "NeonFullyConnectedWorkload.hpp"
7
8 #include "NeonWorkloadUtils.hpp"
9
10 #include <aclCommon/ArmComputeTensorUtils.hpp>
11 #include <aclCommon/ArmComputeUtils.hpp>
12
13 #include <armnn/utility/PolymorphicDowncast.hpp>
14
15 #include <armnn/backends/TensorHandle.hpp>
16
17 #include <arm_compute/runtime/NEON/functions/NEFullyConnectedLayer.h>
18
19 namespace armnn
20 {
21 using namespace armcomputetensorutils;
22 using ACLMemManagerOnDemand = std::shared_ptr<arm_compute::MemoryManagerOnDemand>;
23
NeonFullyConnectedWorkloadValidate(const TensorInfo & input,const TensorInfo & output,const TensorInfo & weights,const Optional<TensorInfo> & biases,const FullyConnectedDescriptor & descriptor,const ActivationDescriptor * activationDescriptor)24 arm_compute::Status NeonFullyConnectedWorkloadValidate(const TensorInfo& input,
25 const TensorInfo& output,
26 const TensorInfo& weights,
27 const Optional<TensorInfo>& biases,
28 const FullyConnectedDescriptor& descriptor,
29 const ActivationDescriptor* activationDescriptor)
30 {
31 const arm_compute::TensorInfo aclInput = BuildArmComputeTensorInfo(input);
32 const arm_compute::TensorInfo aclOutput = BuildArmComputeTensorInfo(output);
33 arm_compute::TensorInfo aclWeights = BuildArmComputeTensorInfo(weights);
34 aclWeights.set_are_values_constant(weights.IsConstant());
35
36 arm_compute::TensorInfo aclBiases;
37 arm_compute::TensorInfo* optionalAclBiases = nullptr;
38 if (descriptor.m_BiasEnabled)
39 {
40 ARMNN_ASSERT(biases.has_value());
41 // Same for bias as weights. We don't currently support non const.
42 if (!biases.value().IsConstant())
43 {
44 return arm_compute::Status{arm_compute::ErrorCode::RUNTIME_ERROR,
45 "Arm NN NeonFullyConnectedWorkload does not support non constant bias."};
46 }
47 aclBiases = BuildArmComputeTensorInfo(biases.value());
48 aclBiases.set_are_values_constant(biases.value().IsConstant());
49 optionalAclBiases = &aclBiases;
50 }
51
52 const arm_compute::FullyConnectedLayerInfo fullyConnectedLayerInfo =
53 ConvertFullyConnectedDescriptorToAclFullyConnectedLayerInfo(descriptor, activationDescriptor);
54 return arm_compute::NEFullyConnectedLayer::validate(&aclInput,
55 &aclWeights,
56 optionalAclBiases,
57 &aclOutput,
58 fullyConnectedLayerInfo);
59 }
60
NeonFullyConnectedWorkload(const FullyConnectedQueueDescriptor & descriptor,const WorkloadInfo & info,ACLMemManagerOnDemand & memoryManager)61 NeonFullyConnectedWorkload::NeonFullyConnectedWorkload(const FullyConnectedQueueDescriptor& descriptor,
62 const WorkloadInfo& info,
63 ACLMemManagerOnDemand& memoryManager)
64 : NeonBaseWorkload<FullyConnectedQueueDescriptor>(descriptor, info)
65 {
66 m_Data.ValidateInputsOutputs("NeonFullyConnectedWorkload", 1, 1);
67
68 arm_compute::ITensor& input = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Inputs[0])->GetTensor();
69 arm_compute::ITensor& output = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Outputs[0])->GetTensor();
70
71 // Copy the weights' tensor into arm_compute tensor.
72 m_WeightsTensor = std::make_unique<arm_compute::Tensor>();
73 m_WeightsTensorInfo = info.m_InputTensorInfos[1];
74 BuildArmComputeTensor(*m_WeightsTensor, m_WeightsTensorInfo);
75
76 if (m_Data.m_Parameters.m_BiasEnabled)
77 {
78 // Copy the biases tensor into arm_compute tensor.
79 m_BiasesTensor = std::make_unique<arm_compute::Tensor>();
80 m_BiasesTensorInfo = info.m_InputTensorInfos[2];
81 BuildArmComputeTensor(*m_BiasesTensor, m_BiasesTensorInfo);
82 }
83
84 const arm_compute::ActivationLayerInfo activationInfo = ConvertAdditionalInfoToAclActivationLayerInfo(descriptor);
85 arm_compute::FullyConnectedLayerInfo fc_info =
86 ConvertFullyConnectedDescriptorToAclFullyConnectedLayerInfo(descriptor.m_Parameters, activationInfo);
87
88 auto layer = std::make_unique<arm_compute::NEFullyConnectedLayer>(memoryManager);
89 layer->configure(&input, m_WeightsTensor.get(), m_BiasesTensor.get(), &output, fc_info);
90 m_FullyConnectedLayer.reset(layer.release());
91
92 // Add details for profiling output
93 WorkloadInfo detailsInfo;
94
95 detailsInfo.m_InputTensorInfos = info.m_InputTensorInfos;
96 detailsInfo.m_OutputTensorInfos = info.m_OutputTensorInfos;
97 detailsInfo.m_WeightsTensorInfo = armnn::Optional<armnn::TensorInfo>(info.m_InputTensorInfos[1]);
98 if (descriptor.m_Parameters.m_BiasEnabled)
99 {
100 detailsInfo.m_BiasTensorInfo = armnn::Optional<armnn::TensorInfo>(info.m_InputTensorInfos[2]);
101 }
102
103 // Report Profiling Details
104 ARMNN_REPORT_PROFILING_WORKLOAD_DESC("NeonFullyConnectedWorkload_Construct",
105 descriptor.m_Parameters,
106 detailsInfo,
107 this->GetGuid());
108
109 // Force Compute Library to perform the necessary copying and reshaping.
110 }
111
Execute() const112 void NeonFullyConnectedWorkload::Execute() const
113 {
114 ARMNN_SCOPED_PROFILING_EVENT_NEON_GUID("NeonFullyConnectedWorkload_Execute", this->GetGuid());
115 // The constant tensors may not be fully in place until the workload is Executed
116 if (!prepared)
117 {
118 InitializeArmComputeTensorData(*m_WeightsTensor, m_WeightsTensorInfo, m_Data.m_Inputs[1]);
119
120 if (m_Data.m_Parameters.m_BiasEnabled)
121 {
122 InitializeArmComputeTensorData(*m_BiasesTensor, m_BiasesTensorInfo, m_Data.m_Inputs[2]);
123 }
124 m_FullyConnectedLayer->prepare();
125 FreeTensorIfUnused(m_WeightsTensor);
126 FreeTensorIfUnused(m_BiasesTensor);
127 prepared = true;
128 }
129 m_FullyConnectedLayer->run();
130 }
131
132 } //namespace armnn
133