ArmNN
 22.05
OptimizedNetworkTests.cpp File Reference
#include <CommonTestUtils.hpp>
#include <Graph.hpp>
#include <Network.hpp>
#include <reference/RefWorkloadFactory.hpp>
#include <doctest/doctest.h>

Go to the source code of this file.

Functions

 TEST_SUITE ("OptimizedNetwork")
 

Function Documentation

◆ TEST_SUITE()

TEST_SUITE ( "OptimizedNetwork"  )

Definition at line 15 of file OptimizedNetworkTests.cpp.

References Graph::AllocateDynamicBuffers(), ARMNN_NO_DEPRECATE_WARN_BEGIN, ARMNN_NO_DEPRECATE_WARN_END, IOutputSlot::Connect(), armnn::CpuAcc, armnn::CpuRef, IRuntime::Create(), INetwork::Create(), IOptimizedNetwork::Destroy(), armnn::Float32, armnn::GetGraphForTesting(), IConnectableLayer::GetInputSlot(), IConnectableLayer::GetOutputSlot(), armnn::GpuAcc, armnn::Input, Convolution2dDescriptor::m_BiasEnabled, Convolution2dDescriptor::m_DataLayout, Convolution2dDescriptor::m_DilationX, Convolution2dDescriptor::m_DilationY, Convolution2dDescriptor::m_PadBottom, Convolution2dDescriptor::m_PadLeft, Convolution2dDescriptor::m_PadRight, Convolution2dDescriptor::m_PadTop, Convolution2dDescriptor::m_StrideX, Convolution2dDescriptor::m_StrideY, armnn::NHWC, armnn::Normalization, armnn::Optimize(), armnn::Output, TensorInfo::SetConstant(), IOutputSlot::SetTensorInfo(), armnn::Success, and armnn::Undefined.

16 {
17 TEST_CASE("SerializeToDot")
18 {
19  // build up the structure of the network
21 
22  //Defines layers.
23  auto input = net->AddInputLayer(0);
24  auto add = net->AddAdditionLayer();
25  auto output = net->AddOutputLayer(0);
26 
27  // Connects layers.
28  input->GetOutputSlot(0).Connect(add->GetInputSlot(0));
29  input->GetOutputSlot(0).Connect(add->GetInputSlot(1));
30  add->GetOutputSlot(0).Connect(output->GetInputSlot(0));
31 
32  armnn::TensorShape shape({4});
34  input->GetOutputSlot(0).SetTensorInfo(info);
35  add->GetOutputSlot(0).SetTensorInfo(info);
36 
39 
40  std::vector<armnn::BackendId> backends = {armnn::Compute::CpuRef};
41  armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
42 
43  std::ostringstream ss;
44  optimizedNet->SerializeToDot(ss);
45 
46  auto inputId = input->GetGuid();
47  auto addId = add->GetGuid();
48  auto outputId = output->GetGuid();
49 
50  std::stringstream expected;
51  expected <<
52  "digraph Optimized {\n"
53  " node [shape=\"record\"];\n"
54  " edge [fontsize=8 fontcolor=\"blue\" fontname=\"arial-bold\"];\n"
55  " " << inputId << " [label=\"{Input|Guid : " << inputId << "\\lLayerType : Input\\l"
56  "BackendID : CpuRef\\l}\"];\n"
57  " " << addId << " [label=\"{Addition|Guid : " << addId << "\\lLayerType : Addition\\l"
58  "BackendID : CpuRef\\l}\"];\n"
59  " " << outputId << " [label=\"{Output|Guid : " << outputId << "\\lLayerType : Output\\l"
60  "BackendID : CpuRef\\l}\"];\n"
61  " " << inputId << " -> " << addId << " [label=< [4] >];\n"
62  " " << inputId << " -> " << addId << " [label=< [4] >];\n"
63  " " << addId << " -> " << outputId << " [label=< [4] >];\n"
64  "}\n";
65 
66  CHECK(ss.str() == expected.str());
67 }
68 
69 TEST_CASE("OptimizeValidateDeviceNonSupportLayerNoFallback")
70 {
71  // build up the structure of the network
73 
74  armnn::IConnectableLayer* input = net->AddInputLayer(0);
75 
76  // This layer configuration isn't supported by CpuAcc and isn't allowed to fall back, so Optimize will return null.
78  armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
79 
80  armnn::IConnectableLayer* output = net->AddOutputLayer(0);
81 
82  input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
83  normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
84 
87 
90 
91  std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
92  std::vector<std::string> errMessages;
93 
94  try
95  {
96  Optimize(*net, backends, runtime->GetDeviceSpec(), armnn::OptimizerOptions(), errMessages);
97  FAIL("Should have thrown an exception.");
98  }
99  catch (const armnn::InvalidArgumentException&)
100  {
101  // Different exceptions are thrown on different backends
102  }
103  CHECK(errMessages.size() > 0);
104 }
105 
106 TEST_CASE("OptimizeValidateDeviceNonSupportLayerWithFallback")
107 {
108  // build up the structure of the network
110 
111  armnn::IConnectableLayer* input = net->AddInputLayer(0);
112 
113  // This layer configuration isn't supported by CpuAcc but it allows to fallback to CpuRef.
115  armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
116 
117  armnn::IConnectableLayer* output = net->AddOutputLayer(0);
118 
119  input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
120  normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
121 
124 
127 
128  std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc, armnn::Compute::CpuRef };
129  armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
130  REQUIRE(optNet);
131 
132  armnn::Graph& graph = GetGraphForTesting(optNet.get());
133  graph.AllocateDynamicBuffers();
134 
135  for (auto&& layer : graph)
136  {
137  // If NEON is enabled, Input and Output layers are supported by CpuAcc,
138  // the other layers are supported by CpuRef.
139  // If NEON is not enabled, all layers are supported by CpuRef.
140 #if defined(ARMCOMPUTENEON_ENABLED)
141  if (layer->GetType() == armnn::LayerType::Output)
142  {
143  CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
144  }
145  else if (layer->GetType() == armnn::LayerType::Normalization)
146  {
147  CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
148  }
149 #else
150  CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
151 #endif
152  }
153 }
154 
155 TEST_CASE("OptimizeValidateWorkloadsUndefinedComputeDevice")
156 {
157  const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
158 
159  // build up the structure of the network
161 
164 
165  // in
166  // |
167  // nm
168  // / |
169  // ac |
170  // \ |
171  // ml
172  // |
173  // sm
174  // |
175  // ot
176  armnn::IConnectableLayer* layer = net->AddInputLayer(0, "in");
177  layer->GetOutputSlot(0).SetTensorInfo(desc);
178 
179  armnn::IConnectableLayer* const normLayer = net->AddNormalizationLayer(nmDesc, "nm");
180 
181  layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
182  normLayer->GetOutputSlot(0).SetTensorInfo(desc);
183 
184  layer = net->AddActivationLayer(acDesc, "ac");
185 
186  normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
187  layer->GetOutputSlot(0).SetTensorInfo(desc);
188 
189  armnn::IConnectableLayer* prevLayer = layer;
190  layer = net->AddMultiplicationLayer("ml");
191 
192  prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
193  normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
194  layer->GetOutputSlot(0).SetTensorInfo(desc);
195 
196  prevLayer = layer;
197  armnn::SoftmaxDescriptor softmaxDescriptor;
198  layer = net->AddSoftmaxLayer(softmaxDescriptor, "sm");
199 
200  prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
201  layer->GetOutputSlot(0).SetTensorInfo(desc);
202 
203  prevLayer = layer;
204  layer = net->AddOutputLayer(0, "ot");
205 
206  prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
207 
210 
211  std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined };
212  std::vector<std::string> errMessages;
213 
214  try
215  {
216  Optimize(*net, backends, runtime->GetDeviceSpec(), armnn::OptimizerOptions(), errMessages);
217  FAIL("Should have thrown an exception.");
218  }
219  catch (const armnn::InvalidArgumentException&)
220  {
221  // Different exceptions are thrown on different backends
222  }
223  CHECK(errMessages.size() > 0);
224 }
225 
226 TEST_CASE("OptimizeValidateWorkloadsUndefinedComputeDeviceWithFallback")
227 {
228  const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
229 
230  // build up the structure of the network
232 
235 
236  // in
237  // |
238  // nm
239  // / |
240  // ac |
241  // \ |
242  // ml
243  // |
244  // sm
245  // |
246  // ot
247  armnn::IConnectableLayer* layer = net->AddInputLayer(0, "in");
248  layer->GetOutputSlot(0).SetTensorInfo(desc);
249 
250  armnn::IConnectableLayer* const normLayer = net->AddNormalizationLayer(nmDesc, "nm");
251 
252  layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
253  normLayer->GetOutputSlot(0).SetTensorInfo(desc);
254 
255  layer = net->AddActivationLayer(acDesc, "ac");
256 
257  normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
258  layer->GetOutputSlot(0).SetTensorInfo(desc);
259 
260  armnn::IConnectableLayer* prevLayer = layer;
261  layer = net->AddMultiplicationLayer("ml");
262 
263  prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
264  normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
265  layer->GetOutputSlot(0).SetTensorInfo(desc);
266 
267  prevLayer = layer;
268  armnn::SoftmaxDescriptor softmaxDescriptor;
269  layer = net->AddSoftmaxLayer(softmaxDescriptor, "sm");
270 
271  prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
272  layer->GetOutputSlot(0).SetTensorInfo(desc);
273 
274  prevLayer = layer;
275  layer = net->AddOutputLayer(0, "ot");
276 
277  prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
278 
281 
282  std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined, armnn::Compute::CpuRef };
283 
284  armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
285  CHECK(optNet);
286 
287  armnn::Graph& graph = GetGraphForTesting(optNet.get());
288  graph.AllocateDynamicBuffers();
289 
290  // validate workloads
292  for (auto&& layer : graph)
293  {
294  CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
295  CHECK_NOTHROW(
296  layer->CreateWorkload(fact));
297  }
298 }
299 
300 TEST_CASE("OptimizeValidateWorkloadsDuplicateComputeDeviceWithFallback")
301 {
302  // build up the structure of the network
304 
305  armnn::IConnectableLayer* input = net->AddInputLayer(0);
306 
307  // This layer configuration isn't supported by CpuAcc but it allows to fallback to CpuRef.
309  armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
310 
311  armnn::IConnectableLayer* output = net->AddOutputLayer(0);
312 
313  input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
314  normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
315 
318 
321 
322  std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc,
324  armnn::Compute::CpuRef };
325 
326  armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
327  REQUIRE(optNet);
328 
329  armnn::Graph& graph = GetGraphForTesting(optNet.get());
330  graph.AllocateDynamicBuffers();
331 
332  for (auto&& layer : graph)
333  {
334  // If NEON is enabled, Input and Output layers are supported by CpuAcc,
335  // the other layers are supported by CpuRef.
336  // If only CL is enabled, Input and Output layers are supported by GpuAcc,
337  // the other layers are supported by CpuRef.
338  // If neither NEON, nor CL is enabled, all layers are supported by CpuRef.
339 #if defined(ARMCOMPUTENEON_ENABLED)
340  if (layer->GetType() == armnn::LayerType::Input)
341  {
342  CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
343  }
344  else if (layer->GetType() == armnn::LayerType::Output)
345  {
346  CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
347  }
348  else if (layer->GetType() == armnn::LayerType::Normalization)
349  {
350  CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
351  }
352 #elif defined(ARMCOMPUTECL_ENABLED)
353  if (layer->GetType() == armnn::LayerType::Input)
354  {
355  CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
356  }
357  else if (layer->GetType() == armnn::LayerType::Output)
358  {
359  CHECK(layer->GetBackendId() == armnn::Compute::GpuAcc);
360  }
361  else if (layer->GetType() == armnn::LayerType::Normalization)
362  {
363  CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
364  }
365 #else
366  CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
367 #endif
368  }
369 }
370 
371 TEST_CASE("OptimizeNetworkCopy")
372 {
374  armnn::IRuntimePtr runtime = armnn::IRuntime::Create(options);
375  std::vector<armnn::NetworkId> networkIds;
376 
377  const std::string layerName("convolution2d");
378  const armnn::TensorInfo inputInfo ({ 1, 5, 5, 1 }, armnn::DataType::Float32);
379  const armnn::TensorInfo outputInfo({ 1, 2, 2, 1 }, armnn::DataType::Float32);
380 
381  const armnn::TensorInfo weightsInfo({ 1, 3, 3, 1 }, armnn::DataType::Float32, 0.0f, 0, true);
382  const armnn::TensorInfo biasesInfo ({ 1 }, armnn::DataType::Float32, 0.0f, 0, true);
383 
384  std::vector<float> weightsData = GenerateRandomData<float>(weightsInfo.GetNumElements());
385  armnn::ConstTensor weights(weightsInfo, weightsData);
386 
387  std::vector<float> biasesData = GenerateRandomData<float>(biasesInfo.GetNumElements());
388  armnn::ConstTensor biases(biasesInfo, biasesData);
389 
391  descriptor.m_PadLeft = 1;
392  descriptor.m_PadRight = 1;
393  descriptor.m_PadTop = 1;
394  descriptor.m_PadBottom = 1;
395  descriptor.m_StrideX = 2;
396  descriptor.m_StrideY = 2;
397  descriptor.m_DilationX = 2;
398  descriptor.m_DilationY = 2;
399  descriptor.m_BiasEnabled = true;
401 
403  armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0);
404 
406  armnn::IConnectableLayer* const convLayer =
407  network->AddConvolution2dLayer(descriptor,
408  weights,
410  layerName.c_str());
412  armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0);
413 
414  inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
415  convLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
416 
417  inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
418  convLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
419 
420  std::vector<armnn::BackendId> preferredBackends { "CpuRef" };
421  armnn::ModelOptions modelOptions;
422  armnn::OptimizerOptions optimizerOptions(false, false, false, false, modelOptions);
423  std::vector<std::string> errorMessages;
424 
425  // optimize the network.
426  armnn::IOptimizedNetworkPtr optNet = Optimize(*network,
427  preferredBackends,
428  runtime->GetDeviceSpec(),
429  optimizerOptions,
431 
432  for (unsigned int i = 0; i < 2; ++i)
433  {
434  armnn::ModelOptions optimizedModelOptions;
435  auto copy = armnn::IOptimizedNetworkPtr(new armnn::IOptimizedNetwork(*optNet.get(), optimizedModelOptions),
437 
438  CHECK(copy);
439 
440  armnn::NetworkId netId;
441  std::string errorMessage;
442 
443  CHECK(armnn::Status::Success == runtime->LoadNetwork(netId, std::move(copy), errorMessage));
444 
445  // Record the networkID for the loaded network
446  networkIds.emplace_back(netId);
447  }
448  armnn::NetworkId optNetId;
449  std::string errorMessage;
450 
451  // Load the original optNet
452  CHECK(armnn::Status::Success == runtime->LoadNetwork(optNetId, std::move(optNet), errorMessage));
453 
454  std::vector<float> inputData = GenerateRandomData<float>(runtime->GetInputTensorInfo(optNetId, 0).GetNumElements());
455  std::vector<float> outputData(runtime->GetOutputTensorInfo(optNetId, 0).GetNumElements());
456 
457  armnn::TensorInfo inputTensorInfo = runtime->GetInputTensorInfo(optNetId, 0);
458  inputTensorInfo.SetConstant(true);
459  armnn::InputTensors inputTensors
460  {
461  {
462  0, armnn::ConstTensor(inputTensorInfo, inputData.data())
463  }
464  };
465  armnn::OutputTensors outputTensors
466  {
467  {
468  0, armnn::Tensor(runtime->GetOutputTensorInfo(optNetId, 0), outputData.data())
469  }
470  };
471  runtime->EnqueueWorkload(optNetId, inputTensors, outputTensors);
472  runtime->UnloadNetwork(optNetId);
473 
474  // Record the networkID for the loaded network
475  for (unsigned int i = 0; i < networkIds.size(); ++i)
476  {
477  armnn::NetworkId netId = networkIds[i];
478  std::vector<float> copyOutputData(runtime->GetOutputTensorInfo(netId, 0).GetNumElements());
479 
480  armnn::TensorInfo inputTensorInfo2 = runtime->GetInputTensorInfo(netId, 0);
481  inputTensorInfo2.SetConstant(true);
482  armnn::InputTensors copyInputTensors
483  {
484  {
485  0, armnn::ConstTensor(inputTensorInfo2, inputData.data())
486  }
487  };
488  armnn::OutputTensors copyOutputTensors
489  {
490  {
491  0, armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), copyOutputData.data())
492  }
493  };
494  runtime->EnqueueWorkload(netId, copyInputTensors, copyOutputTensors);
495  runtime->UnloadNetwork(netId);
496 
497  // Check results are identical to "original" version
498  for (unsigned int j = 0; j < outputData.size(); ++j)
499  {
500  CHECK(outputData[j] == copyOutputData[j]);
501  }
502  }
503 }
504 
505 }
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:49
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:66
CPU Execution: Reference C++ kernels.
#define ARMNN_NO_DEPRECATE_WARN_BEGIN
Definition: Deprecated.hpp:33
std::vector< BackendOptions > ModelOptions
A Convolution2dDescriptor for the Convolution2dLayer.
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:33
std::vector< std::pair< LayerBindingId, class ConstTensor > > InputTensors
Definition: Tensor.hpp:392
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_DilationY
Dilation along y axis.
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
Definition: Tensor.hpp:319
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
#define ARMNN_NO_DEPRECATE_WARN_END
Definition: Deprecated.hpp:34
IOptimizedNetworkPtr Optimize(const INetwork &network, const std::vector< BackendId > &backendPreferences, const IDeviceSpec &deviceSpec, const OptimizerOptions &options=OptimizerOptions(), Optional< std::vector< std::string > &> messages=EmptyOptional())
Create an optimized version of the network.
Definition: Network.cpp:1847
int NetworkId
Definition: IRuntime.hpp:27
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:327
std::vector< std::pair< LayerBindingId, class Tensor > > OutputTensors
Definition: Tensor.hpp:393
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:242
GPU Execution: OpenCL: ArmCompute.
ArmNN performs an optimization on each model/network before it gets loaded for execution.
Definition: INetwork.hpp:137
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:36
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
virtual LayerType GetType() const =0
Returns the armnn::LayerType of this layer.
Graph & GetGraphForTesting(IOptimizedNetwork *optNet)
Definition: TestUtils.cpp:49
uint32_t m_DilationX
Dilation along x axis.
CPU Execution: NEON: ArmCompute.
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
void SetConstant(const bool IsConstant=true)
Marks the data corresponding to this tensor info as constant.
Definition: Tensor.cpp:514
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:241
virtual int Connect(IInputSlot &destination)=0
A NormalizationDescriptor for the NormalizationLayer.
Status AllocateDynamicBuffers()
Allocates memory for all tensors under output tensor handers of each layer.
Definition: Graph.cpp:181
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:476
static void Destroy(IOptimizedNetwork *network)
Definition: Network.cpp:500
A SoftmaxDescriptor for the SoftmaxLayer.
uint32_t m_PadLeft
Padding left value in the width dimension.