blob: 72a35f99e0cbfd0c135033410494b34838af4c3a [file] [log] [blame]
Aron Virginas-Tar70104002018-10-24 15:33:28 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include <armnn/ArmNN.hpp>
7#include <armnn/Graph.hpp>
8#include <armnn/Network.hpp>
9
10#include <backends/reference/RefWorkloadFactory.hpp>
11
12#include <boost/test/unit_test.hpp>
13
14BOOST_AUTO_TEST_SUITE(OptimizedNetwork)
15
16BOOST_AUTO_TEST_CASE(SerializeToDot)
17{
18 armnn::Network net;
19
20 //Defines layers.
21 auto input = net.AddInputLayer(0);
22 auto add = net.AddAdditionLayer();
23 auto output = net.AddOutputLayer(0);
24
25 // Connects layers.
26 input->GetOutputSlot(0).Connect(add->GetInputSlot(0));
27 input->GetOutputSlot(0).Connect(add->GetInputSlot(1));
28 add->GetOutputSlot(0).Connect(output->GetInputSlot(0));
29
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);
34
35 armnn::IRuntime::CreationOptions options;
36 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
37
38 std::vector<armnn::BackendId> backends = {armnn::Compute::CpuRef};
39 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
40
41 std::ostringstream ss;
42 optimizedNet->SerializeToDot(ss);
43
44 auto inputId = input->GetGuid();
45 auto addId = add->GetGuid();
46 auto outputId = output->GetGuid();
47
48 std::stringstream expected;
49 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"
59 "}\n";
60
61 BOOST_TEST(ss.str() == expected.str());
62}
63
64BOOST_AUTO_TEST_CASE(OptimizeValidateDeviceNonSupportLayerNoFallback)
65{
66 // build up the structure of the network
67 armnn::INetworkPtr net(armnn::INetwork::Create());
68
69 armnn::IConnectableLayer* input = net->AddInputLayer(0);
70
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);
74
75 armnn::IConnectableLayer* output = net->AddOutputLayer(0);
76
77 input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
78 normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
79
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));
82
83 armnn::IRuntime::CreationOptions options;
84 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
85
86 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
87 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
88 BOOST_CHECK(!optNet);
89}
90
91BOOST_AUTO_TEST_CASE(OptimizeValidateDeviceNonSupportLayerWithFallback)
92{
93 // build up the structure of the network
94 armnn::INetworkPtr net(armnn::INetwork::Create());
95
96 armnn::IConnectableLayer* input = net->AddInputLayer(0);
97
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);
101
102 armnn::IConnectableLayer* output = net->AddOutputLayer(0);
103
104 input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
105 normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
106
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));
109
110 armnn::IRuntime::CreationOptions options;
111 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
112
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);
116
117 for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
118 {
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)
124 {
125 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
126 }
127 else if (layer->GetType() == armnn::LayerType::Normalization)
128 {
129 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
130 }
131#else
132 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
133#endif
134 }
135}
136
137BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsUndefinedComputeDevice)
138{
139 const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
140
141 armnn::Network net;
142
143 armnn::NormalizationDescriptor nmDesc;
144 armnn::ActivationDescriptor acDesc;
145
146 // in
147 // |
148 // nm
149 // / |
150 // ac |
151 // \ |
152 // ml
153 // |
154 // sm
155 // |
156 // ot
157 armnn::IConnectableLayer* layer = net.AddInputLayer(0, "in");
158 layer->GetOutputSlot(0).SetTensorInfo(desc);
159
160 armnn::IConnectableLayer* const normLayer = net.AddNormalizationLayer(nmDesc, "nm");
161
162 layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
163 normLayer->GetOutputSlot(0).SetTensorInfo(desc);
164
165 layer = net.AddActivationLayer(acDesc, "ac");
166
167 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
168 layer->GetOutputSlot(0).SetTensorInfo(desc);
169
170 armnn::IConnectableLayer* prevLayer = layer;
171 layer = net.AddMultiplicationLayer("ml");
172
173 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
174 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
175 layer->GetOutputSlot(0).SetTensorInfo(desc);
176
177 prevLayer = layer;
178 armnn::SoftmaxDescriptor softmaxDescriptor;
179 layer = net.AddSoftmaxLayer(softmaxDescriptor, "sm");
180
181 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
182 layer->GetOutputSlot(0).SetTensorInfo(desc);
183
184 prevLayer = layer;
185 layer = net.AddOutputLayer(0, "ot");
186
187 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
188
189 armnn::IRuntime::CreationOptions options;
190 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
191
192 std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined };
193
194 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
195 BOOST_CHECK(!optNet);
196
197}
198
199BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsUndefinedComputeDeviceWithFallback)
200{
201 const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
202
203 armnn::Network net;
204
205 armnn::NormalizationDescriptor nmDesc;
206 armnn::ActivationDescriptor acDesc;
207
208 // in
209 // |
210 // nm
211 // / |
212 // ac |
213 // \ |
214 // ml
215 // |
216 // sm
217 // |
218 // ot
219 armnn::IConnectableLayer* layer = net.AddInputLayer(0, "in");
220 layer->GetOutputSlot(0).SetTensorInfo(desc);
221
222 armnn::IConnectableLayer* const normLayer = net.AddNormalizationLayer(nmDesc, "nm");
223
224 layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
225 normLayer->GetOutputSlot(0).SetTensorInfo(desc);
226
227 layer = net.AddActivationLayer(acDesc, "ac");
228
229 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
230 layer->GetOutputSlot(0).SetTensorInfo(desc);
231
232 armnn::IConnectableLayer* prevLayer = layer;
233 layer = net.AddMultiplicationLayer("ml");
234
235 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
236 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
237 layer->GetOutputSlot(0).SetTensorInfo(desc);
238
239 prevLayer = layer;
240 armnn::SoftmaxDescriptor softmaxDescriptor;
241 layer = net.AddSoftmaxLayer(softmaxDescriptor, "sm");
242
243 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
244 layer->GetOutputSlot(0).SetTensorInfo(desc);
245
246 prevLayer = layer;
247 layer = net.AddOutputLayer(0, "ot");
248
249 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
250
251 armnn::IRuntime::CreationOptions options;
252 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
253
254 std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined, armnn::Compute::CpuRef };
255
256 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
257 BOOST_CHECK(optNet);
258
259 // validate workloads
260 armnn::RefWorkloadFactory fact;
261 for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
262 {
263 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
264 BOOST_CHECK_NO_THROW(
265 layer->CreateWorkload(static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph(), fact));
266 }
267}
268
269BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsDuplicateComputeDeviceWithFallback)
270{
271 // build up the structure of the network
272 armnn::INetworkPtr net(armnn::INetwork::Create());
273
274 armnn::IConnectableLayer* input = net->AddInputLayer(0);
275
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);
279
280 armnn::IConnectableLayer* output = net->AddOutputLayer(0);
281
282 input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
283 normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
284
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));
287
288 armnn::IRuntime::CreationOptions options;
289 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
290
291 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc,
292 armnn::Compute::GpuAcc,
293 armnn::Compute::CpuRef };
294
295 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
296 BOOST_REQUIRE(optNet);
297
298 for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
299 {
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)
307 {
308 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
309 }
310 else if (layer->GetType() == armnn::LayerType::Normalization)
311 {
312 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
313 }
314#elif ARMCOMPUTECL_ENABLED
315 if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
316 {
317 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::GpuAcc);
318 }
319 else if (layer->GetType() == armnn::LayerType::Normalization)
320 {
321 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
322 }
323#else
324 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
325#endif
326 }
327}
328
329BOOST_AUTO_TEST_SUITE_END()