ArmNN
 22.02
TensorHandleStrategyTest.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 <doctest/doctest.h>
7 
9 
15 
17 
18 #include <Network.hpp>
19 
21 
22 #include <vector>
23 #include <string>
24 
25 
26 using namespace armnn;
27 
28 class TestMemMgr : public IMemoryManager
29 {
30 public:
31  TestMemMgr() = default;
32 
33  void Acquire() override {}
34  void Release() override {}
35 };
36 
37 class TestFactory1 : public ITensorHandleFactory
38 {
39 public:
40  TestFactory1(std::weak_ptr<IMemoryManager> mgr, ITensorHandleFactory::FactoryId id)
41  : m_Id(id)
42  , m_MemMgr(mgr)
43  {}
44 
45  std::unique_ptr<ITensorHandle> CreateSubTensorHandle(ITensorHandle& parent,
46  TensorShape const& subTensorShape,
47  unsigned int const* subTensorOrigin) const override
48  {
49  IgnoreUnused(parent, subTensorShape, subTensorOrigin);
50  return nullptr;
51  }
52 
53  std::unique_ptr<ITensorHandle> CreateTensorHandle(const TensorInfo& tensorInfo) const override
54  {
55  IgnoreUnused(tensorInfo);
56  return nullptr;
57  }
58 
59  std::unique_ptr<ITensorHandle> CreateTensorHandle(const TensorInfo& tensorInfo,
60  DataLayout dataLayout) const override
61  {
62  IgnoreUnused(tensorInfo, dataLayout);
63  return nullptr;
64  }
65 
66  const FactoryId& GetId() const override { return m_Id; }
67 
68  bool SupportsSubTensors() const override { return true; }
69 
70  MemorySourceFlags GetExportFlags() const override { return 1; }
71 
72 private:
73  FactoryId m_Id = "UninitializedId";
74 
75  std::weak_ptr<IMemoryManager> m_MemMgr;
76 };
77 
78 class TestFactoryImport : public ITensorHandleFactory
79 {
80 public:
81  TestFactoryImport(std::weak_ptr<IMemoryManager> mgr, ITensorHandleFactory::FactoryId id)
82  : m_Id(id)
83  , m_MemMgr(mgr)
84  {}
85 
86  std::unique_ptr<ITensorHandle> CreateSubTensorHandle(ITensorHandle& parent,
87  TensorShape const& subTensorShape,
88  unsigned int const* subTensorOrigin) const override
89  {
90  IgnoreUnused(parent, subTensorShape, subTensorOrigin);
91  return nullptr;
92  }
93 
94  std::unique_ptr<ITensorHandle> CreateTensorHandle(const TensorInfo& tensorInfo) const override
95  {
96  IgnoreUnused(tensorInfo);
97  return nullptr;
98  }
99 
100  std::unique_ptr<ITensorHandle> CreateTensorHandle(const TensorInfo& tensorInfo,
101  DataLayout dataLayout) const override
102  {
103  IgnoreUnused(tensorInfo, dataLayout);
104  return nullptr;
105  }
106 
107  const FactoryId& GetId() const override { return m_Id; }
108 
109  bool SupportsSubTensors() const override { return true; }
110 
111  MemorySourceFlags GetImportFlags() const override { return 1; }
112 
113 private:
114  FactoryId m_Id = "ImporterId";
115 
116  std::weak_ptr<IMemoryManager> m_MemMgr;
117 };
118 
119 class TestBackendA : public IBackendInternal
120 {
121 public:
122  TestBackendA() = default;
123 
124  const BackendId& GetId() const override { return m_Id; }
125 
126  IWorkloadFactoryPtr CreateWorkloadFactory(const IMemoryManagerSharedPtr& memoryManager = nullptr) const override
127  {
128  IgnoreUnused(memoryManager);
129  return IWorkloadFactoryPtr{};
130  }
131 
132  IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
133  {
134  return ILayerSupportSharedPtr{};
135  }
136 
137  std::vector<ITensorHandleFactory::FactoryId> GetHandleFactoryPreferences() const override
138  {
139  return std::vector<ITensorHandleFactory::FactoryId>
140  {
141  "TestHandleFactoryA1",
142  "TestHandleFactoryA2",
143  "TestHandleFactoryB1",
144  "TestHandleFactoryD1"
145  };
146  }
147 
148  void RegisterTensorHandleFactories(TensorHandleFactoryRegistry& registry) override
149  {
150  auto mgr = std::make_shared<TestMemMgr>();
151 
152  registry.RegisterMemoryManager(mgr);
153  registry.RegisterFactory(std::make_unique<TestFactory1>(mgr, "TestHandleFactoryA1"));
154  registry.RegisterFactory(std::make_unique<TestFactory1>(mgr, "TestHandleFactoryA2"));
155  }
156 
157 private:
158  BackendId m_Id = "BackendA";
159 };
160 
161 class TestBackendB : public IBackendInternal
162 {
163 public:
164  TestBackendB() = default;
165 
166  const BackendId& GetId() const override { return m_Id; }
167 
168  IWorkloadFactoryPtr CreateWorkloadFactory(const IMemoryManagerSharedPtr& memoryManager = nullptr) const override
169  {
170  IgnoreUnused(memoryManager);
171  return IWorkloadFactoryPtr{};
172  }
173 
174  IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
175  {
176  return ILayerSupportSharedPtr{};
177  }
178 
179  std::vector<ITensorHandleFactory::FactoryId> GetHandleFactoryPreferences() const override
180  {
181  return std::vector<ITensorHandleFactory::FactoryId>
182  {
183  "TestHandleFactoryB1"
184  };
185  }
186 
187  void RegisterTensorHandleFactories(TensorHandleFactoryRegistry& registry) override
188  {
189  auto mgr = std::make_shared<TestMemMgr>();
190 
191  registry.RegisterMemoryManager(mgr);
192  registry.RegisterFactory(std::make_unique<TestFactory1>(mgr, "TestHandleFactoryB1"));
193  }
194 
195 private:
196  BackendId m_Id = "BackendB";
197 };
198 
199 class TestBackendC : public IBackendInternal
200 {
201 public:
202  TestBackendC() = default;
203 
204  const BackendId& GetId() const override { return m_Id; }
205 
206  IWorkloadFactoryPtr CreateWorkloadFactory(const IMemoryManagerSharedPtr& memoryManager = nullptr) const override
207  {
208  IgnoreUnused(memoryManager);
209  return IWorkloadFactoryPtr{};
210  }
211 
212  IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
213  {
214  return ILayerSupportSharedPtr{};
215  }
216 
217  std::vector<ITensorHandleFactory::FactoryId> GetHandleFactoryPreferences() const override
218  {
219  return std::vector<ITensorHandleFactory::FactoryId>{
220  "TestHandleFactoryC1"
221  };
222  }
223 
224  void RegisterTensorHandleFactories(TensorHandleFactoryRegistry& registry) override
225  {
226  auto mgr = std::make_shared<TestMemMgr>();
227 
228  registry.RegisterMemoryManager(mgr);
229  registry.RegisterFactory(std::make_unique<TestFactory1>(mgr, "TestHandleFactoryC1"));
230  }
231 
232 private:
233  BackendId m_Id = "BackendC";
234 };
235 
236 class TestBackendD : public IBackendInternal
237 {
238 public:
239  TestBackendD() = default;
240 
241  const BackendId& GetId() const override { return m_Id; }
242 
243  IWorkloadFactoryPtr CreateWorkloadFactory(const IMemoryManagerSharedPtr& memoryManager = nullptr) const override
244  {
245  IgnoreUnused(memoryManager);
246  return IWorkloadFactoryPtr{};
247  }
248 
249  IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
250  {
251  return ILayerSupportSharedPtr{};
252  }
253 
254  std::vector<ITensorHandleFactory::FactoryId> GetHandleFactoryPreferences() const override
255  {
256  return std::vector<ITensorHandleFactory::FactoryId>{
257  "TestHandleFactoryD1",
258  };
259  }
260 
261  void RegisterTensorHandleFactories(TensorHandleFactoryRegistry& registry) override
262  {
263  auto mgr = std::make_shared<TestMemMgr>();
264 
265  registry.RegisterMemoryManager(mgr);
266  registry.RegisterFactory(std::make_unique<TestFactoryImport>(mgr, "TestHandleFactoryD1"));
267  }
268 
269 private:
270  BackendId m_Id = "BackendD";
271 };
272 
273 
274 TEST_SUITE("TensorHandle")
275 {
276 TEST_CASE("RegisterFactories")
277 {
278  TestBackendA backendA;
279  TestBackendB backendB;
280 
281  CHECK(backendA.GetHandleFactoryPreferences()[0] == "TestHandleFactoryA1");
282  CHECK(backendA.GetHandleFactoryPreferences()[1] == "TestHandleFactoryA2");
283  CHECK(backendA.GetHandleFactoryPreferences()[2] == "TestHandleFactoryB1");
284  CHECK(backendA.GetHandleFactoryPreferences()[3] == "TestHandleFactoryD1");
285 
287  backendA.RegisterTensorHandleFactories(registry);
288  backendB.RegisterTensorHandleFactories(registry);
289 
290  CHECK((registry.GetFactory("Non-existing Backend") == nullptr));
291  CHECK((registry.GetFactory("TestHandleFactoryA1") != nullptr));
292  CHECK((registry.GetFactory("TestHandleFactoryA2") != nullptr));
293  CHECK((registry.GetFactory("TestHandleFactoryB1") != nullptr));
294 }
295 
296 TEST_CASE("TensorHandleSelectionStrategy")
297 {
298  auto backendA = std::make_unique<TestBackendA>();
299  auto backendB = std::make_unique<TestBackendB>();
300  auto backendC = std::make_unique<TestBackendC>();
301  auto backendD = std::make_unique<TestBackendD>();
302 
304  backendA->RegisterTensorHandleFactories(registry);
305  backendB->RegisterTensorHandleFactories(registry);
306  backendC->RegisterTensorHandleFactories(registry);
307  backendD->RegisterTensorHandleFactories(registry);
308 
309  BackendsMap backends;
310  backends["BackendA"] = std::move(backendA);
311  backends["BackendB"] = std::move(backendB);
312  backends["BackendC"] = std::move(backendC);
313  backends["BackendD"] = std::move(backendD);
314 
315  armnn::Graph graph;
316 
317  armnn::InputLayer* const inputLayer = graph.AddLayer<armnn::InputLayer>(0, "input");
318  inputLayer->SetBackendId("BackendA");
319 
321  armnn::SoftmaxLayer* const softmaxLayer1 = graph.AddLayer<armnn::SoftmaxLayer>(smDesc, "softmax1");
322  softmaxLayer1->SetBackendId("BackendA");
323 
324  armnn::SoftmaxLayer* const softmaxLayer2 = graph.AddLayer<armnn::SoftmaxLayer>(smDesc, "softmax2");
325  softmaxLayer2->SetBackendId("BackendB");
326 
327  armnn::SoftmaxLayer* const softmaxLayer3 = graph.AddLayer<armnn::SoftmaxLayer>(smDesc, "softmax3");
328  softmaxLayer3->SetBackendId("BackendC");
329 
330  armnn::SoftmaxLayer* const softmaxLayer4 = graph.AddLayer<armnn::SoftmaxLayer>(smDesc, "softmax4");
331  softmaxLayer4->SetBackendId("BackendD");
332 
333  armnn::OutputLayer* const outputLayer = graph.AddLayer<armnn::OutputLayer>(0, "output");
334  outputLayer->SetBackendId("BackendA");
335 
336  inputLayer->GetOutputSlot(0).Connect(softmaxLayer1->GetInputSlot(0));
337  softmaxLayer1->GetOutputSlot(0).Connect(softmaxLayer2->GetInputSlot(0));
338  softmaxLayer2->GetOutputSlot(0).Connect(softmaxLayer3->GetInputSlot(0));
339  softmaxLayer3->GetOutputSlot(0).Connect(softmaxLayer4->GetInputSlot(0));
340  softmaxLayer4->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
341 
342  graph.TopologicalSort();
343 
344  std::vector<std::string> errors;
345  auto result = SelectTensorHandleStrategy(graph, backends, registry, true, errors);
346 
347  CHECK(result.m_Error == false);
348  CHECK(result.m_Warning == false);
349 
350  OutputSlot& inputLayerOut = inputLayer->GetOutputSlot(0);
351  OutputSlot& softmaxLayer1Out = softmaxLayer1->GetOutputSlot(0);
352  OutputSlot& softmaxLayer2Out = softmaxLayer2->GetOutputSlot(0);
353  OutputSlot& softmaxLayer3Out = softmaxLayer3->GetOutputSlot(0);
354  OutputSlot& softmaxLayer4Out = softmaxLayer4->GetOutputSlot(0);
355 
356  // Check that the correct factory was selected
357  CHECK(inputLayerOut.GetTensorHandleFactoryId() == "TestHandleFactoryD1");
358  CHECK(softmaxLayer1Out.GetTensorHandleFactoryId() == "TestHandleFactoryB1");
359  CHECK(softmaxLayer2Out.GetTensorHandleFactoryId() == "TestHandleFactoryB1");
360  CHECK(softmaxLayer3Out.GetTensorHandleFactoryId() == "TestHandleFactoryC1");
361  CHECK(softmaxLayer4Out.GetTensorHandleFactoryId() == "TestHandleFactoryD1");
362 
363  // Check that the correct strategy was selected
365  CHECK((softmaxLayer1Out.GetEdgeStrategyForConnection(0) == EdgeStrategy::DirectCompatibility));
366  CHECK((softmaxLayer2Out.GetEdgeStrategyForConnection(0) == EdgeStrategy::CopyToTarget));
367  CHECK((softmaxLayer3Out.GetEdgeStrategyForConnection(0) == EdgeStrategy::ExportToTarget));
368  CHECK((softmaxLayer4Out.GetEdgeStrategyForConnection(0) == EdgeStrategy::DirectCompatibility));
369 
370  graph.AddCompatibilityLayers(backends, registry);
371 
372  // Test for copy layers
373  int copyCount= 0;
374  graph.ForEachLayer([&copyCount](Layer* layer)
375  {
376  if (layer->GetType() == LayerType::MemCopy)
377  {
378  copyCount++;
379  }
380  });
381  CHECK(copyCount == 1);
382 
383  // Test for import layers
384  int importCount= 0;
385  graph.ForEachLayer([&importCount](Layer *layer)
386  {
387  if (layer->GetType() == LayerType::MemImport)
388  {
389  importCount++;
390  }
391  });
392  CHECK(importCount == 1);
393 }
394 
395 TEST_CASE("RegisterCopyAndImportFactoryPairTest")
396 {
398  ITensorHandleFactory::FactoryId copyId = "CopyFactoryId";
399  ITensorHandleFactory::FactoryId importId = "ImportFactoryId";
400  registry.RegisterCopyAndImportFactoryPair(copyId, importId);
401 
402  // Get mathing import factory id correctly
403  CHECK((registry.GetMatchingImportFactoryId(copyId) == importId));
404 
405  // Return empty id when Invalid Id is given
406  CHECK((registry.GetMatchingImportFactoryId("InvalidFactoryId") == ""));
407 }
408 
409 }
TEST_SUITE("TestConstTensorLayerVisitor")
void RegisterMemoryManager(std::shared_ptr< IMemoryManager > memoryManger)
Register a memory manager with shared ownership.
DataLayout
Definition: Types.hpp:49
No strategy has been defined. Used internally to verify integrity of optimizations.
LayerT * AddLayer(Args &&... args)
Adds a new layer, of type LayerType, to the graph constructed with the arguments passed.
Definition: Graph.hpp:420
void RegisterFactory(std::unique_ptr< ITensorHandleFactory > allocator)
Register a TensorHandleFactory and transfer ownership.
Source backends tensor data can be exported to destination backend tensor without copy...
std::shared_ptr< ILayerSupport > ILayerSupportSharedPtr
int Connect(InputSlot &destination)
Definition: Layer.cpp:86
EdgeStrategy GetEdgeStrategyForConnection(unsigned int connectionIdx) const
Definition: Layer.cpp:189
unsigned int MemorySourceFlags
Copyright (c) 2021 ARM Limited and Contributors.
void IgnoreUnused(Ts &&...)
void SetBackendId(const BackendId &id)
Definition: Layer.hpp:275
Destination backend can work directly with tensors on source backend.
void RegisterCopyAndImportFactoryPair(ITensorHandleFactory::FactoryId copyFactoryId, ITensorHandleFactory::FactoryId importFactoryId)
Register a pair of TensorHandleFactory Id for Memory Copy and TensorHandleFactory Id for Memory Impor...
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:321
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: OutputLayer.hpp:13
void ForEachLayer(Func func) const
Definition: Graph.hpp:40
ITensorHandleFactory::FactoryId FactoryId
This layer represents a softmax operation.
LayerType GetType() const override
Returns the armnn::LayerType of this layer.
Definition: Layer.hpp:270
std::shared_ptr< ILayerSupport > ILayerSupportSharedPtr
ITensorHandleFactory::FactoryId GetMatchingImportFactoryId(ITensorHandleFactory::FactoryId copyFactoryId)
Get a matching TensorHandleFatory Id for Memory Import given TensorHandleFactory Id for Memory Copy...
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: InputLayer.hpp:13
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
Definition: Layer.hpp:323
OptimizationResult SelectTensorHandleStrategy(Graph &optGraph, BackendsMap &backends, TensorHandleFactoryRegistry &registry, bool importEnabled, Optional< std::vector< std::string > &> errMessages)
Definition: Network.cpp:1611
ITensorHandleFactory::FactoryId GetTensorHandleFactoryId() const
Definition: Layer.cpp:179
Graph & TopologicalSort()
Sorts layers in topological order and return this.
Definition: Graph.hpp:182
A SoftmaxDescriptor for the SoftmaxLayer.
void AddCompatibilityLayers(std::map< BackendId, std::unique_ptr< class IBackendInternal >> &backends, TensorHandleFactoryRegistry &registry)
Modifies the graph in-place, removing edges connecting layers using different compute devices...
Definition: Graph.cpp:301
std::map< BackendId, std::unique_ptr< class IBackendInternal > > BackendsMap
Definition: Network.hpp:287