blob: e29782f890e080192da922aa6e305a7262637b2d [file] [log] [blame]
Mike Kelly386ff1a2021-03-29 15:04:50 +01001//
2// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
8#include <ResolveType.hpp>
9
10#include <armnn/IWorkingMemHandle.hpp>
11#include <armnn/INetwork.hpp>
Finn Williamsf364d532021-06-09 17:07:33 +010012#include <armnn/Threadpool.hpp>
Keith Davise813d672021-04-22 10:10:34 +010013#include <armnn/IAsyncExecutionCallback.hpp>
Mike Kelly386ff1a2021-03-29 15:04:50 +010014
Keith Davise813d672021-04-22 10:10:34 +010015#include <AsyncExecutionCallback.hpp>
Sadik Armagana097d2a2021-11-24 15:47:28 +000016#include <CommonTestUtils.hpp>
Mike Kelly386ff1a2021-03-29 15:04:50 +010017
Sadik Armagan1625efc2021-06-10 18:24:34 +010018#include <doctest/doctest.h>
Mike Kelly386ff1a2021-03-29 15:04:50 +010019
20#include <vector>
21
22namespace armnn
23{
24
25namespace experimental
26{
27
28template<DataType ArmnnIType, DataType ArmnnOType,
29 typename TInput = ResolveType <ArmnnIType>, typename TOutput = ResolveType <ArmnnOType>>
Finn Williamsb8181f72021-04-07 10:23:21 +010030void AsyncThreadedEndToEndTestImpl(INetworkPtr network,
31 const std::vector<std::map<int, std::vector<TInput>>>& inputTensorData,
32 const std::vector<std::map<int, std::vector<TOutput>>>& expectedOutputData,
33 std::vector<BackendId> backends,
34 const size_t numberOfInferences,
35 float tolerance = 0.000001f)
36{
37 // Create Runtime in which test will run
38 IRuntime::CreationOptions options;
39 IRuntimePtr runtime(IRuntime::Create(options));
40
41 // Optimize the Network
42 IOptimizedNetworkPtr optNet = Optimize(*network, backends, runtime->GetDeviceSpec());
43
44
45 // Creates AsyncNetwork
46 NetworkId networkId = 0;
47 std::string errorMessage;
Francis Murtagh73d3e2e2021-04-29 14:23:04 +010048 const INetworkProperties networkProperties(true, MemorySource::Undefined, MemorySource::Undefined);
Finn Williamsb8181f72021-04-07 10:23:21 +010049 runtime->LoadNetwork(networkId, std::move(optNet), errorMessage, networkProperties);
50
51 std::vector<InputTensors> inputTensorsVec;
52 std::vector<OutputTensors> outputTensorsVec;
53 std::vector<std::map<int, std::vector<TOutput>>> outputStorageVec;
54 std::vector<std::unique_ptr<IWorkingMemHandle>> workingMemHandles;
55
56 for (unsigned int i = 0; i < numberOfInferences; ++i)
57 {
58 InputTensors inputTensors;
59 OutputTensors outputTensors;
60 outputStorageVec.emplace_back(std::map<int, std::vector<TOutput>>());
61
62 inputTensors.reserve(inputTensorData.size());
63 for (auto&& it : inputTensorData[i])
64 {
Cathal Corbett5b8093c2021-10-22 11:12:07 +010065 TensorInfo inputTensorInfo = runtime->GetInputTensorInfo(networkId, it.first);
66 inputTensorInfo.SetConstant(true);
Finn Williamsb8181f72021-04-07 10:23:21 +010067 inputTensors.push_back({it.first,
Cathal Corbett5b8093c2021-10-22 11:12:07 +010068 ConstTensor(inputTensorInfo, it.second.data())});
Finn Williamsb8181f72021-04-07 10:23:21 +010069 }
70
71 outputTensors.reserve(expectedOutputData.size());
72 for (auto&& it : expectedOutputData[i])
73 {
74 std::vector<TOutput> out(it.second.size());
75 outputStorageVec[i].emplace(it.first, out);
76 outputTensors.push_back({it.first,
77 Tensor(runtime->GetOutputTensorInfo(networkId, it.first),
78 outputStorageVec[i].at(it.first).data())});
79 }
80
81 inputTensorsVec.push_back(inputTensors);
82 outputTensorsVec.push_back(outputTensors);
83
84 workingMemHandles.push_back(runtime->CreateWorkingMemHandle(networkId));
85 }
86
87 std::vector<std::thread> threads;
88 for (unsigned int i = 0; i < numberOfInferences; ++i)
89 {
90 // Access the vectors before we do anything multi-threaded
91 InputTensors& inputTensors = inputTensorsVec[i];
92 OutputTensors& outputTensors = outputTensorsVec[i];
93 IWorkingMemHandle& workingMemHandle = *workingMemHandles[i].get();
94
95 threads.emplace_back([&]()
96 {
97 // Run the async network
98 runtime->Execute(workingMemHandle, inputTensors, outputTensors);
99 });
100 }
101
102 for (unsigned int i = 0; i < numberOfInferences; ++i)
103 {
104 threads[i].join();
105 }
106
107 // Checks the results.
108 for (unsigned int i = 0; i < numberOfInferences; ++i)
109 {
110 for (auto &&it : expectedOutputData[i])
111 {
112 std::vector<TOutput> out = outputStorageVec[i].at(it.first);
113 for (unsigned int j = 0; j < out.size(); ++j)
114 {
Sadik Armagan1625efc2021-06-10 18:24:34 +0100115 CHECK(Compare<ArmnnOType>(it.second[j], out[j], tolerance) == true);
Finn Williamsb8181f72021-04-07 10:23:21 +0100116 }
117 }
118 }
119
120}
121
Finn Williamsb8181f72021-04-07 10:23:21 +0100122template<DataType ArmnnIType, DataType ArmnnOType,
Keith Davise813d672021-04-22 10:10:34 +0100123 typename TInput = ResolveType<ArmnnIType>, typename TOutput = ResolveType<ArmnnOType>>
Mike Kelly386ff1a2021-03-29 15:04:50 +0100124void AsyncEndToEndTestImpl(INetworkPtr network,
125 const std::map<int, std::vector<TInput>>& inputTensorData,
126 const std::map<int, std::vector<TOutput>>& expectedOutputData,
127 std::vector<BackendId> backends,
Keith Davise813d672021-04-22 10:10:34 +0100128 float tolerance = 0.000001f,
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100129 size_t numThreads = 1)
Mike Kelly386ff1a2021-03-29 15:04:50 +0100130{
131 // Create Runtime in which test will run
132 IRuntime::CreationOptions options;
Keith Davise813d672021-04-22 10:10:34 +0100133 IRuntimePtr runtime(IRuntime::Create(options));
Mike Kelly386ff1a2021-03-29 15:04:50 +0100134
135 // Optimize the Network
136 IOptimizedNetworkPtr optNet = Optimize(*network, backends, runtime->GetDeviceSpec());
137
138 // Creates AsyncNetwork
139 NetworkId networkId = 0;
Keith Davise813d672021-04-22 10:10:34 +0100140
Mike Kelly386ff1a2021-03-29 15:04:50 +0100141 std::string errorMessage;
Keith Davise813d672021-04-22 10:10:34 +0100142
Finn Williamsf364d532021-06-09 17:07:33 +0100143 const INetworkProperties networkProperties(true, MemorySource::Undefined, MemorySource::Undefined);
Keith Davise813d672021-04-22 10:10:34 +0100144
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100145 runtime->LoadNetwork(networkId, std::move(optNet), errorMessage, networkProperties);
Mike Kelly386ff1a2021-03-29 15:04:50 +0100146
147 InputTensors inputTensors;
148 inputTensors.reserve(inputTensorData.size());
149 for (auto&& it : inputTensorData)
150 {
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100151 TensorInfo inputTensorInfo = runtime->GetInputTensorInfo(networkId, it.first);
152 inputTensorInfo.SetConstant(true);
Mike Kelly386ff1a2021-03-29 15:04:50 +0100153 inputTensors.push_back({it.first,
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100154 ConstTensor(inputTensorInfo, it.second.data())});
Mike Kelly386ff1a2021-03-29 15:04:50 +0100155 }
156
157 OutputTensors outputTensors;
158 outputTensors.reserve(expectedOutputData.size());
159 std::map<int, std::vector<TOutput>> outputStorage;
160 for (auto&& it : expectedOutputData)
161 {
162 std::vector<TOutput> out(it.second.size());
163 outputStorage.emplace(it.first, out);
164 outputTensors.push_back({it.first,
Mike Kelly55a8ffd2021-04-07 20:10:49 +0100165 Tensor(runtime->GetOutputTensorInfo(networkId, it.first),
Mike Kelly386ff1a2021-03-29 15:04:50 +0100166 outputStorage.at(it.first).data())});
167 }
168
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100169 if (numThreads <= 1)
Keith Davise813d672021-04-22 10:10:34 +0100170 {
171 // Create WorkingMemHandle for this async network
172 std::unique_ptr<IWorkingMemHandle> workingMemHandle = runtime->CreateWorkingMemHandle(networkId);
173 IWorkingMemHandle& workingMemHandleRef = *workingMemHandle.get();
Mike Kelly386ff1a2021-03-29 15:04:50 +0100174
Keith Davise813d672021-04-22 10:10:34 +0100175 // Run the async network
176 runtime->Execute(workingMemHandleRef, inputTensors, outputTensors);
177 }
178 else
179 {
Finn Williamsf364d532021-06-09 17:07:33 +0100180 std::vector<std::shared_ptr<IWorkingMemHandle>> memHandles;
Mike Kelly386ff1a2021-03-29 15:04:50 +0100181
Finn Williamsf364d532021-06-09 17:07:33 +0100182 for (size_t i = 0; i < numThreads; ++i)
Keith Davise813d672021-04-22 10:10:34 +0100183 {
Finn Williamsf364d532021-06-09 17:07:33 +0100184 memHandles.emplace_back(runtime->CreateWorkingMemHandle(networkId));
Keith Davise813d672021-04-22 10:10:34 +0100185 }
186
Finn Williamsf364d532021-06-09 17:07:33 +0100187 Threadpool threadpool(numThreads, runtime.get(), memHandles);
188 AsyncCallbackManager callbackManager;
189
Keith Davise813d672021-04-22 10:10:34 +0100190 // For the asyncronous execution, we are adding a pool of working memory handles (1 per thread) in the
191 // LoadedNetwork with a each scheduled inference having a spefic priority
Finn Williamsf364d532021-06-09 17:07:33 +0100192 for (size_t i = 0; i < 1000; ++i)
Keith Davise813d672021-04-22 10:10:34 +0100193 {
Finn Williamsf364d532021-06-09 17:07:33 +0100194 threadpool.Schedule(networkId,
195 inputTensors,
196 outputTensors,
197 static_cast<QosExecPriority>(rand()%3),
198 callbackManager.GetNewCallback());
Keith Davise813d672021-04-22 10:10:34 +0100199 }
200
201 // Wait until the execution signals a notify
Finn Williamsf364d532021-06-09 17:07:33 +0100202 for (size_t i = 0; i < 1000; ++i)
Keith Davise813d672021-04-22 10:10:34 +0100203 {
Finn Williamsf364d532021-06-09 17:07:33 +0100204 auto cb = callbackManager.GetNotifiedCallback();
205
Keith Davise813d672021-04-22 10:10:34 +0100206 // Checks the results.
Sadik Armagan1625efc2021-06-10 18:24:34 +0100207 CHECK(cb->GetStatus() == Status::Success);
Keith Davise813d672021-04-22 10:10:34 +0100208 }
209 }
210
Mike Kelly386ff1a2021-03-29 15:04:50 +0100211 for (auto&& it : expectedOutputData)
212 {
213 std::vector<TOutput> out = outputStorage.at(it.first);
Keith Davise813d672021-04-22 10:10:34 +0100214
Mike Kelly386ff1a2021-03-29 15:04:50 +0100215 for (unsigned int i = 0; i < out.size(); ++i)
216 {
Sadik Armagan1625efc2021-06-10 18:24:34 +0100217 CHECK(Compare<ArmnnOType>(it.second[i], out[i], tolerance) == true);
Mike Kelly386ff1a2021-03-29 15:04:50 +0100218 }
219 }
220}
221
222template<typename armnn::DataType DataType>
223INetworkPtr CreateStridedSliceNetwork(const TensorShape& inputShape,
224 const TensorShape& outputShape,
225 const std::vector<int>& beginData,
226 const std::vector<int>& endData,
227 const std::vector<int>& stridesData,
228 int beginMask = 0,
229 int endMask = 0,
230 int shrinkAxisMask = 0,
231 int ellipsisMask = 0,
232 int newAxisMask = 0,
233 const float qScale = 1.0f,
234 const int32_t qOffset = 0)
235{
236 using namespace armnn;
237 // Builds up the structure of the network.
238 INetworkPtr net(INetwork::Create());
239
240 TensorInfo inputTensorInfo(inputShape, DataType, qScale, qOffset);
241 TensorInfo outputTensorInfo(outputShape, DataType, qScale, qOffset);
242
243 armnn::StridedSliceDescriptor stridedSliceDescriptor;
244 stridedSliceDescriptor.m_Begin = beginData;
245 stridedSliceDescriptor.m_End = endData;
246 stridedSliceDescriptor.m_Stride = stridesData;
247 stridedSliceDescriptor.m_BeginMask = beginMask;
248 stridedSliceDescriptor.m_EndMask = endMask;
249 stridedSliceDescriptor.m_ShrinkAxisMask = shrinkAxisMask;
250 stridedSliceDescriptor.m_EllipsisMask = ellipsisMask;
251 stridedSliceDescriptor.m_NewAxisMask = newAxisMask;
252
253 IConnectableLayer* input = net->AddInputLayer(0, "Input_Layer");
254 IConnectableLayer* stridedSlice = net->AddStridedSliceLayer(stridedSliceDescriptor, "splitter");
255 IConnectableLayer* output = net->AddOutputLayer(0);
256
257 Connect(input, stridedSlice, inputTensorInfo, 0, 0);
258 Connect(stridedSlice, output, outputTensorInfo, 0, 0);
259
260 return net;
261}
262
263template<armnn::DataType ArmnnType>
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100264void StridedSlicedEndToEndTest(const std::vector<BackendId>& backends, size_t numThreads)
Mike Kelly386ff1a2021-03-29 15:04:50 +0100265{
266 using namespace armnn;
267 using T = ResolveType<ArmnnType>;
268
269 const TensorShape& inputShape = {3, 2, 3, 1};
270 const TensorShape& outputShape = {1, 2, 3, 1};
271 const std::vector<int>& beginData = {1, 0, 0, 0};
272 const std::vector<int>& endData = {2, 2, 3, 1};
273 const std::vector<int>& stridesData = {1, 1, 1, 1};
274 int beginMask = 0;
275 int endMask = 0;
276 int shrinkAxisMask = 0;
277 int ellipsisMask = 0;
278 int newAxisMask = 0;
279
280 // Builds up the structure of the network
281 INetworkPtr net = CreateStridedSliceNetwork<ArmnnType>(inputShape,
282 outputShape,
283 beginData,
284 endData,
285 stridesData,
286 beginMask,
287 endMask,
288 shrinkAxisMask,
289 ellipsisMask,
290 newAxisMask);
291
Sadik Armagan1625efc2021-06-10 18:24:34 +0100292 CHECK(net);
Mike Kelly386ff1a2021-03-29 15:04:50 +0100293 // Creates structures for input & output.
294 std::vector<T> inputData{
295 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f,
296
297 3.0f, 3.0f, 3.0f, 4.0f, 4.0f, 4.0f,
298
299 5.0f, 5.0f, 5.0f, 6.0f, 6.0f, 6.0f
300 };
301
302 std::vector<T> outputExpected{
303 3.0f, 3.0f, 3.0f, 4.0f, 4.0f, 4.0f
304 };
305
306 std::map<int, std::vector<T>> inputTensorData = {{0, inputData}};
307 std::map<int, std::vector<T>> expectedOutputData = {{0, outputExpected}};
308
Kevin Mayb4b3ac92021-05-21 16:42:21 +0100309 AsyncEndToEndTestImpl<ArmnnType, ArmnnType>(move(net),
310 inputTensorData,
311 expectedOutputData,
312 backends,
313 0.000001f,
314 numThreads);
Finn Williamsb8181f72021-04-07 10:23:21 +0100315}
316
317template<armnn::DataType ArmnnType>
318void StridedSlicedMultiThreadedEndToEndTest(const std::vector<BackendId>& backends)
319{
320 using namespace armnn;
321 using T = ResolveType<ArmnnType>;
322
323 const TensorShape& inputShape = {3, 2, 3, 1};
324 const TensorShape& outputShape = {1, 2, 3, 1};
325 const std::vector<int>& beginData = {1, 0, 0, 0};
326 const std::vector<int>& endData = {2, 2, 3, 1};
327 const std::vector<int>& stridesData = {1, 1, 1, 1};
328 int beginMask = 0;
329 int endMask = 0;
330 int shrinkAxisMask = 0;
331 int ellipsisMask = 0;
332 int newAxisMask = 0;
333
334 // Builds up the structure of the network
335 INetworkPtr net = CreateStridedSliceNetwork<ArmnnType>(inputShape,
336 outputShape,
337 beginData,
338 endData,
339 stridesData,
340 beginMask,
341 endMask,
342 shrinkAxisMask,
343 ellipsisMask,
344 newAxisMask);
345
Sadik Armagan1625efc2021-06-10 18:24:34 +0100346 CHECK(net);
Finn Williamsb8181f72021-04-07 10:23:21 +0100347
348 // Creates structures for input & output.
349 std::vector<T> inputData1{
350 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f,
351
352 3.0f, 3.0f, 3.0f, 4.0f, 4.0f, 4.0f,
353
354 5.0f, 5.0f, 5.0f, 6.0f, 6.0f, 6.0f
355 };
356
357 std::vector<T> outputExpected1{ 3.0f, 3.0f, 3.0f, 4.0f, 4.0f, 4.0f };
358
359 // Creates structures for input & output.
360 std::vector<T> inputData2{
361 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f,
362
363 8.0f, 8.0f, 8.0f, 7.0f, 7.0f, 7.0f,
364
365 5.0f, 5.0f, 5.0f, 6.0f, 6.0f, 6.0f
366 };
367
368 std::vector<T> outputExpected2{ 8.0f, 8.0f, 8.0f, 7.0f, 7.0f, 7.0f };
369
370 std::vector<std::map<int, std::vector<T>>> inputTensors;
371 std::vector<std::map<int, std::vector<T>>> outputTensors;
372
373 inputTensors.push_back(std::map<int, std::vector<T>> {{0, inputData1}});
374 inputTensors.push_back(std::map<int, std::vector<T>> {{0, inputData2}});
375 outputTensors.push_back(std::map<int, std::vector<T>> {{0, outputExpected1}});
376 outputTensors.push_back(std::map<int, std::vector<T>> {{0, outputExpected2}});
377
378 AsyncThreadedEndToEndTestImpl<ArmnnType, ArmnnType>(move(net), inputTensors, outputTensors, backends, 2);
Mike Kelly386ff1a2021-03-29 15:04:50 +0100379}
380
381} // experimental namespace
382
383} // armnn namespace
384