2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
6 #include <armnn/ArmNN.hpp>
7 #include <armnn/Graph.hpp>
8 #include <armnn/Network.hpp>
10 #include <backends/reference/RefWorkloadFactory.hpp>
12 #include <boost/test/unit_test.hpp>
14 BOOST_AUTO_TEST_SUITE(OptimizedNetwork)
16 BOOST_AUTO_TEST_CASE(SerializeToDot)
21 auto input = net.AddInputLayer(0);
22 auto add = net.AddAdditionLayer();
23 auto output = net.AddOutputLayer(0);
26 input->GetOutputSlot(0).Connect(add->GetInputSlot(0));
27 input->GetOutputSlot(0).Connect(add->GetInputSlot(1));
28 add->GetOutputSlot(0).Connect(output->GetInputSlot(0));
30 armnn::TensorShape shape({4});
31 armnn::TensorInfo info(shape, armnn::DataType::Float32);
32 input->GetOutputSlot(0).SetTensorInfo(info);
33 add->GetOutputSlot(0).SetTensorInfo(info);
35 armnn::IRuntime::CreationOptions options;
36 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
38 std::vector<armnn::BackendId> backends = {armnn::Compute::CpuRef};
39 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
41 std::ostringstream ss;
42 optimizedNet->SerializeToDot(ss);
44 auto inputId = input->GetGuid();
45 auto addId = add->GetGuid();
46 auto outputId = output->GetGuid();
48 std::stringstream expected;
50 "digraph Optimized {\n"
51 " node [shape=\"record\"];\n"
52 " edge [fontsize=8 fontcolor=\"blue\" fontname=\"arial-bold\"];\n"
53 " " << inputId << " [label=\"{Input}\"];\n"
54 " " << addId << " [label=\"{Addition}\"];\n"
55 " " << outputId << " [label=\"{Output}\"];\n"
56 " " << inputId << " -> " << addId << " [label=< [4] >];\n"
57 " " << inputId << " -> " << addId << " [label=< [4] >];\n"
58 " " << addId << " -> " << outputId << " [label=< [4] >];\n"
61 BOOST_TEST(ss.str() == expected.str());
64 BOOST_AUTO_TEST_CASE(OptimizeValidateDeviceNonSupportLayerNoFallback)
66 // build up the structure of the network
67 armnn::INetworkPtr net(armnn::INetwork::Create());
69 armnn::IConnectableLayer* input = net->AddInputLayer(0);
71 // This layer configuration isn't supported by CpuAcc and isn't allowed to fall back, so Optimize will return null.
72 armnn::NormalizationDescriptor descriptor;
73 armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
75 armnn::IConnectableLayer* output = net->AddOutputLayer(0);
77 input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
78 normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
80 input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
81 normalize->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
83 armnn::IRuntime::CreationOptions options;
84 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
86 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
87 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
91 BOOST_AUTO_TEST_CASE(OptimizeValidateDeviceNonSupportLayerWithFallback)
93 // build up the structure of the network
94 armnn::INetworkPtr net(armnn::INetwork::Create());
96 armnn::IConnectableLayer* input = net->AddInputLayer(0);
98 // This layer configuration isn't supported by CpuAcc but it allows to fallback to CpuRef.
99 armnn::NormalizationDescriptor descriptor;
100 armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
102 armnn::IConnectableLayer* output = net->AddOutputLayer(0);
104 input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
105 normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
107 input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
108 normalize->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
110 armnn::IRuntime::CreationOptions options;
111 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
113 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc, armnn::Compute::CpuRef };
114 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
115 BOOST_REQUIRE(optNet);
117 for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
119 // If NEON is enabled, Input and Output layers are supported by CpuAcc,
120 // the other layers are supported by CpuRef.
121 // If NEON is not enabled, all layers are supported by CpuRef.
122 #if ARMCOMPUTENEON_ENABLED
123 if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
125 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
127 else if (layer->GetType() == armnn::LayerType::Normalization)
129 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
132 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
137 BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsUndefinedComputeDevice)
139 const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
143 armnn::NormalizationDescriptor nmDesc;
144 armnn::ActivationDescriptor acDesc;
157 armnn::IConnectableLayer* layer = net.AddInputLayer(0, "in");
158 layer->GetOutputSlot(0).SetTensorInfo(desc);
160 armnn::IConnectableLayer* const normLayer = net.AddNormalizationLayer(nmDesc, "nm");
162 layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
163 normLayer->GetOutputSlot(0).SetTensorInfo(desc);
165 layer = net.AddActivationLayer(acDesc, "ac");
167 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
168 layer->GetOutputSlot(0).SetTensorInfo(desc);
170 armnn::IConnectableLayer* prevLayer = layer;
171 layer = net.AddMultiplicationLayer("ml");
173 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
174 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
175 layer->GetOutputSlot(0).SetTensorInfo(desc);
178 armnn::SoftmaxDescriptor softmaxDescriptor;
179 layer = net.AddSoftmaxLayer(softmaxDescriptor, "sm");
181 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
182 layer->GetOutputSlot(0).SetTensorInfo(desc);
185 layer = net.AddOutputLayer(0, "ot");
187 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
189 armnn::IRuntime::CreationOptions options;
190 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
192 std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined };
194 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
195 BOOST_CHECK(!optNet);
199 BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsUndefinedComputeDeviceWithFallback)
201 const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
205 armnn::NormalizationDescriptor nmDesc;
206 armnn::ActivationDescriptor acDesc;
219 armnn::IConnectableLayer* layer = net.AddInputLayer(0, "in");
220 layer->GetOutputSlot(0).SetTensorInfo(desc);
222 armnn::IConnectableLayer* const normLayer = net.AddNormalizationLayer(nmDesc, "nm");
224 layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
225 normLayer->GetOutputSlot(0).SetTensorInfo(desc);
227 layer = net.AddActivationLayer(acDesc, "ac");
229 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
230 layer->GetOutputSlot(0).SetTensorInfo(desc);
232 armnn::IConnectableLayer* prevLayer = layer;
233 layer = net.AddMultiplicationLayer("ml");
235 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
236 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
237 layer->GetOutputSlot(0).SetTensorInfo(desc);
240 armnn::SoftmaxDescriptor softmaxDescriptor;
241 layer = net.AddSoftmaxLayer(softmaxDescriptor, "sm");
243 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
244 layer->GetOutputSlot(0).SetTensorInfo(desc);
247 layer = net.AddOutputLayer(0, "ot");
249 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
251 armnn::IRuntime::CreationOptions options;
252 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
254 std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined, armnn::Compute::CpuRef };
256 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
259 // validate workloads
260 armnn::RefWorkloadFactory fact;
261 for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
263 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
264 BOOST_CHECK_NO_THROW(
265 layer->CreateWorkload(static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph(), fact));
269 BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsDuplicateComputeDeviceWithFallback)
271 // build up the structure of the network
272 armnn::INetworkPtr net(armnn::INetwork::Create());
274 armnn::IConnectableLayer* input = net->AddInputLayer(0);
276 // This layer configuration isn't supported by CpuAcc but it allows to fallback to CpuRef.
277 armnn::NormalizationDescriptor descriptor;
278 armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
280 armnn::IConnectableLayer* output = net->AddOutputLayer(0);
282 input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
283 normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
285 input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
286 normalize->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
288 armnn::IRuntime::CreationOptions options;
289 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
291 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc,
292 armnn::Compute::GpuAcc,
293 armnn::Compute::CpuRef };
295 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
296 BOOST_REQUIRE(optNet);
298 for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
300 // If NEON is enabled, Input and Output layers are supported by CpuAcc,
301 // the other layers are supported by CpuRef.
302 // If only CL is enabled, Input and Output layers are supported by GpuAcc,
303 // the other layers are supported by CpuRef.
304 // If neither NEON, nor CL is enabled, all layers are supported by CpuRef.
305 #if ARMCOMPUTENEON_ENABLED
306 if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
308 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
310 else if (layer->GetType() == armnn::LayerType::Normalization)
312 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
314 #elif ARMCOMPUTECL_ENABLED
315 if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
317 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::GpuAcc);
319 else if (layer->GetType() == armnn::LayerType::Normalization)
321 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
324 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
329 BOOST_AUTO_TEST_SUITE_END()