blob: 040782bf6859234f5eef2f05a5d8b5fe7f2a5366 [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
Aron Virginas-Tard4f0fea2019-04-09 14:08:06 +01007#include <ResolveType.hpp>
Nattapat Chaimanowong1fcb4ff2019-01-24 15:25:26 +00008
Aron Virginas-Tar70104002018-10-24 15:33:28 +01009#include <armnn/ArmNN.hpp>
narpra01b9546cf2018-11-20 15:21:28 +000010#include <armnn/INetwork.hpp>
Ferran Balaguerdcaa6102019-08-21 13:28:38 +010011#include <Profiling.hpp>
Aron Virginas-Tar70104002018-10-24 15:33:28 +010012
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000013#include <backendsCommon/test/QuantizeHelper.hpp>
Aron Virginas-Tar70104002018-10-24 15:33:28 +010014
narpra01b9546cf2018-11-20 15:21:28 +000015#include <boost/test/unit_test.hpp>
16
Aron Virginas-Tar70104002018-10-24 15:33:28 +010017#include <vector>
18
19namespace
20{
21
22using namespace armnn;
23
24template<typename T>
25bool ConstantUsageTest(const std::vector<BackendId>& computeDevice,
26 const TensorInfo& commonTensorInfo,
27 const std::vector<T>& inputData,
28 const std::vector<T>& constantData,
29 const std::vector<T>& expectedOutputData)
30{
31 // Create runtime in which test will run
32 IRuntime::CreationOptions options;
33 IRuntimePtr runtime(IRuntime::Create(options));
34
35 // Builds up the structure of the network.
36 INetworkPtr net(INetwork::Create());
37
38 IConnectableLayer* input = net->AddInputLayer(0);
39 IConnectableLayer* constant = net->AddConstantLayer(ConstTensor(commonTensorInfo, constantData));
40 IConnectableLayer* add = net->AddAdditionLayer();
41 IConnectableLayer* output = net->AddOutputLayer(0);
42
43 input->GetOutputSlot(0).Connect(add->GetInputSlot(0));
44 constant->GetOutputSlot(0).Connect(add->GetInputSlot(1));
45 add->GetOutputSlot(0).Connect(output->GetInputSlot(0));
46
47 // Sets the tensors in the network.
48 input->GetOutputSlot(0).SetTensorInfo(commonTensorInfo);
49 constant->GetOutputSlot(0).SetTensorInfo(commonTensorInfo);
50 add->GetOutputSlot(0).SetTensorInfo(commonTensorInfo);
51
52 // optimize the network
53 IOptimizedNetworkPtr optNet = Optimize(*net, computeDevice, runtime->GetDeviceSpec());
54
55 // Loads it into the runtime.
56 NetworkId netId;
57 runtime->LoadNetwork(netId, std::move(optNet));
58
59 // Creates structures for input & output.
60 std::vector<T> outputData(inputData.size());
61
62 InputTensors inputTensors
63 {
64 {0, ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())}
65 };
66 OutputTensors outputTensors
67 {
68 {0, Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())}
69 };
70
71 // Does the inference.
72 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
73
74 // Checks the results.
75 return outputData == expectedOutputData;
76}
77
78inline bool ConstantUsageFloat32Test(const std::vector<BackendId>& backends)
79{
80 const TensorInfo commonTensorInfo({ 2, 3 }, DataType::Float32);
81
82 return ConstantUsageTest(backends,
83 commonTensorInfo,
84 std::vector<float>{ 1.f, 2.f, 3.f, 4.f, 5.f, 6.f }, // Input.
85 std::vector<float>{ 6.f, 5.f, 4.f, 3.f, 2.f, 1.f }, // Const input.
86 std::vector<float>{ 7.f, 7.f, 7.f, 7.f, 7.f, 7.f } // Expected output.
87 );
88}
89
90inline bool ConstantUsageUint8Test(const std::vector<BackendId>& backends)
91{
92 TensorInfo commonTensorInfo({ 2, 3 }, DataType::QuantisedAsymm8);
93
94 const float scale = 0.023529f;
95 const int8_t offset = -43;
96
97 commonTensorInfo.SetQuantizationScale(scale);
98 commonTensorInfo.SetQuantizationOffset(offset);
99
100 return ConstantUsageTest(backends,
101 commonTensorInfo,
102 QuantizedVector<uint8_t>(scale, offset, { 1.f, 2.f, 3.f, 4.f, 5.f, 6.f }), // Input.
103 QuantizedVector<uint8_t>(scale, offset, { 6.f, 5.f, 4.f, 3.f, 2.f, 1.f }), // Const input.
104 QuantizedVector<uint8_t>(scale, offset, { 7.f, 7.f, 7.f, 7.f, 7.f, 7.f }) // Expected output.
105 );
106}
107
Nattapat Chaimanowong1fcb4ff2019-01-24 15:25:26 +0000108template<typename T>
109bool CompareBoolean(T a, T b)
110{
111 return (a == 0 && b == 0) ||(a != 0 && b != 0);
112};
113
114template<DataType ArmnnIType, DataType ArmnnOType,
115 typename TInput = ResolveType<ArmnnIType>, typename TOutput = ResolveType<ArmnnOType>>
narpra01b9546cf2018-11-20 15:21:28 +0000116void EndToEndLayerTestImpl(INetworkPtr network,
kevmay012b4d88e2019-01-24 14:05:09 +0000117 const std::map<int, std::vector<TInput>>& inputTensorData,
118 const std::map<int, std::vector<TOutput>>& expectedOutputData,
narpra01b9546cf2018-11-20 15:21:28 +0000119 std::vector<BackendId> backends)
120{
121 // Create runtime in which test will run
122 IRuntime::CreationOptions options;
123 IRuntimePtr runtime(IRuntime::Create(options));
124
125 // optimize the network
126 IOptimizedNetworkPtr optNet = Optimize(*network, backends, runtime->GetDeviceSpec());
127
128 // Loads it into the runtime.
129 NetworkId netId;
130 runtime->LoadNetwork(netId, std::move(optNet));
131
132 InputTensors inputTensors;
133 inputTensors.reserve(inputTensorData.size());
134 for (auto&& it : inputTensorData)
135 {
136 inputTensors.push_back({it.first,
137 ConstTensor(runtime->GetInputTensorInfo(netId, it.first), it.second.data())});
138 }
139 OutputTensors outputTensors;
140 outputTensors.reserve(expectedOutputData.size());
kevmay012b4d88e2019-01-24 14:05:09 +0000141 std::map<int, std::vector<TOutput>> outputStorage;
narpra01b9546cf2018-11-20 15:21:28 +0000142 for (auto&& it : expectedOutputData)
143 {
kevmay012b4d88e2019-01-24 14:05:09 +0000144 std::vector<TOutput> out(it.second.size());
narpra01b9546cf2018-11-20 15:21:28 +0000145 outputStorage.emplace(it.first, out);
146 outputTensors.push_back({it.first,
147 Tensor(runtime->GetOutputTensorInfo(netId, it.first),
148 outputStorage.at(it.first).data())});
149 }
150
151 // Does the inference.
152 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
153
154 // Checks the results.
155 for (auto&& it : expectedOutputData)
156 {
kevmay012b4d88e2019-01-24 14:05:09 +0000157 std::vector<TOutput> out = outputStorage.at(it.first);
Nattapat Chaimanowong1fcb4ff2019-01-24 15:25:26 +0000158 if (ArmnnOType == DataType::Boolean)
159 {
160 for (unsigned int i = 0; i < out.size(); ++i)
161 {
162 BOOST_TEST(CompareBoolean<TOutput>(it.second[i], out[i]));
163 }
164 }
165 else
166 {
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +0000167 for (unsigned int i = 0; i < out.size(); ++i)
168 {
169 BOOST_TEST(it.second[i] == out[i], boost::test_tools::tolerance(0.000001f));
170 }
Nattapat Chaimanowong1fcb4ff2019-01-24 15:25:26 +0000171 }
narpra01b9546cf2018-11-20 15:21:28 +0000172 }
173}
174
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100175inline void ImportNonAlignedPointerTest(std::vector<BackendId> backends)
176{
177 using namespace armnn;
178
179 // Create runtime in which test will run
180 IRuntime::CreationOptions options;
181 IRuntimePtr runtime(armnn::IRuntime::Create(options));
182
183 // build up the structure of the network
184 INetworkPtr net(INetwork::Create());
185
186 IConnectableLayer* input = net->AddInputLayer(0);
187
188 NormalizationDescriptor descriptor;
189 IConnectableLayer* norm = net->AddNormalizationLayer(descriptor);
190
191 IConnectableLayer* output = net->AddOutputLayer(0);
192
193 input->GetOutputSlot(0).Connect(norm->GetInputSlot(0));
194 norm->GetOutputSlot(0).Connect(output->GetInputSlot(0));
195
196 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 1 }, DataType::Float32));
197 norm->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 1 }, DataType::Float32));
198
199 // Optimize the network
200 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
201
202 // Loads it into the runtime.
203 NetworkId netId;
204 runtime->LoadNetwork(netId, std::move(optNet));
205
206 // Creates structures for input & output
207 std::vector<float> inputData
208 {
209 1.0f, 2.0f, 3.0f, 4.0f, 5.0f
210 };
211
212 // Misaligned input
Aron Virginas-Tard9f7c8b2019-09-13 13:37:03 +0100213 float* misalignedInputData = reinterpret_cast<float*>(reinterpret_cast<char*>(inputData.data()) + 1);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100214
215 std::vector<float> outputData(5);
216
217 // Misaligned output
Aron Virginas-Tard9f7c8b2019-09-13 13:37:03 +0100218 float* misalignedOutputData = reinterpret_cast<float*>(reinterpret_cast<char*>(outputData.data()) + 1);
Ferran Balaguerdcaa6102019-08-21 13:28:38 +0100219
220 InputTensors inputTensors
221 {
222 {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), misalignedInputData)},
223 };
224 OutputTensors outputTensors
225 {
226 {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), misalignedOutputData)}
227 };
228
229 // The result of the inference is not important, just the fact that there
230 // should not be CopyMemGeneric workloads.
231 runtime->GetProfiler(netId)->EnableProfiling(true);
232
233 // Do the inference
234 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
235
236 // Retrieve the Profiler.Print() output to get the workload execution
237 ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
238 std::stringstream ss;
239 profilerManager.GetProfiler()->Print(ss);;
240 std::string dump = ss.str();
241
242 // Contains RefNormalizationWorkload
243 std::size_t found = dump.find("RefNormalizationWorkload");
244 BOOST_TEST(found != std::string::npos);
245 // No Contains SyncMemGeneric (Created when importing the output tensor handle)
246 found = dump.find("SyncMemGeneric");
247 BOOST_TEST(found == std::string::npos);
248 // Contains CopyMemGeneric
249 found = dump.find("CopyMemGeneric");
250 BOOST_TEST(found != std::string::npos);
251}
252
253inline void ImportAlignedPointerTest(std::vector<BackendId> backends)
254{
255 using namespace armnn;
256
257 // Create runtime in which test will run
258 IRuntime::CreationOptions options;
259 IRuntimePtr runtime(armnn::IRuntime::Create(options));
260
261 // build up the structure of the network
262 INetworkPtr net(INetwork::Create());
263
264 IConnectableLayer* input = net->AddInputLayer(0);
265
266 NormalizationDescriptor descriptor;
267 IConnectableLayer* norm = net->AddNormalizationLayer(descriptor);
268
269 IConnectableLayer* output = net->AddOutputLayer(0);
270
271 input->GetOutputSlot(0).Connect(norm->GetInputSlot(0));
272 norm->GetOutputSlot(0).Connect(output->GetInputSlot(0));
273
274 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 1 }, DataType::Float32));
275 norm->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 1 }, DataType::Float32));
276
277 // Optimize the network
278 IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime->GetDeviceSpec());
279
280 // Loads it into the runtime.
281 NetworkId netId;
282 runtime->LoadNetwork(netId, std::move(optNet));
283
284 // Creates structures for input & output
285 std::vector<float> inputData
286 {
287 1.0f, 2.0f, 3.0f, 4.0f
288 };
289
290 std::vector<float> outputData(4);
291
292 InputTensors inputTensors
293 {
294 {0,armnn::ConstTensor(runtime->GetInputTensorInfo(netId, 0), inputData.data())},
295 };
296 OutputTensors outputTensors
297 {
298 {0,armnn::Tensor(runtime->GetOutputTensorInfo(netId, 0), outputData.data())}
299 };
300
301 // The result of the inference is not important, just the fact that there
302 // should not be CopyMemGeneric workloads.
303 runtime->GetProfiler(netId)->EnableProfiling(true);
304
305 // Do the inference
306 runtime->EnqueueWorkload(netId, inputTensors, outputTensors);
307
308 // Retrieve the Profiler.Print() output to get the workload execution
309 ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
310 std::stringstream ss;
311 profilerManager.GetProfiler()->Print(ss);;
312 std::string dump = ss.str();
313
314 // Contains RefNormalizationWorkload
315 std::size_t found = dump.find("RefNormalizationWorkload");
316 BOOST_TEST(found != std::string::npos);
317 // Contains SyncMemGeneric
318 found = dump.find("SyncMemGeneric");
319 BOOST_TEST(found != std::string::npos);
320 // No contains CopyMemGeneric
321 found = dump.find("CopyMemGeneric");
322 BOOST_TEST(found == std::string::npos);
323}
324
Nattapat Chaimanowong1fcb4ff2019-01-24 15:25:26 +0000325} // anonymous namespace