xref: /aosp_15_r20/external/armnn/src/backends/neon/workloads/NeonFullyConnectedWorkload.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
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