blob: b1ee89ac3c247e2fe0f8f4598d118ab33f5bc081 [file] [log] [blame]
Finn Williamsb76eaed2021-03-31 16:22:40 +01001//
2// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include <armnn/Exceptions.hpp>
7
Colm Donelan0c479742021-12-10 12:43:54 +00008#include <armnn/backends/TensorHandle.hpp>
9#include <armnn/backends/Workload.hpp>
Finn Williamsb76eaed2021-03-31 16:22:40 +010010
Sadik Armagan1625efc2021-06-10 18:24:34 +010011#include <doctest/doctest.h>
Finn Williamsb76eaed2021-03-31 16:22:40 +010012
Jim Flynn27761832022-03-20 21:52:17 +000013#include <thread>
14
Finn Williamsb76eaed2021-03-31 16:22:40 +010015using namespace armnn;
16
Finn Williamsb76eaed2021-03-31 16:22:40 +010017
18namespace
19{
20
Sadik Armagan1625efc2021-06-10 18:24:34 +010021TEST_SUITE("WorkloadAsyncExecuteTests")
22{
23
Finn Williamsb76eaed2021-03-31 16:22:40 +010024struct Workload0 : BaseWorkload<ElementwiseUnaryQueueDescriptor>
25{
26 Workload0(const ElementwiseUnaryQueueDescriptor& descriptor, const WorkloadInfo& info)
Sadik Armagan1625efc2021-06-10 18:24:34 +010027 : BaseWorkload(descriptor, info)
Finn Williamsb76eaed2021-03-31 16:22:40 +010028 {
29 }
30
31 Workload0() : BaseWorkload(ElementwiseUnaryQueueDescriptor(), WorkloadInfo())
32 {
33 }
34
35 void Execute() const
36 {
37 int* inVals = static_cast<int*>(m_Data.m_Inputs[0][0].Map());
38 int* outVals = static_cast<int*>(m_Data.m_Outputs[0][0].Map());
39
Sadik Armagan1625efc2021-06-10 18:24:34 +010040 for (unsigned int i = 0;
41 i < m_Data.m_Inputs[0][0].GetShape().GetNumElements();
42 ++i)
Finn Williamsb76eaed2021-03-31 16:22:40 +010043 {
44 outVals[i] = inVals[i] * outVals[i];
45 inVals[i] = outVals[i];
46 }
47 }
48
49 void ExecuteAsync(WorkingMemDescriptor& desc)
50 {
51 int* inVals = static_cast<int*>(desc.m_Inputs[0][0].Map());
52 int* outVals = static_cast<int*>(desc.m_Outputs[0][0].Map());
53
Sadik Armagan1625efc2021-06-10 18:24:34 +010054 for (unsigned int i = 0;
55 i < desc.m_Inputs[0][0].GetShape().GetNumElements();
56 ++i)
Finn Williamsb76eaed2021-03-31 16:22:40 +010057 {
58 outVals[i] = inVals[i] + outVals[i];
59 inVals[i] = outVals[i];
60 }
61 }
62
63 QueueDescriptor* GetQueueDescriptor()
64 {
65 return &m_Data;
66 }
67};
68
69struct Workload1 : BaseWorkload<ElementwiseUnaryQueueDescriptor>
70{
71 Workload1(const ElementwiseUnaryQueueDescriptor& descriptor, const WorkloadInfo& info)
Sadik Armagan1625efc2021-06-10 18:24:34 +010072 : BaseWorkload(descriptor, info)
Finn Williamsb76eaed2021-03-31 16:22:40 +010073 {
74 }
75
76 void Execute() const
77 {
78 int* inVals = static_cast<int*>(m_Data.m_Inputs[0][0].Map());
79 int* outVals = static_cast<int*>(m_Data.m_Outputs[0][0].Map());
80
Sadik Armagan1625efc2021-06-10 18:24:34 +010081 for (unsigned int i = 0;
82 i < m_Data.m_Inputs[0][0].GetShape().GetNumElements();
83 ++i)
Finn Williamsb76eaed2021-03-31 16:22:40 +010084 {
85 outVals[i] = inVals[i] * outVals[i];
86 inVals[i] = outVals[i];
87 }
88 }
89};
90
91void ValidateTensor(ITensorHandle* tensorHandle, int expectedValue)
92{
93 int* actualOutput = static_cast<int*>(tensorHandle->Map());
94
95 bool allValuesCorrect = true;
Sadik Armagan1625efc2021-06-10 18:24:34 +010096 for (unsigned int i = 0;
97 i < tensorHandle->GetShape().GetNumElements();
98 ++i)
Finn Williamsb76eaed2021-03-31 16:22:40 +010099 {
100 if (actualOutput[i] != expectedValue)
101 {
102 allValuesCorrect = false;
103 }
104 }
105
Sadik Armagan1625efc2021-06-10 18:24:34 +0100106 CHECK(allValuesCorrect);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100107}
108
109template<typename Workload>
110std::unique_ptr<Workload> CreateWorkload(TensorInfo info, ITensorHandle* inputTensor, ITensorHandle* outputTensor)
111{
112 WorkloadInfo workloadInfo;
113 workloadInfo.m_InputTensorInfos = std::vector<TensorInfo>{info};
114 workloadInfo.m_OutputTensorInfos = std::vector<TensorInfo>{info};
115
116 ElementwiseUnaryQueueDescriptor elementwiseUnaryQueueDescriptor;
117 elementwiseUnaryQueueDescriptor.m_Inputs = std::vector<ITensorHandle*>{inputTensor};
118 elementwiseUnaryQueueDescriptor.m_Outputs = std::vector<ITensorHandle*>{outputTensor};
119
120 return std::make_unique<Workload>(elementwiseUnaryQueueDescriptor, workloadInfo);
121}
122
Sadik Armagan1625efc2021-06-10 18:24:34 +0100123TEST_CASE("TestAsyncExecute")
Finn Williamsb76eaed2021-03-31 16:22:40 +0100124{
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100125 TensorInfo info({5}, DataType::Signed32, 0.0, 0, true);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100126
127 int inVals[5]{2, 2, 2, 2, 2};
128 int outVals[5]{1, 1, 1, 1, 1};
129
130 int expectedExecuteval = 2;
131 int expectedExecuteAsyncval = 3;
132
133 ConstTensor constInputTensor(info, inVals);
134 ConstTensor constOutputTensor(info, outVals);
135
James Conroy1f58f032021-04-27 17:13:27 +0100136 ScopedTensorHandle syncInput0(constInputTensor);
137 ScopedTensorHandle syncOutput0(constOutputTensor);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100138
139 std::unique_ptr<Workload0> workload0 = CreateWorkload<Workload0>(info, &syncInput0, &syncOutput0);
140
141 workload0.get()->Execute();
142
James Conroy1f58f032021-04-27 17:13:27 +0100143 ScopedTensorHandle asyncInput0(constInputTensor);
144 ScopedTensorHandle asyncOutput0(constOutputTensor);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100145
146 WorkingMemDescriptor workingMemDescriptor0;
147 workingMemDescriptor0.m_Inputs = std::vector<ITensorHandle*>{&asyncInput0};
148 workingMemDescriptor0.m_Outputs = std::vector<ITensorHandle*>{&asyncOutput0};
149
150 workload0.get()->ExecuteAsync(workingMemDescriptor0);
151
152 // Inputs are also changed by the execute/executeAsync calls to make sure there is no interference with them
153 ValidateTensor(workingMemDescriptor0.m_Outputs[0], expectedExecuteAsyncval);
154 ValidateTensor(workingMemDescriptor0.m_Inputs[0], expectedExecuteAsyncval);
155
156 ValidateTensor(&workload0.get()->GetQueueDescriptor()->m_Outputs[0][0], expectedExecuteval);
157 ValidateTensor(&workload0.get()->GetQueueDescriptor()->m_Inputs[0][0], expectedExecuteval);
158}
159
Sadik Armagan1625efc2021-06-10 18:24:34 +0100160TEST_CASE("TestDefaultAsyncExecute")
Finn Williamsb76eaed2021-03-31 16:22:40 +0100161{
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100162 TensorInfo info({5}, DataType::Signed32, 0.0f, 0, true);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100163
164 std::vector<int> inVals{2, 2, 2, 2, 2};
165 std::vector<int> outVals{1, 1, 1, 1, 1};
166 std::vector<int> defaultVals{0, 0, 0, 0, 0};
167
168 int expectedExecuteval = 2;
169
170 ConstTensor constInputTensor(info, inVals);
171 ConstTensor constOutputTensor(info, outVals);
172 ConstTensor defaultTensor(info, &defaultVals);
173
James Conroy1f58f032021-04-27 17:13:27 +0100174 ScopedTensorHandle defaultInput = ScopedTensorHandle(defaultTensor);
175 ScopedTensorHandle defaultOutput = ScopedTensorHandle(defaultTensor);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100176
177 std::unique_ptr<Workload1> workload1 = CreateWorkload<Workload1>(info, &defaultInput, &defaultOutput);
178
James Conroy1f58f032021-04-27 17:13:27 +0100179 ScopedTensorHandle asyncInput(constInputTensor);
180 ScopedTensorHandle asyncOutput(constOutputTensor);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100181
182 WorkingMemDescriptor workingMemDescriptor;
183 workingMemDescriptor.m_Inputs = std::vector<ITensorHandle*>{&asyncInput};
184 workingMemDescriptor.m_Outputs = std::vector<ITensorHandle*>{&asyncOutput};
185
186 workload1.get()->ExecuteAsync(workingMemDescriptor);
187
188 // workload1 has no AsyncExecute implementation and so should use the default workload AsyncExecute
189 // implementation which will call workload1.Execute() in a thread safe manner
190 ValidateTensor(workingMemDescriptor.m_Outputs[0], expectedExecuteval);
191 ValidateTensor(workingMemDescriptor.m_Inputs[0], expectedExecuteval);
192}
193
Sadik Armagan1625efc2021-06-10 18:24:34 +0100194TEST_CASE("TestDefaultAsyncExeuteWithThreads")
Finn Williamsb76eaed2021-03-31 16:22:40 +0100195{
196 // Use a large vector so the threads have a chance to interact
197 unsigned int vecSize = 1000;
Cathal Corbett5b8093c2021-10-22 11:12:07 +0100198 TensorInfo info({vecSize}, DataType::Signed32, 0.0f, 0, true);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100199
200 std::vector<int> inVals1(vecSize, 2);
201 std::vector<int> outVals1(vecSize, 1);
202 std::vector<int> inVals2(vecSize, 5);
203 std::vector<int> outVals2(vecSize, -1);
204
205 std::vector<int> defaultVals(vecSize, 0);
206
207 int expectedExecuteval1 = 4;
208 int expectedExecuteval2 = 25;
209 ConstTensor constInputTensor1(info, inVals1);
210 ConstTensor constOutputTensor1(info, outVals1);
211
212 ConstTensor constInputTensor2(info, inVals2);
213 ConstTensor constOutputTensor2(info, outVals2);
214
Ryan OShea49428b72022-02-07 11:16:23 +0000215 ConstTensor defaultTensor(info, defaultVals.data());
Finn Williamsb76eaed2021-03-31 16:22:40 +0100216
James Conroy1f58f032021-04-27 17:13:27 +0100217 ScopedTensorHandle defaultInput = ScopedTensorHandle(defaultTensor);
218 ScopedTensorHandle defaultOutput = ScopedTensorHandle(defaultTensor);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100219 std::unique_ptr<Workload1> workload = CreateWorkload<Workload1>(info, &defaultInput, &defaultOutput);
220
James Conroy1f58f032021-04-27 17:13:27 +0100221 ScopedTensorHandle asyncInput1(constInputTensor1);
222 ScopedTensorHandle asyncOutput1(constOutputTensor1);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100223
224 WorkingMemDescriptor workingMemDescriptor1;
225 workingMemDescriptor1.m_Inputs = std::vector<ITensorHandle*>{&asyncInput1};
226 workingMemDescriptor1.m_Outputs = std::vector<ITensorHandle*>{&asyncOutput1};
227
228
James Conroy1f58f032021-04-27 17:13:27 +0100229 ScopedTensorHandle asyncInput2(constInputTensor2);
230 ScopedTensorHandle asyncOutput2(constOutputTensor2);
Finn Williamsb76eaed2021-03-31 16:22:40 +0100231
232 WorkingMemDescriptor workingMemDescriptor2;
233 workingMemDescriptor2.m_Inputs = std::vector<ITensorHandle*>{&asyncInput2};
234 workingMemDescriptor2.m_Outputs = std::vector<ITensorHandle*>{&asyncOutput2};
235
236 std::thread thread1 = std::thread([&]()
237 {
238 workload.get()->ExecuteAsync(workingMemDescriptor1);
239 workload.get()->ExecuteAsync(workingMemDescriptor1);
240 });
241
242 std::thread thread2 = std::thread([&]()
243 {
244 workload.get()->ExecuteAsync(workingMemDescriptor2);
245 workload.get()->ExecuteAsync(workingMemDescriptor2);
246 });
247
248 thread1.join();
249 thread2.join();
250
251 ValidateTensor(workingMemDescriptor1.m_Outputs[0], expectedExecuteval1);
252 ValidateTensor(workingMemDescriptor1.m_Inputs[0], expectedExecuteval1);
253
254 ValidateTensor(workingMemDescriptor2.m_Outputs[0], expectedExecuteval2);
255 ValidateTensor(workingMemDescriptor2.m_Inputs[0], expectedExecuteval2);
256}
257
Sadik Armagan1625efc2021-06-10 18:24:34 +0100258}
Finn Williamsb76eaed2021-03-31 16:22:40 +0100259
Jim Flynn27761832022-03-20 21:52:17 +0000260}