ArmNN
 21.05
NeonBackend.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "NeonBackend.hpp"
7 #include "NeonBackendId.hpp"
10 #include "NeonLayerSupport.hpp"
12 
14 #include <armnn/Descriptors.hpp>
15 
19 
22 
24 
33 
34 #include <Optimizer.hpp>
35 
36 #include <arm_compute/core/Types.h>
37 #include <arm_compute/runtime/Allocator.h>
38 
39 namespace armnn
40 {
41 
43 {
44  static const BackendId s_Id{NeonBackendId()};
45  return s_Id;
46 }
47 
49 {
50  return std::make_unique<NeonMemoryManager>(std::make_unique<arm_compute::Allocator>(),
52 }
53 
55  const IBackendInternal::IMemoryManagerSharedPtr& memoryManager) const
56 {
57  return std::make_unique<NeonWorkloadFactory>(
58  PolymorphicPointerDowncast<NeonMemoryManager>(memoryManager));
59 }
60 
62  const IBackendInternal::IMemoryManagerSharedPtr& memoryManager, const ModelOptions& modelOptions) const
63 {
64  return std::make_unique<NeonWorkloadFactory>(
65  PolymorphicPointerDowncast<NeonMemoryManager>(memoryManager), CreateBackendSpecificModelContext(modelOptions));
66 }
67 
69  class TensorHandleFactoryRegistry& tensorHandleFactoryRegistry) const
70 {
71  auto memoryManager = std::make_shared<NeonMemoryManager>(std::make_unique<arm_compute::Allocator>(),
73 
74  tensorHandleFactoryRegistry.RegisterMemoryManager(memoryManager);
75  tensorHandleFactoryRegistry.RegisterFactory(std::make_unique<NeonTensorHandleFactory>(memoryManager));
76 
77  return std::make_unique<NeonWorkloadFactory>(
78  PolymorphicPointerDowncast<NeonMemoryManager>(memoryManager));
79 }
80 
82  TensorHandleFactoryRegistry& tensorHandleFactoryRegistry, const ModelOptions& modelOptions) const
83 {
84  auto memoryManager = std::make_shared<NeonMemoryManager>(std::make_unique<arm_compute::Allocator>(),
86 
87  tensorHandleFactoryRegistry.RegisterMemoryManager(memoryManager);
88  tensorHandleFactoryRegistry.RegisterFactory(std::make_unique<NeonTensorHandleFactory>(memoryManager));
89 
90  return std::make_unique<NeonWorkloadFactory>(
91  PolymorphicPointerDowncast<NeonMemoryManager>(memoryManager), CreateBackendSpecificModelContext(modelOptions));
92 }
93 
95 {
96  return IBackendContextPtr{};
97 }
98 
101 {
103 }
104 
106 {
107  return Optimizations{};
108 }
109 
111  const ModelOptions& modelOptions) const
112 {
114 }
115 
117 {
118  static ILayerSupportSharedPtr layerSupport
119  {
121  };
122  return layerSupport;
123 }
124 
126 {
127  static ILayerSupportSharedPtr layerSupport
128  {
130  };
131  return layerSupport;
132 }
133 
135 {
136  auto search = cpuAccCapabilities.find(capabilityClass);
137  if (search != cpuAccCapabilities.end())
138  {
139  return true;
140  }
141  return false;
142 }
143 
145 {
146  OptimizationViews optimizationViews;
147 
148  auto it = subgraph.end();
149  std::map<LayerGuid, Layer*> untouched;
150 
151  while (it != subgraph.begin())
152  {
153  --it;
154  Layer& base = **it;
155  untouched.insert({base.GetGuid(), &base});
156  }
157 
158  it = subgraph.end();
159  while (it != subgraph.begin())
160  {
161  --it;
162  Layer& base = **it;
163 
167  || base.GetType() == LayerType::Subtraction || base.GetType() == LayerType::Division)
168  && (base.GetAdditionalInformation<ActivationDescriptor>() == nullptr))
169  {
170  for (auto output = base.BeginOutputSlots(); output != base.EndOutputSlots(); ++output)
171  {
172  if (output->GetNumConnections() == 1)
173  {
174  for (auto&& childInput : output->GetConnections())
175  {
176  if ((childInput->GetOwningLayer().GetType() == LayerType::Activation) &&
177  (checkDataTypeInputandOutput(childInput->GetOwningLayer())))
178  {
179  Layer& child = childInput->GetOwningLayer();
180 
181  auto* activationLayer = PolymorphicDowncast<ActivationLayer*>(&child);
182 
183  const std::string name = std::string("fused-") + child.GetName() + std::string("-into-") +
184  base.GetName();
185 
186  // Get params from activation layer
187  ActivationDescriptor activationDesc = activationLayer->GetParameters();
188 
189  if (base.GetType() == LayerType::Convolution2d)
190  {
191  Convolution2dLayer* baseLayer = PolymorphicDowncast<Convolution2dLayer*>(&base);
192 
193  Optional<TensorInfo> biases;
194 
195  if (baseLayer->GetParameters().m_BiasEnabled)
196  {
197  biases = baseLayer->m_Bias->GetTensorInfo();
198  }
199 
202  activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
203  baseLayer->GetParameters(),
204  baseLayer->m_Weight->GetTensorInfo(),
205  biases,
206  false,
207  &activationDesc);
208 
209  if (status)
210  {
211  FuseLayerWithWeightsAndBiases<Convolution2dLayer>(optimizationViews,
212  baseLayer,
213  activationLayer,
214  activationDesc,
215  name);
216  untouched.erase(baseLayer->GetGuid());
217  untouched.erase(activationLayer->GetGuid());
218  }
219  }
220  else if (base.GetType() == LayerType::DepthwiseConvolution2d)
221  {
222  DepthwiseConvolution2dLayer* baseLayer =
223  PolymorphicDowncast<DepthwiseConvolution2dLayer*>(&base);
224 
225  Optional<TensorInfo> biases;
226 
227  if (baseLayer->GetParameters().m_BiasEnabled)
228  {
229  biases = baseLayer->m_Bias->GetTensorInfo();
230  }
231 
234  activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
235  baseLayer->GetParameters(),
236  baseLayer->m_Weight->GetTensorInfo(),
237  biases,
238  &activationDesc);
239 
240  if (status)
241  {
242  FuseLayerWithWeightsAndBiases<DepthwiseConvolution2dLayer>(optimizationViews,
243  baseLayer,
244  activationLayer,
245  activationDesc,
246  name);
247  untouched.erase(baseLayer->GetGuid());
248  untouched.erase(activationLayer->GetGuid());
249  }
250  }
251  else if (base.GetType() == LayerType::FullyConnected)
252  {
253  FullyConnectedLayer* baseLayer = PolymorphicDowncast<FullyConnectedLayer*>(&base);
254 
257  activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
258  baseLayer->m_Weight->GetTensorInfo(),
259  baseLayer->m_Bias->GetTensorInfo(),
260  baseLayer->GetParameters(),
261  &activationDesc);
262 
263  if (status)
264  {
265  FuseLayerWithWeightsAndBiases<FullyConnectedLayer>(optimizationViews,
266  baseLayer,
267  activationLayer,
268  activationDesc,
269  name);
270  untouched.erase(baseLayer->GetGuid());
271  untouched.erase(activationLayer->GetGuid());
272  }
273  }
274  else if (base.GetType() == LayerType::BatchNormalization)
275  {
276  BatchNormalizationLayer* baseLayer =
277  PolymorphicDowncast<BatchNormalizationLayer*>(&base);
278 
281  activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
282  baseLayer->m_Mean->GetTensorInfo(),
283  baseLayer->m_Variance->GetTensorInfo(),
284  baseLayer->m_Beta->GetTensorInfo(),
285  baseLayer->m_Gamma->GetTensorInfo(),
286  baseLayer->GetParameters(),
287  &activationDesc);
288 
289  if (status)
290  {
291  BatchNormalizationLayer* replacementLayer =
292  FuseLayerWithParameters<BatchNormalizationLayer>(
293  optimizationViews,
294  baseLayer,
295  activationLayer,
296  activationDesc,
297  name);
298 
299  replacementLayer->m_Beta = std::move(baseLayer->m_Beta);
300  replacementLayer->m_Gamma = std::move(baseLayer->m_Gamma);
301  replacementLayer->m_Mean = std::move(baseLayer->m_Mean);
302  replacementLayer->m_Variance = std::move(baseLayer->m_Variance);
303  untouched.erase(baseLayer->GetGuid());
304  untouched.erase(activationLayer->GetGuid());
305  }
306  }
307  else if (base.GetType() == LayerType::Addition)
308  {
309  AdditionLayer* baseLayer = PolymorphicDowncast<AdditionLayer*>(&base);
310 
314  activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
315  &activationDesc);
316 
317  if (status)
318  {
319  FuseLayerWithoutParameters<AdditionLayer>(optimizationViews,
320  baseLayer,
321  activationLayer,
322  activationDesc,
323  name);
324  untouched.erase(baseLayer->GetGuid());
325  untouched.erase(activationLayer->GetGuid());
326  }
327  }
328  else if (base.GetType() == LayerType::Division)
329  {
330  DivisionLayer* baseLayer = PolymorphicDowncast<DivisionLayer*>(&base);
331 
335  activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
336  &activationDesc);
337 
338  if (status)
339  {
340  FuseLayerWithoutParameters<DivisionLayer>(optimizationViews,
341  baseLayer,
342  activationLayer,
343  activationDesc,
344  name);
345  untouched.erase(baseLayer->GetGuid());
346  untouched.erase(activationLayer->GetGuid());
347  }
348  }
349  else if (base.GetType() == LayerType::Multiplication)
350  {
351  MultiplicationLayer* baseLayer = PolymorphicDowncast<MultiplicationLayer*>(&base);
352 
356  activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
357  &activationDesc);
358 
359  if (status)
360  {
361  FuseLayerWithoutParameters<MultiplicationLayer>(optimizationViews,
362  baseLayer,
363  activationLayer,
364  activationDesc,
365  name);
366  untouched.erase(baseLayer->GetGuid());
367  untouched.erase(activationLayer->GetGuid());
368  }
369  }
370  else if (base.GetType() == LayerType::Subtraction)
371  {
372  SubtractionLayer* baseLayer = PolymorphicDowncast<SubtractionLayer*>(&base);
373 
377  activationLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(),
378  &activationDesc);
379 
380  if (status)
381  {
382  FuseLayerWithoutParameters<SubtractionLayer>(optimizationViews,
383  baseLayer,
384  activationLayer,
385  activationDesc,
386  name);
387  untouched.erase(baseLayer->GetGuid());
388  untouched.erase(activationLayer->GetGuid());
389  }
390  }
391  }
392  }
393  }
394  }
395  }
396  }
397 
398  if (optimizationViews.GetSubstitutions().empty())
399  {
400  optimizationViews.AddUntouchedSubgraph(SubgraphView(subgraph));
401  }
402  else
403  {
404  ReportUntouchedLayers(optimizationViews, untouched);
405  }
406 
407  return optimizationViews;
408 }
409 
410 std::vector<ITensorHandleFactory::FactoryId> NeonBackend::GetHandleFactoryPreferences() const
411 {
412  return std::vector<ITensorHandleFactory::FactoryId>() = { NeonTensorHandleFactory::GetIdStatic() };
413 }
414 
416 {
417  auto memoryManager = std::make_shared<NeonMemoryManager>(std::make_unique<arm_compute::Allocator>(),
419 
420  registry.RegisterMemoryManager(memoryManager);
421  registry.RegisterFactory(std::make_unique<NeonTensorHandleFactory>(memoryManager));
422 }
423 
424 } // namespace armnn
bool m_BiasEnabled
Enable/disable bias.
void RegisterMemoryManager(std::shared_ptr< IMemoryManager > memoryManger)
Register a memory manager with shared ownership.
This layer represents a batch normalization operation.
std::unique_ptr< IWorkloadFactory > IWorkloadFactoryPtr
bool m_BiasEnabled
Enable/disable bias.
std::vector< OptimizationPtr > Optimizations
bool HasCapability(BackendCapability capabilityClass) const override
Returns true if backend support the capability false otherwise.
const Parameters & GetParameters() const
IBackendInternal::IBackendProfilingContextPtr CreateBackendProfilingContext(const IRuntime::CreationOptions &, IBackendProfilingPtr &backendProfiling) override
Create context specifically used for profiling interaction from backends.
Definition: NeonBackend.cpp:99
arm_compute::Status NeonBatchNormalizationValidate(const TensorInfo &input, const TensorInfo &output, const TensorInfo &mean, const TensorInfo &var, const TensorInfo &beta, const TensorInfo &gamma, const BatchNormalizationDescriptor &descriptor, const ActivationDescriptor *activationDescriptor)
std::vector< ITensorHandleFactory::FactoryId > GetHandleFactoryPreferences() const override
(Optional) Returns a vector of supported TensorHandleFactory ids in preference order.
This layer represents a depthwise convolution 2d operation.
OptimizationViews OptimizeSubgraphView(const SubgraphView &subgraph) const override
std::vector< BackendOptions > ModelOptions
void RegisterFactory(std::unique_ptr< ITensorHandleFactory > allocator)
Register a TensorHandleFactory and transfer ownership.
void ReportUntouchedLayers(OptimizationViews &optimizationViews, std::map< LayerGuid, Layer *> untouched)
arm_compute::Status NeonDepthwiseConvolutionWorkloadValidate(const TensorInfo &input, const TensorInfo &output, const DepthwiseConvolution2dDescriptor &descriptor, const TensorInfo &weights, const Optional< TensorInfo > &biases, const ActivationDescriptor *activationDescriptor)
IWorkloadFactoryPtr CreateWorkloadFactory(const IBackendInternal::IMemoryManagerSharedPtr &memoryManager=nullptr) const override
Definition: NeonBackend.cpp:54
constexpr const char * NeonBackendId()
IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions &) const override
Create the runtime context of the backend.
Definition: NeonBackend.cpp:94
std::shared_ptr< ConstTensorHandle > m_Weight
A unique pointer to store Weight values.
std::shared_ptr< ConstTensorHandle > m_Mean
A unique pointer to store Mean values.
arm_compute::Status NeonFullyConnectedWorkloadValidate(const TensorInfo &input, const TensorInfo &output, const TensorInfo &weights, const TensorInfo &biases, const FullyConnectedDescriptor &descriptor, const ActivationDescriptor *activationDescriptor)
Copyright (c) 2021 ARM Limited and Contributors.
std::unique_ptr< IMemoryManager > IMemoryManagerUniquePtr
arm_compute::Status NeonAdditionWorkloadValidate(const TensorInfo &input0, const TensorInfo &input1, const TensorInfo &output, const ActivationDescriptor *activationDescriptor)
The NeonBackendModelContext is used to pass in Neon specific backend ModelOptions.
std::shared_ptr< ConstTensorHandle > m_Beta
A unique pointer to store Beta values.
The SubgraphView class represents a subgraph of a Graph.
arm_compute::Status NeonSubtractionWorkloadValidate(const TensorInfo &input0, const TensorInfo &input1, const TensorInfo &output, const ActivationDescriptor *activationDescriptor)
void RegisterTensorHandleFactories(class TensorHandleFactoryRegistry &registry) override
(Optional) Register TensorHandleFactories Either this method or CreateMemoryManager() and IWorkloadFa...
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:316
std::unique_ptr< armnn::profiling::IBackendProfiling > IBackendProfilingPtr
This layer represents a fully connected operation.
std::shared_ptr< ConstTensorHandle > m_Weight
A unique pointer to store Weight values.
std::shared_ptr< IBackendModelContext > IBackendSpecificModelContextPtr
BackendCapability
BackendCapability class.
Definition: Types.hpp:220
std::shared_ptr< IMemoryManager > IMemoryManagerSharedPtr
IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
std::shared_ptr< ConstTensorHandle > m_Bias
A unique pointer to store Bias values.
LayerType GetType() const override
Returns the armnn::LayerType of this layer.
Definition: Layer.hpp:265
Status
enumeration
Definition: Types.hpp:30
const OutputSlot * GetConnectedOutputSlot() const
Definition: Layer.hpp:55
IBackendInternal::IBackendSpecificModelContextPtr CreateBackendSpecificModelContext(const ModelOptions &modelOptions) const override
std::shared_ptr< ConstTensorHandle > m_Gamma
A unique pointer to store Gamma values.
arm_compute::Status NeonConvolution2dWorkloadValidate(const TensorInfo &input, const TensorInfo &output, const Convolution2dDescriptor &descriptor, const TensorInfo &weights, const Optional< TensorInfo > &biases, bool isFastMathEnabled, const ActivationDescriptor *activationDescriptor)
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:25
void AddUntouchedSubgraph(SubgraphView &&subgraph)
std::shared_ptr< ConstTensorHandle > m_Variance
A unique pointer to store Variance values.
std::shared_ptr< ConstTensorHandle > m_Bias
A unique pointer to store Bias values.
arm_compute::Status NeonDivisionWorkloadValidate(const TensorInfo &input0, const TensorInfo &input1, const TensorInfo &output, const ActivationDescriptor *activationDescriptor)
This layer represents an addition operation.
std::shared_ptr< ILayerSupport > ILayerSupportSharedPtr
const Substitutions & GetSubstitutions() const
This layer represents a subtraction operation.
IBackendInternal::Optimizations GetOptimizations() const override
std::vector< OutputSlot >::iterator BeginOutputSlots()
Definition: Layer.hpp:245
std::shared_ptr< ConstTensorHandle > m_Bias
A unique pointer to store Bias values.
std::shared_ptr< ConstTensorHandle > m_Weight
A unique pointer to store Weight values.
This layer represents a division operation.
std::vector< OutputSlot >::iterator EndOutputSlots()
Definition: Layer.hpp:246
static const BackendId & GetIdStatic()
Definition: NeonBackend.cpp:42
static const FactoryId & GetIdStatic()
const char * GetName() const override
Returns the name of the layer.
Definition: Layer.hpp:311
This layer represents a convolution 2d operation.
const std::set< armnn::BackendCapability > cpuAccCapabilities
Definition: NeonBackend.hpp:12
This layer represents a multiplication operation.
const TensorInfo & GetTensorInfo() const override
Definition: Layer.cpp:63
std::shared_ptr< armnn::profiling::IBackendProfilingContext > IBackendProfilingContextPtr
This is the bridge between backend and backend profiling we&#39;ll keep it in the backend namespace...
arm_compute::Status NeonMultiplicationWorkloadValidate(const TensorInfo &input0, const TensorInfo &input1, const TensorInfo &output, const ActivationDescriptor *activationDescriptor)
std::shared_ptr< T > GetAdditionalInformation() const
Definition: Layer.hpp:342
IBackendInternal::IMemoryManagerUniquePtr CreateMemoryManager() const override
Definition: NeonBackend.cpp:48
LayerGuid GetGuid() const final
Returns the unique id of the layer.
Definition: Layer.hpp:322
std::unique_ptr< IBackendContext > IBackendContextPtr