1 //
2 // Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include "RefBackend.hpp"
7 #include "RefBackendId.hpp"
8 #include "RefWorkloadFactory.hpp"
9 #include "RefLayerSupport.hpp"
10 #include "RefTensorHandleFactory.hpp"
11
12 #include <armnn/BackendRegistry.hpp>
13 #include <armnn/backends/IBackendContext.hpp>
14 #include <armnn/backends/IMemoryManager.hpp>
15 #include <armnn/utility/PolymorphicDowncast.hpp>
16 #include <backendsCommon/DefaultAllocator.hpp>
17 #include <backendsCommon/SubgraphUtils.hpp>
18
19 #include <Optimizer.hpp>
20
21 namespace armnn
22 {
23
GetIdStatic()24 const BackendId& RefBackend::GetIdStatic()
25 {
26 static const BackendId s_Id{RefBackendId()};
27 return s_Id;
28 }
29
CreateWorkloadFactory(const IBackendInternal::IMemoryManagerSharedPtr & memoryManager) const30 IBackendInternal::IWorkloadFactoryPtr RefBackend::CreateWorkloadFactory(
31 const IBackendInternal::IMemoryManagerSharedPtr& memoryManager) const
32 {
33 return std::make_unique<RefWorkloadFactory>(PolymorphicPointerDowncast<RefMemoryManager>(memoryManager));
34 }
35
CreateWorkloadFactory(class TensorHandleFactoryRegistry & tensorHandleFactoryRegistry) const36 IBackendInternal::IWorkloadFactoryPtr RefBackend::CreateWorkloadFactory(
37 class TensorHandleFactoryRegistry& tensorHandleFactoryRegistry) const
38 {
39 auto memoryManager = std::make_shared<RefMemoryManager>();
40
41 tensorHandleFactoryRegistry.RegisterMemoryManager(memoryManager);
42
43 std::unique_ptr<RefTensorHandleFactory> factory = std::make_unique<RefTensorHandleFactory>(memoryManager);
44 // Register copy and import factory pair
45 tensorHandleFactoryRegistry.RegisterCopyAndImportFactoryPair(factory->GetId(), factory->GetId());
46 // Register the factory
47 tensorHandleFactoryRegistry.RegisterFactory(std::move(factory));
48
49 return std::make_unique<RefWorkloadFactory>(PolymorphicPointerDowncast<RefMemoryManager>(memoryManager));
50 }
51
CreateBackendContext(const IRuntime::CreationOptions &) const52 IBackendInternal::IBackendContextPtr RefBackend::CreateBackendContext(const IRuntime::CreationOptions&) const
53 {
54 return IBackendContextPtr{};
55 }
56
CreateBackendProfilingContext(const IRuntime::CreationOptions &,IBackendProfilingPtr &)57 IBackendInternal::IBackendProfilingContextPtr RefBackend::CreateBackendProfilingContext(
58 const IRuntime::CreationOptions&, IBackendProfilingPtr&)
59 {
60 return IBackendProfilingContextPtr{};
61 }
62
CreateMemoryManager() const63 IBackendInternal::IMemoryManagerUniquePtr RefBackend::CreateMemoryManager() const
64 {
65 return std::make_unique<RefMemoryManager>();
66 }
67
GetLayerSupport() const68 IBackendInternal::ILayerSupportSharedPtr RefBackend::GetLayerSupport() const
69 {
70 static ILayerSupportSharedPtr layerSupport{new RefLayerSupport};
71 return layerSupport;
72 }
73
OptimizeSubgraphView(const SubgraphView & subgraph,const ModelOptions & modelOptions) const74 OptimizationViews RefBackend::OptimizeSubgraphView(const SubgraphView& subgraph,
75 const ModelOptions& modelOptions) const
76 {
77 OptimizationViews optimizationViews(modelOptions);
78
79 auto it = subgraph.endIConnectable();
80 std::map<LayerGuid, Layer*> untouched;
81
82 while (it != subgraph.beginIConnectable())
83 {
84 --it;
85 Layer& base = *(PolymorphicDowncast<Layer*>(*it));
86 untouched.insert({base.GetGuid(), &base});
87 }
88
89 it = subgraph.endIConnectable();
90 while (it != subgraph.beginIConnectable())
91 {
92 --it;
93 Layer& base = *(PolymorphicDowncast<Layer*>(*it));
94
95 // Special case to fuse padding into average pooling 2d for quantized datatype.
96 // Required to be done as a backend specific optimization as Neon does not support this special case.
97 if (base.GetType() == LayerType::Pooling2d)
98 {
99 Pooling2dLayer* baseLayer = PolymorphicDowncast<Pooling2dLayer*>(&base);
100 Pooling2dDescriptor poolingDescriptor = baseLayer->GetParameters();
101
102 if (baseLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer().GetType() == LayerType::Pad)
103 {
104 PadLayer* padLayer = PolymorphicDowncast<PadLayer*>(
105 &baseLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer());
106 if (padLayer->GetOutputSlot(0).GetNumConnections() == 1 &&
107 optimizations::pad_fold::TryFoldPadIntoLayer2d(padLayer->GetParameters(),
108 poolingDescriptor,
109 padLayer->GetOutputSlot().GetTensorInfo(),
110 true))
111 {
112 FoldPadIntoAveragePool2d<Pooling2dLayer>(optimizationViews, baseLayer,
113 poolingDescriptor, padLayer);
114 untouched.erase(baseLayer->GetGuid());
115 untouched.erase(padLayer->GetGuid());
116 }
117 }
118 }
119 }
120
121 if (optimizationViews.GetSubstitutions().empty())
122 {
123 optimizationViews.AddUntouchedSubgraph(SubgraphView(subgraph));
124 }
125 else
126 {
127 ReportUntouchedLayers(optimizationViews, untouched);
128 }
129
130 return optimizationViews;
131 }
132
GetHandleFactoryPreferences() const133 std::vector<ITensorHandleFactory::FactoryId> RefBackend::GetHandleFactoryPreferences() const
134 {
135 return std::vector<ITensorHandleFactory::FactoryId> { RefTensorHandleFactory::GetIdStatic() };
136 }
137
RegisterTensorHandleFactories(class TensorHandleFactoryRegistry & registry)138 void RefBackend::RegisterTensorHandleFactories(class TensorHandleFactoryRegistry& registry)
139 {
140 auto memoryManager = std::make_shared<RefMemoryManager>();
141
142 registry.RegisterMemoryManager(memoryManager);
143
144 std::unique_ptr<RefTensorHandleFactory> factory = std::make_unique<RefTensorHandleFactory>(memoryManager);
145
146 // Register copy and import factory pair
147 registry.RegisterCopyAndImportFactoryPair(factory->GetId(), factory->GetId());
148 // Register the factory
149 registry.RegisterFactory(std::move(factory));
150 }
151
GetDefaultAllocator() const152 std::unique_ptr<ICustomAllocator> RefBackend::GetDefaultAllocator() const
153 {
154 return std::make_unique<DefaultAllocator>();
155 }
156
CreateExecutionData(WorkingMemDescriptor & workingMemDescriptor) const157 ExecutionData RefBackend::CreateExecutionData(WorkingMemDescriptor& workingMemDescriptor) const
158 {
159 ExecutionData executionData;
160 executionData.m_Data = &workingMemDescriptor;
161 return executionData;
162 }
163
UpdateExecutionData(ExecutionData & executionData,WorkingMemDescriptor & workingMemDescriptor) const164 void RefBackend::UpdateExecutionData(ExecutionData& executionData, WorkingMemDescriptor& workingMemDescriptor) const
165 {
166 executionData.m_Data = &workingMemDescriptor;
167 }
168
169 } // namespace armnn
170