blob: 8d8824100013a32c067f173d63bc0776c5709adf [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>
Aron Virginas-Tar70104002018-10-24 15:33:28 +01007
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +00008#include <Graph.hpp>
9#include <Network.hpp>
10
11#include <reference/RefWorkloadFactory.hpp>
Aron Virginas-Tar70104002018-10-24 15:33:28 +010012
13#include <boost/test/unit_test.hpp>
14
15BOOST_AUTO_TEST_SUITE(OptimizedNetwork)
16
17BOOST_AUTO_TEST_CASE(SerializeToDot)
18{
19 armnn::Network net;
20
21 //Defines layers.
22 auto input = net.AddInputLayer(0);
23 auto add = net.AddAdditionLayer();
24 auto output = net.AddOutputLayer(0);
25
26 // Connects layers.
27 input->GetOutputSlot(0).Connect(add->GetInputSlot(0));
28 input->GetOutputSlot(0).Connect(add->GetInputSlot(1));
29 add->GetOutputSlot(0).Connect(output->GetInputSlot(0));
30
31 armnn::TensorShape shape({4});
32 armnn::TensorInfo info(shape, armnn::DataType::Float32);
33 input->GetOutputSlot(0).SetTensorInfo(info);
34 add->GetOutputSlot(0).SetTensorInfo(info);
35
36 armnn::IRuntime::CreationOptions options;
37 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
38
39 std::vector<armnn::BackendId> backends = {armnn::Compute::CpuRef};
40 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
41
42 std::ostringstream ss;
43 optimizedNet->SerializeToDot(ss);
44
45 auto inputId = input->GetGuid();
46 auto addId = add->GetGuid();
47 auto outputId = output->GetGuid();
48
49 std::stringstream expected;
50 expected <<
51 "digraph Optimized {\n"
52 " node [shape=\"record\"];\n"
53 " edge [fontsize=8 fontcolor=\"blue\" fontname=\"arial-bold\"];\n"
54 " " << inputId << " [label=\"{Input}\"];\n"
55 " " << addId << " [label=\"{Addition}\"];\n"
56 " " << outputId << " [label=\"{Output}\"];\n"
57 " " << inputId << " -> " << addId << " [label=< [4] >];\n"
58 " " << inputId << " -> " << addId << " [label=< [4] >];\n"
59 " " << addId << " -> " << outputId << " [label=< [4] >];\n"
60 "}\n";
61
62 BOOST_TEST(ss.str() == expected.str());
63}
64
65BOOST_AUTO_TEST_CASE(OptimizeValidateDeviceNonSupportLayerNoFallback)
66{
67 // build up the structure of the network
68 armnn::INetworkPtr net(armnn::INetwork::Create());
69
70 armnn::IConnectableLayer* input = net->AddInputLayer(0);
71
72 // This layer configuration isn't supported by CpuAcc and isn't allowed to fall back, so Optimize will return null.
73 armnn::NormalizationDescriptor descriptor;
74 armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
75
76 armnn::IConnectableLayer* output = net->AddOutputLayer(0);
77
78 input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
79 normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
80
81 input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
82 normalize->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
83
84 armnn::IRuntime::CreationOptions options;
85 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
86
87 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
88 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
89 BOOST_CHECK(!optNet);
90}
91
92BOOST_AUTO_TEST_CASE(OptimizeValidateDeviceNonSupportLayerWithFallback)
93{
94 // build up the structure of the network
95 armnn::INetworkPtr net(armnn::INetwork::Create());
96
97 armnn::IConnectableLayer* input = net->AddInputLayer(0);
98
99 // This layer configuration isn't supported by CpuAcc but it allows to fallback to CpuRef.
100 armnn::NormalizationDescriptor descriptor;
101 armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
102
103 armnn::IConnectableLayer* output = net->AddOutputLayer(0);
104
105 input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
106 normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
107
108 input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
109 normalize->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
110
111 armnn::IRuntime::CreationOptions options;
112 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
113
114 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc, armnn::Compute::CpuRef };
115 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
116 BOOST_REQUIRE(optNet);
117
118 for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
119 {
120 // If NEON is enabled, Input and Output layers are supported by CpuAcc,
121 // the other layers are supported by CpuRef.
122 // If NEON is not enabled, all layers are supported by CpuRef.
123#if ARMCOMPUTENEON_ENABLED
124 if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
125 {
126 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
127 }
128 else if (layer->GetType() == armnn::LayerType::Normalization)
129 {
130 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
131 }
132#else
133 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
134#endif
135 }
136}
137
138BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsUndefinedComputeDevice)
139{
140 const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
141
142 armnn::Network net;
143
144 armnn::NormalizationDescriptor nmDesc;
145 armnn::ActivationDescriptor acDesc;
146
147 // in
148 // |
149 // nm
150 // / |
151 // ac |
152 // \ |
153 // ml
154 // |
155 // sm
156 // |
157 // ot
158 armnn::IConnectableLayer* layer = net.AddInputLayer(0, "in");
159 layer->GetOutputSlot(0).SetTensorInfo(desc);
160
161 armnn::IConnectableLayer* const normLayer = net.AddNormalizationLayer(nmDesc, "nm");
162
163 layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
164 normLayer->GetOutputSlot(0).SetTensorInfo(desc);
165
166 layer = net.AddActivationLayer(acDesc, "ac");
167
168 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
169 layer->GetOutputSlot(0).SetTensorInfo(desc);
170
171 armnn::IConnectableLayer* prevLayer = layer;
172 layer = net.AddMultiplicationLayer("ml");
173
174 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
175 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
176 layer->GetOutputSlot(0).SetTensorInfo(desc);
177
178 prevLayer = layer;
179 armnn::SoftmaxDescriptor softmaxDescriptor;
180 layer = net.AddSoftmaxLayer(softmaxDescriptor, "sm");
181
182 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
183 layer->GetOutputSlot(0).SetTensorInfo(desc);
184
185 prevLayer = layer;
186 layer = net.AddOutputLayer(0, "ot");
187
188 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
189
190 armnn::IRuntime::CreationOptions options;
191 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
192
193 std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined };
194
195 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
196 BOOST_CHECK(!optNet);
197
198}
199
200BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsUndefinedComputeDeviceWithFallback)
201{
202 const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
203
204 armnn::Network net;
205
206 armnn::NormalizationDescriptor nmDesc;
207 armnn::ActivationDescriptor acDesc;
208
209 // in
210 // |
211 // nm
212 // / |
213 // ac |
214 // \ |
215 // ml
216 // |
217 // sm
218 // |
219 // ot
220 armnn::IConnectableLayer* layer = net.AddInputLayer(0, "in");
221 layer->GetOutputSlot(0).SetTensorInfo(desc);
222
223 armnn::IConnectableLayer* const normLayer = net.AddNormalizationLayer(nmDesc, "nm");
224
225 layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
226 normLayer->GetOutputSlot(0).SetTensorInfo(desc);
227
228 layer = net.AddActivationLayer(acDesc, "ac");
229
230 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
231 layer->GetOutputSlot(0).SetTensorInfo(desc);
232
233 armnn::IConnectableLayer* prevLayer = layer;
234 layer = net.AddMultiplicationLayer("ml");
235
236 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
237 normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
238 layer->GetOutputSlot(0).SetTensorInfo(desc);
239
240 prevLayer = layer;
241 armnn::SoftmaxDescriptor softmaxDescriptor;
242 layer = net.AddSoftmaxLayer(softmaxDescriptor, "sm");
243
244 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
245 layer->GetOutputSlot(0).SetTensorInfo(desc);
246
247 prevLayer = layer;
248 layer = net.AddOutputLayer(0, "ot");
249
250 prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
251
252 armnn::IRuntime::CreationOptions options;
253 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
254
255 std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined, armnn::Compute::CpuRef };
256
257 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
258 BOOST_CHECK(optNet);
259
260 // validate workloads
261 armnn::RefWorkloadFactory fact;
262 for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
263 {
264 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
265 BOOST_CHECK_NO_THROW(
266 layer->CreateWorkload(static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph(), fact));
267 }
268}
269
270BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsDuplicateComputeDeviceWithFallback)
271{
272 // build up the structure of the network
273 armnn::INetworkPtr net(armnn::INetwork::Create());
274
275 armnn::IConnectableLayer* input = net->AddInputLayer(0);
276
277 // This layer configuration isn't supported by CpuAcc but it allows to fallback to CpuRef.
278 armnn::NormalizationDescriptor descriptor;
279 armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
280
281 armnn::IConnectableLayer* output = net->AddOutputLayer(0);
282
283 input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
284 normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
285
286 input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
287 normalize->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
288
289 armnn::IRuntime::CreationOptions options;
290 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
291
292 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc,
293 armnn::Compute::GpuAcc,
294 armnn::Compute::CpuRef };
295
296 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
297 BOOST_REQUIRE(optNet);
298
299 for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
300 {
301 // If NEON is enabled, Input and Output layers are supported by CpuAcc,
302 // the other layers are supported by CpuRef.
303 // If only CL is enabled, Input and Output layers are supported by GpuAcc,
304 // the other layers are supported by CpuRef.
305 // If neither NEON, nor CL is enabled, all layers are supported by CpuRef.
306#if ARMCOMPUTENEON_ENABLED
307 if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
308 {
309 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
310 }
311 else if (layer->GetType() == armnn::LayerType::Normalization)
312 {
313 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
314 }
315#elif ARMCOMPUTECL_ENABLED
316 if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
317 {
318 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::GpuAcc);
319 }
320 else if (layer->GetType() == armnn::LayerType::Normalization)
321 {
322 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
323 }
324#else
325 BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
326#endif
327 }
328}
329
330BOOST_AUTO_TEST_SUITE_END()