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