blob: 2d268f8ea10f51c935cdf83dd6dfd8a21c058982 [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#pragma once
6
Mike Kelly386ff1a2021-03-29 15:04:50 +01007#include "CommonTestUtils.hpp"
8
Matthew Bentham246bd462020-01-20 16:16:06 +00009#include <armnn/Descriptors.hpp>
narpra01b9546cf2018-11-20 15:21:28 +000010#include <armnn/INetwork.hpp>
Matthew Bentham246bd462020-01-20 16:16:06 +000011#include <armnn/IRuntime.hpp>
Aron Virginas-Tar70104002018-10-24 15:33:28 +010012
Aron Virginas-Tar48623a02019-10-22 10:00:28 +010013#include <Profiling.hpp>
14#include <QuantizeHelper.hpp>
15#include <ResolveType.hpp>
Aron Virginas-Tar70104002018-10-24 15:33:28 +010016
Sadik Armagan1625efc2021-06-10 18:24:34 +010017#include <doctest/doctest.h>
narpra01b9546cf2018-11-20 15:21:28 +000018
Aron Virginas-Tar70104002018-10-24 15:33:28 +010019#include <vector>
20
21namespace
22{
23
24using namespace armnn;
25
26template<typename T>
27bool ConstantUsageTest(const std::vector<BackendId>& computeDevice,
28 const TensorInfo& commonTensorInfo,
29 const std::vector<T>& inputData,
30 const std::vector<T>& constantData,
31 const std::vector<T>& expectedOutputData)
32{
33 // Create runtime in which test will run
34 IRuntime::CreationOptions options;
35 IRuntimePtr runtime(IRuntime::Create(options));
36
37 // Builds up the structure of the network.
38 INetworkPtr net(INetwork::Create());
39
40 IConnectableLayer* input = net->AddInputLayer(0);
41 IConnectableLayer* constant = net->AddConstantLayer(ConstTensor(commonTensorInfo, constantData));
42 IConnectableLayer* add = net->AddAdditionLayer();
43 IConnectableLayer* output = net->AddOutputLayer(0);
44
45 input->GetOutputSlot(0).Connect(add->GetInputSlot(0));
46 constant->GetOutputSlot(0).Connect(add->GetInputSlot(1));
47 add->GetOutputSlot(0).Connect(output->GetInputSlot(0));
48
49 // Sets the tensors in the network.
50 input->GetOutputSlot(0).SetTensorInfo(commonTensorInfo);
51 constant->GetOutputSlot(0).SetTensorInfo(commonTensorInfo);
52 add->GetOutputSlot(0).SetTensorInfo(commonTensorInfo);
53
54 // optimize the network
55 IOptimizedNetworkPtr optNet = Optimize(*net, computeDevice, runtime->GetDeviceSpec());
56
57 // Loads it into the runtime.
58 NetworkId netId;
59 runtime->LoadNetwork(netId, std::move(optNet));
60
61 // Creates structures for input & output.
62 std::vector<T> outputData(inputData.size());
63
64 InputTensors inputTensors
65 {
66 {0, ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())}
67 };
68 OutputTensors outputTensors
69 {
70 {0, Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())}
71 };
72
73 // Does the inference.
74 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
75
76 // Checks the results.
77 return outputData == expectedOutputData;
78}
79
80inline bool ConstantUsageFloat32Test(const std::vector<BackendId>& backends)
81{
82 const TensorInfo commonTensorInfo({ 2, 3 }, DataType::Float32);
83
84 return ConstantUsageTest(backends,
85 commonTensorInfo,
86 std::vector<float>{ 1.f, 2.f, 3.f, 4.f, 5.f, 6.f }, // Input.
87 std::vector<float>{ 6.f, 5.f, 4.f, 3.f, 2.f, 1.f }, // Const input.
88 std::vector<float>{ 7.f, 7.f, 7.f, 7.f, 7.f, 7.f } // Expected output.
89 );
90}
91
92inline bool ConstantUsageUint8Test(const std::vector<BackendId>& backends)
93{
Derek Lambertif90c56d2020-01-10 17:14:08 +000094 TensorInfo commonTensorInfo({ 2, 3 }, DataType::QAsymmU8);
Aron Virginas-Tar70104002018-10-24 15:33:28 +010095
96 const float scale = 0.023529f;
97 const int8_t offset = -43;
98
99 commonTensorInfo.SetQuantizationScale(scale);
100 commonTensorInfo.SetQuantizationOffset(offset);
101
102 return ConstantUsageTest(backends,
103 commonTensorInfo,
Aron Virginas-Tar48623a02019-10-22 10:00:28 +0100104 armnnUtils::QuantizedVector<uint8_t>({ 1.f, 2.f, 3.f, 4.f, 5.f, 6.f }, scale, offset), // Input.
105 armnnUtils::QuantizedVector<uint8_t>({ 6.f, 5.f, 4.f, 3.f, 2.f, 1.f }, scale, offset), // Const input.
106 armnnUtils::QuantizedVector<uint8_t>({ 7.f, 7.f, 7.f, 7.f, 7.f, 7.f }, scale, offset) // Expected output.
Aron Virginas-Tar70104002018-10-24 15:33:28 +0100107 );
108}
109
Ferran Balaguer83239f92019-09-19 11:49:25 +0100110// Utility function to find the number of instances of a substring within a string.
111int SubStringCounter(std::string& string, std::string&& substring)
112{
113 std::size_t found = 0;
114 int count = 0;
115 // Look for the substring starting from where we last found the substring
116 while((found = string.find(substring, found)) != std::string::npos)
117 {
118 count++;
119 // Offset by substring length to avoid finding the same substring twice
120 found += substring.length();
121 }
122 return count;
123}
124
Nattapat Chaimanowong1fcb4ff2019-01-24 15:25:26 +0000125template<DataType ArmnnIType, DataType ArmnnOType,
126 typename TInput = ResolveType<ArmnnIType>, typename TOutput = ResolveType<ArmnnOType>>
narpra01b9546cf2018-11-20 15:21:28 +0000127void EndToEndLayerTestImpl(INetworkPtr network,
kevmay012b4d88e2019-01-24 14:05:09 +0000128 const std::map<int, std::vector<TInput>>& inputTensorData,
129 const std::map<int, std::vector<TOutput>>& expectedOutputData,
Jan Eilersbca73e12020-03-11 12:52:46 +0000130 std::vector<BackendId> backends,
131 float tolerance = 0.000001f)
narpra01b9546cf2018-11-20 15:21:28 +0000132{
133 // Create runtime in which test will run
134 IRuntime::CreationOptions options;
135 IRuntimePtr runtime(IRuntime::Create(options));
136
137 // optimize the network
138 IOptimizedNetworkPtr optNet = Optimize(*network, backends, runtime->GetDeviceSpec());
139
140 // Loads it into the runtime.
141 NetworkId netId;
142 runtime->LoadNetwork(netId, std::move(optNet));
143
144 InputTensors inputTensors;
145 inputTensors.reserve(inputTensorData.size());
146 for (auto&& it : inputTensorData)
147 {
148 inputTensors.push_back({it.first,
149 ConstTensor(runtime->GetInputTensorInfo(netId, it.first), it.second.data())});
150 }
151 OutputTensors outputTensors;
152 outputTensors.reserve(expectedOutputData.size());
kevmay012b4d88e2019-01-24 14:05:09 +0000153 std::map<int, std::vector<TOutput>> outputStorage;
narpra01b9546cf2018-11-20 15:21:28 +0000154 for (auto&& it : expectedOutputData)
155 {
kevmay012b4d88e2019-01-24 14:05:09 +0000156 std::vector<TOutput> out(it.second.size());
narpra01b9546cf2018-11-20 15:21:28 +0000157 outputStorage.emplace(it.first, out);
158 outputTensors.push_back({it.first,
159 Tensor(runtime->GetOutputTensorInfo(netId, it.first),
160 outputStorage.at(it.first).data())});
161 }
162
163 // Does the inference.
164 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
165
166 // Checks the results.
167 for (auto&& it : expectedOutputData)
168 {
kevmay012b4d88e2019-01-24 14:05:09 +0000169 std::vector<TOutput> out = outputStorage.at(it.first);
Aron Virginas-Tarf97f6da2019-10-01 18:35:44 +0100170 for (unsigned int i = 0; i < out.size(); ++i)
Nattapat Chaimanowong1fcb4ff2019-01-24 15:25:26 +0000171 {
Sadik Armagan1625efc2021-06-10 18:24:34 +0100172 CHECK_MESSAGE(Compare<ArmnnOType>(it.second[i], out[i], tolerance) == true,
Teresa Charlin2e3f4d22020-07-29 14:29:20 +0100173 "Actual output: " << out[i] << ". Expected output:" << it.second[i]);
174
Nattapat Chaimanowong1fcb4ff2019-01-24 15:25:26 +0000175 }
narpra01b9546cf2018-11-20 15:21:28 +0000176 }
177}
178
David Monahan4f1e8e42019-09-04 09:22:10 +0100179inline void ImportNonAlignedInputPointerTest(std::vector<BackendId> backends)
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100180{
181 using namespace armnn;
182
183 // Create runtime in which test will run
184 IRuntime::CreationOptions options;
185 IRuntimePtr runtime(armnn::IRuntime::Create(options));
186
187 // build up the structure of the network
188 INetworkPtr net(INetwork::Create());
189
190 IConnectableLayer* input = net->AddInputLayer(0);
191
David Monahan3fb7e102019-08-20 11:25:29 +0100192 ActivationDescriptor descriptor;
193 descriptor.m_Function = ActivationFunction::Square;
194 IConnectableLayer* pooling = net->AddActivationLayer(descriptor);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100195
196 IConnectableLayer* output = net->AddOutputLayer(0);
197
David Monahan3fb7e102019-08-20 11:25:29 +0100198 input->GetOutputSlot(0).Connect(pooling->GetInputSlot(0));
199 pooling->GetOutputSlot(0).Connect(output->GetInputSlot(0));
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100200
David Monahan3fb7e102019-08-20 11:25:29 +0100201 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
202 pooling->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100203
204 // Optimize the network
205 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
Sadik Armagan1625efc2021-06-10 18:24:34 +0100206 CHECK(optNet);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100207
208 // Loads it into the runtime.
209 NetworkId netId;
David Monahan4f1e8e42019-09-04 09:22:10 +0100210 std::string ignoredErrorMessage;
211 // Enable Importing
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100212 INetworkProperties networkProperties(false, MemorySource::Malloc, MemorySource::Undefined);
David Monahan4f1e8e42019-09-04 09:22:10 +0100213 runtime->LoadNetwork(netId, std::move(optNet), ignoredErrorMessage, networkProperties);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100214
215 // Creates structures for input & output
216 std::vector<float> inputData
217 {
David Monahan3fb7e102019-08-20 11:25:29 +0100218 1.0f, 2.0f, 3.0f, 4.0f
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100219 };
220
221 // Misaligned input
Aron Virginas-Tard9f7c8b2019-09-13 13:37:03 +0100222 float* misalignedInputData = reinterpret_cast<float*>(reinterpret_cast<char*>(inputData.data()) + 1);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100223
David Monahan3fb7e102019-08-20 11:25:29 +0100224 std::vector<float> outputData(4);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100225
David Monahan4f1e8e42019-09-04 09:22:10 +0100226 // Aligned output
David Monahan3fb7e102019-08-20 11:25:29 +0100227 float* alignedOutputData = outputData.data();
David Monahan4f1e8e42019-09-04 09:22:10 +0100228
229 InputTensors inputTensors
230 {
231 {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), misalignedInputData)},
232 };
233 OutputTensors outputTensors
234 {
235 {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), alignedOutputData)}
236 };
237
David Monahan4f1e8e42019-09-04 09:22:10 +0100238 runtime->GetProfiler(netId)->EnableProfiling(true);
239
240 // Do the inference and expect it to fail with a ImportMemoryException
Sadik Armagan1625efc2021-06-10 18:24:34 +0100241 CHECK_THROWS_AS(runtime->EnqueueWorkload(netId, inputTensors, outputTensors), MemoryImportException);
David Monahan4f1e8e42019-09-04 09:22:10 +0100242}
243
Ferran Balaguer83239f92019-09-19 11:49:25 +0100244inline void ExportNonAlignedOutputPointerTest(std::vector<BackendId> backends)
David Monahan4f1e8e42019-09-04 09:22:10 +0100245{
246 using namespace armnn;
247
248 // Create runtime in which test will run
249 IRuntime::CreationOptions options;
250 IRuntimePtr runtime(armnn::IRuntime::Create(options));
251
252 // build up the structure of the network
253 INetworkPtr net(INetwork::Create());
254
255 IConnectableLayer* input = net->AddInputLayer(0);
256
David Monahan3fb7e102019-08-20 11:25:29 +0100257 ActivationDescriptor descriptor;
258 descriptor.m_Function = ActivationFunction::Square;
259 IConnectableLayer* pooling = net->AddActivationLayer(descriptor);
David Monahan4f1e8e42019-09-04 09:22:10 +0100260
261 IConnectableLayer* output = net->AddOutputLayer(0);
262
David Monahan3fb7e102019-08-20 11:25:29 +0100263 input->GetOutputSlot(0).Connect(pooling->GetInputSlot(0));
264 pooling->GetOutputSlot(0).Connect(output->GetInputSlot(0));
David Monahan4f1e8e42019-09-04 09:22:10 +0100265
David Monahan3fb7e102019-08-20 11:25:29 +0100266 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
267 pooling->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
David Monahan4f1e8e42019-09-04 09:22:10 +0100268
269 // Optimize the network
270 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
Sadik Armagan1625efc2021-06-10 18:24:34 +0100271 CHECK(optNet);
David Monahan4f1e8e42019-09-04 09:22:10 +0100272
273 // Loads it into the runtime.
274 NetworkId netId;
275 std::string ignoredErrorMessage;
David Monahan3fb7e102019-08-20 11:25:29 +0100276 // Enable Importing and Exporting
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100277 INetworkProperties networkProperties(false, MemorySource::Malloc, MemorySource::Malloc);
David Monahan4f1e8e42019-09-04 09:22:10 +0100278 runtime->LoadNetwork(netId, std::move(optNet), ignoredErrorMessage, networkProperties);
279
280 // Creates structures for input & output
281 std::vector<float> inputData
282 {
283 1.0f, 2.0f, 3.0f, 4.0f, 5.0f
284 };
285
286 // Aligned input
David Monahan3fb7e102019-08-20 11:25:29 +0100287 float* alignedInputData = inputData.data();
David Monahan4f1e8e42019-09-04 09:22:10 +0100288
289 std::vector<float> outputData(5);
290
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100291 // Misaligned output
Aron Virginas-Tard9f7c8b2019-09-13 13:37:03 +0100292 float* misalignedOutputData = reinterpret_cast<float*>(reinterpret_cast<char*>(outputData.data()) + 1);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100293
294 InputTensors inputTensors
295 {
David Monahan4f1e8e42019-09-04 09:22:10 +0100296 {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), alignedInputData)},
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100297 };
298 OutputTensors outputTensors
299 {
300 {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), misalignedOutputData)}
301 };
302
Ferran Balaguer83239f92019-09-19 11:49:25 +0100303 // Do the inference and expect it to fail with a ExportMemoryException
304 if (backends[0] == Compute::CpuAcc)
305 {
306 // For CpuAcc the NeonTensorHandle will throw its own exception on misaligned memory
Sadik Armagan1625efc2021-06-10 18:24:34 +0100307 CHECK_THROWS_AS(runtime->EnqueueWorkload(netId, inputTensors, outputTensors), MemoryImportException);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100308 }
309 else
310 {
Sadik Armagan1625efc2021-06-10 18:24:34 +0100311 CHECK_THROWS_AS(runtime->EnqueueWorkload(netId, inputTensors, outputTensors), MemoryExportException);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100312 }
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100313}
314
315inline void ImportAlignedPointerTest(std::vector<BackendId> backends)
316{
317 using namespace armnn;
318
319 // Create runtime in which test will run
320 IRuntime::CreationOptions options;
321 IRuntimePtr runtime(armnn::IRuntime::Create(options));
322
323 // build up the structure of the network
324 INetworkPtr net(INetwork::Create());
325
326 IConnectableLayer* input = net->AddInputLayer(0);
327
David Monahan3fb7e102019-08-20 11:25:29 +0100328 ActivationDescriptor descriptor;
329 descriptor.m_Function = ActivationFunction::Square;
330 IConnectableLayer* pooling = net->AddActivationLayer(descriptor);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100331
332 IConnectableLayer* output = net->AddOutputLayer(0);
333
David Monahan3fb7e102019-08-20 11:25:29 +0100334 input->GetOutputSlot(0).Connect(pooling->GetInputSlot(0));
335 pooling->GetOutputSlot(0).Connect(output->GetInputSlot(0));
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100336
David Monahan3fb7e102019-08-20 11:25:29 +0100337 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
338 pooling->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100339
340 // Optimize the network
341 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
Sadik Armagan1625efc2021-06-10 18:24:34 +0100342 CHECK(optNet);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100343
344 // Loads it into the runtime.
345 NetworkId netId;
David Monahan4f1e8e42019-09-04 09:22:10 +0100346 std::string ignoredErrorMessage;
347 // Enable Importing
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100348 INetworkProperties networkProperties(false, MemorySource::Malloc, MemorySource::Malloc);
David Monahan4f1e8e42019-09-04 09:22:10 +0100349 runtime->LoadNetwork(netId, std::move(optNet), ignoredErrorMessage, networkProperties);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100350
351 // Creates structures for input & output
352 std::vector<float> inputData
353 {
354 1.0f, 2.0f, 3.0f, 4.0f
355 };
356
357 std::vector<float> outputData(4);
358
James Conroy57d10b72019-10-25 09:44:14 +0100359 std::vector<float> expectedOutput
360 {
361 1.0f, 4.0f, 9.0f, 16.0f
362 };
363
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100364 InputTensors inputTensors
365 {
366 {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())},
367 };
368 OutputTensors outputTensors
369 {
370 {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())}
371 };
372
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100373 runtime->GetProfiler(netId)->EnableProfiling(true);
374
375 // Do the inference
376 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
377
378 // Retrieve the Profiler.Print() output to get the workload execution
379 ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
380 std::stringstream ss;
381 profilerManager.GetProfiler()->Print(ss);;
382 std::string dump = ss.str();
383
David Monahan3fb7e102019-08-20 11:25:29 +0100384 // Contains ActivationWorkload
385 std::size_t found = dump.find("ActivationWorkload");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100386 CHECK(found != std::string::npos);
James Conroy57d10b72019-10-25 09:44:14 +0100387
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100388 // Contains SyncMemGeneric
389 found = dump.find("SyncMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100390 CHECK(found != std::string::npos);
James Conroy57d10b72019-10-25 09:44:14 +0100391
Ferran Balaguer83239f92019-09-19 11:49:25 +0100392 // Does not contain CopyMemGeneric
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100393 found = dump.find("CopyMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100394 CHECK(found == std::string::npos);
James Conroy57d10b72019-10-25 09:44:14 +0100395
396 // Check output is as expected
Sadik Armagan1625efc2021-06-10 18:24:34 +0100397 CHECK(outputData == expectedOutput);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100398}
399
Ferran Balaguer83239f92019-09-19 11:49:25 +0100400inline void ImportOnlyWorkload(std::vector<BackendId> backends)
401{
402 using namespace armnn;
403
404 IRuntime::CreationOptions options;
405 IRuntimePtr runtime(IRuntime::Create(options));
406
407 // Builds up the structure of the network.
408 INetworkPtr net(INetwork::Create());
409
410 IConnectableLayer* input = net->AddInputLayer(0);
411
412 ActivationDescriptor descriptor;
413 descriptor.m_Function = ActivationFunction::Square;
414 IConnectableLayer* pooling = net->AddActivationLayer(descriptor);
415
416 IConnectableLayer* output = net->AddOutputLayer(0);
417
418 input->GetOutputSlot(0).Connect(pooling->GetInputSlot(0));
419 pooling->GetOutputSlot(0).Connect(output->GetInputSlot(0));
420
421 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
422 pooling->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
423
424 // optimize the network
425 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
426
Sadik Armagan1625efc2021-06-10 18:24:34 +0100427 INFO("Load Network");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100428 // Load it into the runtime. It should pass.
429 NetworkId netId;
430 std::string ignoredErrorMessage;
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100431
432 INetworkProperties networkProperties(false, MemorySource::Malloc, MemorySource::Undefined);
433
Sadik Armagan1625efc2021-06-10 18:24:34 +0100434 CHECK(runtime->LoadNetwork(netId, std::move(optNet),ignoredErrorMessage, networkProperties)
Ferran Balaguer83239f92019-09-19 11:49:25 +0100435 == Status::Success);
436
Sadik Armagan1625efc2021-06-10 18:24:34 +0100437 INFO("Generate Data");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100438 // Creates structures for input & output
439 std::vector<float> inputData
440 {
441 1.0f, 2.0f, 3.0f, 4.0f
442 };
443
444 std::vector<float> outputData(4);
445
446 std::vector<float> expectedOutput
447 {
448 1.0f, 4.0f, 9.0f, 16.0f
449 };
450
Sadik Armagan1625efc2021-06-10 18:24:34 +0100451 INFO("Create Network");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100452 InputTensors inputTensors
453 {
454 {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())},
455 };
456 OutputTensors outputTensors
457 {
458 {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())}
459 };
460
Sadik Armagan1625efc2021-06-10 18:24:34 +0100461 INFO("Get Profiler");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100462 runtime->GetProfiler(netId)->EnableProfiling(true);
463
Sadik Armagan1625efc2021-06-10 18:24:34 +0100464 INFO("Run Inference");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100465 // Do the inference
466 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
467
Sadik Armagan1625efc2021-06-10 18:24:34 +0100468 INFO("Print Profiler");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100469 // Retrieve the Profiler.Print() output to get the workload execution
470 ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
471 std::stringstream ss;
472 profilerManager.GetProfiler()->Print(ss);;
473 std::string dump = ss.str();
474
475 // Check there are no SyncMemGeneric workloads as we didn't export
Sadik Armagan1625efc2021-06-10 18:24:34 +0100476 INFO("Find SyncMemGeneric");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100477 int count = SubStringCounter(dump, "SyncMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100478 CHECK(count == 0);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100479
480 // Should only be 1 CopyMemGeneric for the output as we imported
Sadik Armagan1625efc2021-06-10 18:24:34 +0100481 INFO("Find CopyMemGeneric");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100482 count = SubStringCounter(dump, "CopyMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100483 CHECK(count == 1);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100484
485 // Check the output is correct
Sadik Armagan1625efc2021-06-10 18:24:34 +0100486 CHECK(std::equal(outputData.begin(), outputData.end(), expectedOutput.begin(), expectedOutput.end()));
Ferran Balaguer83239f92019-09-19 11:49:25 +0100487}
488
489inline void ExportOnlyWorkload(std::vector<BackendId> backends)
490{
491 using namespace armnn;
492
493 IRuntime::CreationOptions options;
494 IRuntimePtr runtime(IRuntime::Create(options));
495
496 // Builds up the structure of the network.
497 INetworkPtr net(INetwork::Create());
498
499 IConnectableLayer* input = net->AddInputLayer(0);
500
501 ActivationDescriptor descriptor;
502 descriptor.m_Function = ActivationFunction::Square;
503 IConnectableLayer* pooling = net->AddActivationLayer(descriptor);
504
505 IConnectableLayer* output = net->AddOutputLayer(0);
506
507 input->GetOutputSlot(0).Connect(pooling->GetInputSlot(0));
508 pooling->GetOutputSlot(0).Connect(output->GetInputSlot(0));
509
510 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
511 pooling->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
512
513 // optimize the network
514 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
515
Sadik Armagan1625efc2021-06-10 18:24:34 +0100516 INFO("Load Network");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100517 // Load it into the runtime. It should pass.
518 NetworkId netId;
519 std::string ignoredErrorMessage;
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100520 INetworkProperties networkProperties(false, MemorySource::Undefined, MemorySource::Malloc);
Sadik Armagan1625efc2021-06-10 18:24:34 +0100521 CHECK(runtime->LoadNetwork(netId, std::move(optNet),ignoredErrorMessage, networkProperties)
Ferran Balaguer83239f92019-09-19 11:49:25 +0100522 == Status::Success);
523
Sadik Armagan1625efc2021-06-10 18:24:34 +0100524 INFO("Generate Data");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100525 // Creates structures for input & output
526 std::vector<float> inputData
527 {
528 1.0f, 2.0f, 3.0f, 4.0f
529 };
530
531 std::vector<float> outputData(4);
532
533 std::vector<float> expectedOutput
534 {
535 1.0f, 4.0f, 9.0f, 16.0f
536 };
537
Sadik Armagan1625efc2021-06-10 18:24:34 +0100538 INFO("Create Network");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100539 InputTensors inputTensors
540 {
541 {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())},
542 };
543 OutputTensors outputTensors
544 {
545 {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())}
546 };
547
Sadik Armagan1625efc2021-06-10 18:24:34 +0100548 INFO("Get Profiler");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100549 runtime->GetProfiler(netId)->EnableProfiling(true);
550
Sadik Armagan1625efc2021-06-10 18:24:34 +0100551 INFO("Run Inference");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100552 // Do the inference
553 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
554
Sadik Armagan1625efc2021-06-10 18:24:34 +0100555 INFO("Print Profiler");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100556 // Retrieve the Profiler.Print() output to get the workload execution
557 ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
558 std::stringstream ss;
559 profilerManager.GetProfiler()->Print(ss);;
560 std::string dump = ss.str();
561
562 // Check there is a SyncMemGeneric workload as we exported
Sadik Armagan1625efc2021-06-10 18:24:34 +0100563 INFO("Find SyncMemGeneric");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100564 int count = SubStringCounter(dump, "SyncMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100565 CHECK(count == 1);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100566
567 // Should be 1 CopyMemGeneric for the output as we did not import
Sadik Armagan1625efc2021-06-10 18:24:34 +0100568 INFO("Find CopyMemGeneric");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100569 count = SubStringCounter(dump, "CopyMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100570 CHECK(count == 1);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100571
572 // Check the output is correct
Sadik Armagan1625efc2021-06-10 18:24:34 +0100573 CHECK(std::equal(outputData.begin(), outputData.end(), expectedOutput.begin(), expectedOutput.end()));
Ferran Balaguer83239f92019-09-19 11:49:25 +0100574}
575
576inline void ImportAndExportWorkload(std::vector<BackendId> backends)
577{
578 using namespace armnn;
579
580 IRuntime::CreationOptions options;
581 IRuntimePtr runtime(IRuntime::Create(options));
582
583 // Builds up the structure of the network.
584 INetworkPtr net(INetwork::Create());
585
586 IConnectableLayer* input = net->AddInputLayer(0);
587
588 ActivationDescriptor descriptor;
589 descriptor.m_Function = ActivationFunction::Square;
590 IConnectableLayer* pooling = net->AddActivationLayer(descriptor);
591
592 IConnectableLayer* output = net->AddOutputLayer(0);
593
594 input->GetOutputSlot(0).Connect(pooling->GetInputSlot(0));
595 pooling->GetOutputSlot(0).Connect(output->GetInputSlot(0));
596
597 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
598 pooling->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 1, 4 }, DataType::Float32));
599
600 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
601
Sadik Armagan1625efc2021-06-10 18:24:34 +0100602 INFO("Load Network");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100603 // Load it into the runtime. It should pass.
604 NetworkId netId;
605 std::string ignoredErrorMessage;
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100606
607 INetworkProperties networkProperties(false, MemorySource::Malloc, MemorySource::Malloc);
608
Sadik Armagan1625efc2021-06-10 18:24:34 +0100609 CHECK(runtime->LoadNetwork(netId, std::move(optNet),ignoredErrorMessage, networkProperties)
Ferran Balaguer83239f92019-09-19 11:49:25 +0100610 == Status::Success);
611
Sadik Armagan1625efc2021-06-10 18:24:34 +0100612 INFO("Generate Data");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100613 // Creates structures for input & output
614 std::vector<float> inputData
615 {
616 1.0f, 2.0f, 3.0f, 4.0f
617 };
618
619 std::vector<float> outputData(4);
620
621 std::vector<float> expectedOutput
622 {
623 1.0f, 4.0f, 9.0f, 16.0f
624 };
625
Sadik Armagan1625efc2021-06-10 18:24:34 +0100626 INFO("Create Network");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100627 InputTensors inputTensors
628 {
629 {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())},
630 };
631 OutputTensors outputTensors
632 {
633 {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())}
634 };
635
Sadik Armagan1625efc2021-06-10 18:24:34 +0100636 INFO("Get Profiler");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100637 runtime->GetProfiler(netId)->EnableProfiling(true);
638
Sadik Armagan1625efc2021-06-10 18:24:34 +0100639 INFO("Run Inference");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100640 // Do the inference
641 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
642
Sadik Armagan1625efc2021-06-10 18:24:34 +0100643 INFO("Print Profiler");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100644 // Retrieve the Profiler.Print() output to get the workload execution
645 ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
646 std::stringstream ss;
647 profilerManager.GetProfiler()->Print(ss);;
648 std::string dump = ss.str();
649
650 // Check there is a SyncMemGeneric workload as we exported
Sadik Armagan1625efc2021-06-10 18:24:34 +0100651 INFO("Find SyncMemGeneric");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100652 int count = SubStringCounter(dump, "SyncMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100653 CHECK(count == 1);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100654
655 // Shouldn't be any CopyMemGeneric workloads
Sadik Armagan1625efc2021-06-10 18:24:34 +0100656 INFO("Find CopyMemGeneric");
Ferran Balaguer83239f92019-09-19 11:49:25 +0100657 count = SubStringCounter(dump, "CopyMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100658 CHECK(count == 0);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100659
660 // Check the output is correct
Sadik Armagan1625efc2021-06-10 18:24:34 +0100661 CHECK(std::equal(outputData.begin(), outputData.end(), expectedOutput.begin(), expectedOutput.end()));
Ferran Balaguer83239f92019-09-19 11:49:25 +0100662}
663
664inline void ExportOutputWithSeveralOutputSlotConnectionsTest(std::vector<BackendId> backends)
665{
666 using namespace armnn;
667
668 // Create runtime in which test will run
669 IRuntime::CreationOptions options;
670 IRuntimePtr runtime(armnn::IRuntime::Create(options));
671
672 // build up the structure of the network
673 INetworkPtr net(INetwork::Create());
674
675 IConnectableLayer* input = net->AddInputLayer(0);
676
677 ActivationDescriptor descriptor;
678 descriptor.m_Function = ActivationFunction::Square;
679 IConnectableLayer* activation = net->AddActivationLayer(descriptor);
680
681 IConnectableLayer* output0 = net->AddOutputLayer(0);
682 IConnectableLayer* output1 = net->AddOutputLayer(1);
683
684 input->GetOutputSlot(0).Connect(activation->GetInputSlot(0));
685 activation->GetOutputSlot(0).Connect(output0->GetInputSlot(0));
686 activation->GetOutputSlot(0).Connect(output1->GetInputSlot(0));
687
688 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 1 }, DataType::Float32));
689 activation->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 1 }, DataType::Float32));
690
691 // Optimize the network
692 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
693
694 // Loads it into the runtime.
695 NetworkId netId;
696 std::string ignoredErrorMessage;
697 // Enable Importing
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100698 INetworkProperties networkProperties(false, MemorySource::Malloc, MemorySource::Malloc);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100699 runtime->LoadNetwork(netId, std::move(optNet), ignoredErrorMessage, networkProperties);
700
701 // Creates structures for input & output
702 std::vector<float> inputData
703 {
704 1.0f, 2.0f, 3.0f, 4.0f
705 };
706
707 std::vector<float> outputData0(4);
708 std::vector<float> outputData1(4);
709
Narumol Prangnawarat3b90af62020-06-26 11:00:21 +0100710 std::vector<float> expectedOutput
711 {
712 1.0f, 4.0f, 9.0f, 16.0f
713 };
714
Ferran Balaguer83239f92019-09-19 11:49:25 +0100715 InputTensors inputTensors
716 {
717 {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())},
718 };
719 OutputTensors outputTensors
720 {
721 {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData0.data())},
722 {1,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 1), outputData1.data())}
723 };
724
725 // The result of the inference is not important, just the fact that there
726 // should not be CopyMemGeneric workloads.
727 runtime->GetProfiler(netId)->EnableProfiling(true);
728
729 // Do the inference
730 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
731
732 // Retrieve the Profiler.Print() output to get the workload execution
733 ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
734 std::stringstream ss;
735 profilerManager.GetProfiler()->Print(ss);
736 std::string dump = ss.str();
737
738 std::size_t found = std::string::npos;
739
740 if (backends[0] == Compute::CpuRef)
741 {
742 found = dump.find("RefActivationWorkload");
743 }
744 else if (backends[0] == Compute::CpuAcc)
745 {
746 found = dump.find("NeonActivationWorkload");
747 }
748 else if (backends[0] == Compute::GpuAcc)
749 {
750 found = dump.find("ClActivationWorkload");
751 }
752
Sadik Armagan1625efc2021-06-10 18:24:34 +0100753 CHECK(found != std::string::npos);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100754 // No contains SyncMemGeneric
755 found = dump.find("SyncMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100756 CHECK(found == std::string::npos);
Ferran Balaguer83239f92019-09-19 11:49:25 +0100757 // Contains CopyMemGeneric
758 found = dump.find("CopyMemGeneric");
Sadik Armagan1625efc2021-06-10 18:24:34 +0100759 CHECK(found != std::string::npos);
Narumol Prangnawarat3b90af62020-06-26 11:00:21 +0100760
761 // Check that the outputs are correct
Sadik Armagan1625efc2021-06-10 18:24:34 +0100762 CHECK(std::equal(outputData0.begin(), outputData0.end(),
763 expectedOutput.begin(), expectedOutput.end()));
764 CHECK(std::equal(outputData1.begin(), outputData1.end(),
765 expectedOutput.begin(), expectedOutput.end()));
Ferran Balaguer83239f92019-09-19 11:49:25 +0100766}
767
David Monahan0a99a142020-03-13 07:52:54 +0000768inline void StridedSliceInvalidSliceEndToEndTest(std::vector<BackendId> backends)
769{
770 using namespace armnn;
771
772 // Create runtime in which test will run
773 IRuntime::CreationOptions options;
774 IRuntimePtr runtime(armnn::IRuntime::Create(options));
775
776 // build up the structure of the network
777 INetworkPtr net(INetwork::Create());
778
779 IConnectableLayer* input = net->AddInputLayer(0);
780
781 // Configure a strided slice with a stride the same size as the input but with a ShrinkAxisMask on the first
782 // dim of the output to make it too small to hold the specified slice.
783 StridedSliceDescriptor descriptor;
784 descriptor.m_Begin = {0, 0};
785 descriptor.m_End = {2, 3};
786 descriptor.m_Stride = {1, 1};
787 descriptor.m_BeginMask = 0;
788 descriptor.m_EndMask = 0;
789 descriptor.m_ShrinkAxisMask = 1;
790 IConnectableLayer* stridedSlice = net->AddStridedSliceLayer(descriptor);
791
792 IConnectableLayer* output0 = net->AddOutputLayer(0);
793
794 input->GetOutputSlot(0).Connect(stridedSlice->GetInputSlot(0));
795 stridedSlice->GetOutputSlot(0).Connect(output0->GetInputSlot(0));
796
797 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 2, 3 }, DataType::Float32));
798 stridedSlice->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 3 }, DataType::Float32));
799
800 // Attempt to optimize the network and check that the correct exception is thrown
Sadik Armagan1625efc2021-06-10 18:24:34 +0100801 CHECK_THROWS_AS(Optimize(*net, backends, runtime->GetDeviceSpec()), armnn::LayerValidationException);
David Monahan0a99a142020-03-13 07:52:54 +0000802}
803
Nattapat Chaimanowong1fcb4ff2019-01-24 15:25:26 +0000804} // anonymous namespace