blob: 3e96dcbf2dd066d8c379cba50e10544af9da773e [file] [log] [blame]
Michalis Spyroucaa7dee2019-09-09 19:23:39 +01001/*
Matthew Bentham945b8da2023-07-12 11:54:59 +00002 * Copyright (c) 2019-2021, 2023 Arm Limited.
Michalis Spyroucaa7dee2019-09-09 19:23:39 +01003 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#ifndef ARM_COMPUTE_TEST_UNIT_DYNAMIC_TENSOR
25#define ARM_COMPUTE_TEST_UNIT_DYNAMIC_TENSOR
26
27#include "arm_compute/core/TensorShape.h"
28#include "arm_compute/core/Types.h"
29#include "tests/AssetsLibrary.h"
30#include "tests/Globals.h"
31#include "tests/IAccessor.h"
32#include "tests/framework/Asserts.h"
33#include "tests/framework/Fixture.h"
34#include "tests/validation/Helpers.h"
Georgios Pinitas2ff00092019-09-30 16:50:08 +010035#include "tests/validation/reference/ConvolutionLayer.h"
Michalis Spyroucaa7dee2019-09-09 19:23:39 +010036#include "tests/validation/reference/NormalizationLayer.h"
37
38namespace arm_compute
39{
40namespace test
41{
42namespace validation
43{
Michalis Spyroucaa7dee2019-09-09 19:23:39 +010044template <typename AllocatorType,
45 typename LifetimeMgrType,
46 typename PoolMgrType,
47 typename MemoryMgrType>
48struct MemoryManagementService
49{
50public:
Georgios Pinitas2ff00092019-09-30 16:50:08 +010051 using LftMgrType = LifetimeMgrType;
52
53public:
Michalis Spyroucaa7dee2019-09-09 19:23:39 +010054 MemoryManagementService()
55 : allocator(), lifetime_mgr(nullptr), pool_mgr(nullptr), mm(nullptr), mg(), num_pools(0)
56 {
57 lifetime_mgr = std::make_shared<LifetimeMgrType>();
58 pool_mgr = std::make_shared<PoolMgrType>();
59 mm = std::make_shared<MemoryMgrType>(lifetime_mgr, pool_mgr);
60 mg = MemoryGroup(mm);
61 }
62
63 void populate(size_t pools)
64 {
65 mm->populate(allocator, pools);
66 num_pools = pools;
67 }
68
69 void clear()
70 {
71 mm->clear();
72 num_pools = 0;
73 }
74
75 void validate(bool validate_finalized) const
76 {
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +010077 ARM_COMPUTE_ASSERT(mm->pool_manager() != nullptr);
78 ARM_COMPUTE_ASSERT(mm->lifetime_manager() != nullptr);
Michalis Spyroucaa7dee2019-09-09 19:23:39 +010079
80 if(validate_finalized)
81 {
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +010082 ARM_COMPUTE_ASSERT(mm->lifetime_manager()->are_all_finalized());
Michalis Spyroucaa7dee2019-09-09 19:23:39 +010083 }
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +010084 ARM_COMPUTE_ASSERT(mm->pool_manager()->num_pools() == num_pools);
Michalis Spyroucaa7dee2019-09-09 19:23:39 +010085 }
86
87 AllocatorType allocator;
88 std::shared_ptr<LifetimeMgrType> lifetime_mgr;
89 std::shared_ptr<PoolMgrType> pool_mgr;
90 std::shared_ptr<MemoryMgrType> mm;
91 MemoryGroup mg;
92 size_t num_pools;
93};
Georgios Pinitasb785dd42019-09-19 12:09:32 +010094
95template <typename MemoryMgrType, typename FuncType, typename ITensorType>
96class SimpleFunctionWrapper
97{
98public:
99 SimpleFunctionWrapper(std::shared_ptr<MemoryMgrType> mm)
100 : _func(mm)
101 {
102 }
103 void configure(ITensorType *src, ITensorType *dst)
104 {
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100105 ARM_COMPUTE_UNUSED(src, dst);
Georgios Pinitasb785dd42019-09-19 12:09:32 +0100106 }
107 void run()
108 {
109 _func.run();
110 }
111
112private:
113 FuncType _func;
114};
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100115
116/** Simple test case to run a single function with different shapes twice.
117 *
118 * Runs a specified function twice, where the second time the size of the input/output is different
119 * Internal memory of the function and input/output are managed by different services
120 */
121template <typename TensorType,
122 typename AccessorType,
Georgios Pinitas2ff00092019-09-30 16:50:08 +0100123 typename MemoryManagementServiceType,
Georgios Pinitasb785dd42019-09-19 12:09:32 +0100124 typename SimpleFunctionWrapperType>
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100125class DynamicTensorType3SingleFunction : public framework::Fixture
126{
Georgios Pinitas2ff00092019-09-30 16:50:08 +0100127 using T = float;
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100128
129public:
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100130 void setup(TensorShape input_level0, TensorShape input_level1)
131 {
132 input_l0 = input_level0;
133 input_l1 = input_level1;
134 run();
135 }
136
137protected:
138 void run()
139 {
140 MemoryManagementServiceType serv_internal;
141 MemoryManagementServiceType serv_cross;
142 const size_t num_pools = 1;
143 const bool validate_finalized = true;
144
145 // Create Tensor shapes.
146 TensorShape level_0 = TensorShape(input_l0);
147 TensorShape level_1 = TensorShape(input_l1);
148
149 // Level 0
150 // Create tensors
Georgios Pinitasb785dd42019-09-19 12:09:32 +0100151 TensorType src = create_tensor<TensorType>(level_0, DataType::F32, 1);
152 TensorType dst = create_tensor<TensorType>(level_0, DataType::F32, 1);
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100153
154 serv_cross.mg.manage(&src);
155 serv_cross.mg.manage(&dst);
156
157 // Create and configure function
Georgios Pinitasb785dd42019-09-19 12:09:32 +0100158 SimpleFunctionWrapperType layer(serv_internal.mm);
159 layer.configure(&src, &dst);
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100160
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100161 ARM_COMPUTE_ASSERT(src.info()->is_resizable());
162 ARM_COMPUTE_ASSERT(dst.info()->is_resizable());
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100163
164 // Allocate tensors
165 src.allocator()->allocate();
166 dst.allocator()->allocate();
167
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100168 ARM_COMPUTE_ASSERT(!src.info()->is_resizable());
169 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100170
171 // Populate and validate memory manager
172 serv_cross.populate(num_pools);
173 serv_internal.populate(num_pools);
174 serv_cross.validate(validate_finalized);
175 serv_internal.validate(validate_finalized);
176
177 // Extract lifetime manager meta-data information
178 internal_l0 = serv_internal.lifetime_mgr->info();
179 cross_l0 = serv_cross.lifetime_mgr->info();
180
181 // Acquire memory manager, fill tensors and compute functions
182 serv_cross.mg.acquire();
Georgios Pinitasb785dd42019-09-19 12:09:32 +0100183 arm_compute::test::library->fill_tensor_value(AccessorType(src), 12.f);
184 layer.run();
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100185 serv_cross.mg.release();
186
187 // Clear manager
188 serv_cross.clear();
189 serv_internal.clear();
190 serv_cross.validate(validate_finalized);
191 serv_internal.validate(validate_finalized);
192
193 // Level 1
194 // Update the tensor shapes
195 src.info()->set_tensor_shape(level_1);
196 dst.info()->set_tensor_shape(level_1);
197 src.info()->set_is_resizable(true);
198 dst.info()->set_is_resizable(true);
199
200 serv_cross.mg.manage(&src);
201 serv_cross.mg.manage(&dst);
202
203 // Re-configure the function
Georgios Pinitasb785dd42019-09-19 12:09:32 +0100204 layer.configure(&src, &dst);
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100205
206 // Allocate tensors
207 src.allocator()->allocate();
208 dst.allocator()->allocate();
209
210 // Populate and validate memory manager
211 serv_cross.populate(num_pools);
212 serv_internal.populate(num_pools);
213 serv_cross.validate(validate_finalized);
214 serv_internal.validate(validate_finalized);
215
216 // Extract lifetime manager meta-data information
217 internal_l1 = serv_internal.lifetime_mgr->info();
218 cross_l1 = serv_cross.lifetime_mgr->info();
219
220 // Compute functions
221 serv_cross.mg.acquire();
222 arm_compute::test::library->fill_tensor_value(AccessorType(src), 12.f);
Georgios Pinitasb785dd42019-09-19 12:09:32 +0100223 layer.run();
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100224 serv_cross.mg.release();
225
226 // Clear manager
227 serv_cross.clear();
228 serv_internal.clear();
229 serv_cross.validate(validate_finalized);
230 serv_internal.validate(validate_finalized);
231 }
232
233public:
Georgios Pinitas2ff00092019-09-30 16:50:08 +0100234 TensorShape input_l0{}, input_l1{};
235 typename MemoryManagementServiceType::LftMgrType::info_type internal_l0{}, internal_l1{};
236 typename MemoryManagementServiceType::LftMgrType::info_type cross_l0{}, cross_l1{};
237};
238
239/** Simple test case to run a single function with different shapes twice.
240 *
241 * Runs a specified function twice, where the second time the size of the input/output is different
242 * Internal memory of the function and input/output are managed by different services
243 */
244template <typename TensorType,
245 typename AccessorType,
246 typename MemoryManagementServiceType,
247 typename ComplexFunctionType>
248class DynamicTensorType3ComplexFunction : public framework::Fixture
249{
250 using T = float;
251
252public:
Georgios Pinitas2ff00092019-09-30 16:50:08 +0100253 void setup(std::vector<TensorShape> input_shapes, TensorShape weights_shape, TensorShape bias_shape, std::vector<TensorShape> output_shapes, PadStrideInfo info)
254 {
255 num_iterations = input_shapes.size();
256 _data_type = DataType::F32;
257 _data_layout = DataLayout::NHWC;
258 _input_shapes = input_shapes;
259 _output_shapes = output_shapes;
260 _weights_shape = weights_shape;
261 _bias_shape = bias_shape;
262 _info = info;
263
264 // Create function
Georgios Pinitas40f51a62020-11-21 03:04:18 +0000265 _f_target = std::make_unique<ComplexFunctionType>(_ms.mm);
Georgios Pinitas2ff00092019-09-30 16:50:08 +0100266 }
267
268 void run_iteration(unsigned int idx)
269 {
270 auto input_shape = _input_shapes[idx];
271 auto output_shape = _output_shapes[idx];
272
273 dst_ref = run_reference(input_shape, _weights_shape, _bias_shape, output_shape, _info);
274 dst_target = run_target(input_shape, _weights_shape, _bias_shape, output_shape, _info, WeightsInfo());
275 }
276
277protected:
278 template <typename U>
279 void fill(U &&tensor, int i)
280 {
281 switch(tensor.data_type())
282 {
283 case DataType::F32:
284 {
285 std::uniform_real_distribution<> distribution(-1.0f, 1.0f);
286 library->fill(tensor, distribution, i);
287 break;
288 }
289 default:
290 library->fill_tensor_uniform(tensor, i);
291 }
292 }
293
294 TensorType run_target(TensorShape input_shape, TensorShape weights_shape, TensorShape bias_shape, TensorShape output_shape,
295 PadStrideInfo info, WeightsInfo weights_info)
296 {
297 if(_data_layout == DataLayout::NHWC)
298 {
299 permute(input_shape, PermutationVector(2U, 0U, 1U));
300 permute(weights_shape, PermutationVector(2U, 0U, 1U));
301 permute(output_shape, PermutationVector(2U, 0U, 1U));
302 }
303
304 _weights_target = create_tensor<TensorType>(weights_shape, _data_type, 1, QuantizationInfo(), _data_layout);
305 _bias_target = create_tensor<TensorType>(bias_shape, _data_type, 1);
306
307 // Create tensors
308 TensorType src = create_tensor<TensorType>(input_shape, _data_type, 1, QuantizationInfo(), _data_layout);
309 TensorType dst = create_tensor<TensorType>(output_shape, _data_type, 1, QuantizationInfo(), _data_layout);
310
311 // Create and configure function
312 _f_target->configure(&src, &_weights_target, &_bias_target, &dst, info, weights_info);
313
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100314 ARM_COMPUTE_ASSERT(src.info()->is_resizable());
315 ARM_COMPUTE_ASSERT(dst.info()->is_resizable());
Georgios Pinitas2ff00092019-09-30 16:50:08 +0100316
317 // Allocate tensors
318 src.allocator()->allocate();
319 dst.allocator()->allocate();
320 _weights_target.allocator()->allocate();
321 _bias_target.allocator()->allocate();
322
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100323 ARM_COMPUTE_ASSERT(!src.info()->is_resizable());
324 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Georgios Pinitas2ff00092019-09-30 16:50:08 +0100325
326 // Fill tensors
327 fill(AccessorType(src), 0);
328 fill(AccessorType(_weights_target), 1);
329 fill(AccessorType(_bias_target), 2);
330
331 // Populate and validate memory manager
332 _ms.clear();
333 _ms.populate(1);
334 _ms.mg.acquire();
335
336 // Compute NEConvolutionLayer function
337 _f_target->run();
338 _ms.mg.release();
339
340 return dst;
341 }
342
343 SimpleTensor<T> run_reference(TensorShape input_shape, TensorShape weights_shape, TensorShape bias_shape, TensorShape output_shape, PadStrideInfo info)
344 {
345 // Create reference
346 SimpleTensor<T> src{ input_shape, _data_type, 1 };
347 SimpleTensor<T> weights{ weights_shape, _data_type, 1 };
348 SimpleTensor<T> bias{ bias_shape, _data_type, 1 };
349
350 // Fill reference
351 fill(src, 0);
352 fill(weights, 1);
353 fill(bias, 2);
354
355 return reference::convolution_layer<T>(src, weights, bias, output_shape, info);
356 }
357
358public:
359 unsigned int num_iterations{ 0 };
360 SimpleTensor<T> dst_ref{};
361 TensorType dst_target{};
362
363private:
364 DataType _data_type{ DataType::UNKNOWN };
365 DataLayout _data_layout{ DataLayout::UNKNOWN };
366 PadStrideInfo _info{};
367 std::vector<TensorShape> _input_shapes{};
368 std::vector<TensorShape> _output_shapes{};
369 TensorShape _weights_shape{};
370 TensorShape _bias_shape{};
371 MemoryManagementServiceType _ms{};
372 TensorType _weights_target{};
373 TensorType _bias_target{};
374 std::unique_ptr<ComplexFunctionType> _f_target{};
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100375};
Georgios Pinitas3d426c52019-10-10 19:35:43 +0100376
377/** Fixture that create a pipeline of Convolutions and changes the inputs dynamically
378 *
379 * Runs a list of convolutions and then resizes the inputs and reruns.
380 * Updates the memory manager and allocated memory.
381 */
382template <typename TensorType,
383 typename AccessorType,
384 typename MemoryManagementServiceType,
385 typename ComplexFunctionType>
386class DynamicTensorType2PipelineFunction : public framework::Fixture
387{
388 using T = float;
389
390public:
Georgios Pinitas3d426c52019-10-10 19:35:43 +0100391 void setup(std::vector<TensorShape> input_shapes)
392 {
393 _data_type = DataType::F32;
394 _data_layout = DataLayout::NHWC;
395 _input_shapes = input_shapes;
396
397 run();
398 }
399
400protected:
401 template <typename U>
402 void fill(U &&tensor, int i)
403 {
404 switch(tensor.data_type())
405 {
406 case DataType::F32:
407 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000408 std::uniform_real_distribution<float> distribution(-1.0f, 1.0f);
Georgios Pinitas3d426c52019-10-10 19:35:43 +0100409 library->fill(tensor, distribution, i);
410 break;
411 }
412 default:
413 library->fill_tensor_uniform(tensor, i);
414 }
415 }
416
417 void run()
418 {
419 const unsigned int num_functions = 5;
420 const unsigned int num_tensors = num_functions + 1;
421 const unsigned int num_resizes = _input_shapes.size();
422
423 for(unsigned int i = 0; i < num_functions; ++i)
424 {
Georgios Pinitas40f51a62020-11-21 03:04:18 +0000425 _functions.emplace_back(std::make_unique<ComplexFunctionType>(_ms.mm));
Georgios Pinitas3d426c52019-10-10 19:35:43 +0100426 }
427
428 for(unsigned int i = 0; i < num_resizes; ++i)
429 {
430 TensorShape input_shape = _input_shapes[i];
431 TensorShape weights_shape = TensorShape(3U, 3U, input_shape[2], input_shape[2]);
432 TensorShape output_shape = input_shape;
433 PadStrideInfo info(1U, 1U, 1U, 1U);
434
435 if(_data_layout == DataLayout::NHWC)
436 {
437 permute(input_shape, PermutationVector(2U, 0U, 1U));
438 permute(weights_shape, PermutationVector(2U, 0U, 1U));
439 permute(output_shape, PermutationVector(2U, 0U, 1U));
440 }
441
442 std::vector<TensorType> tensors(num_tensors);
443 std::vector<TensorType> ws(num_functions);
444 std::vector<TensorType> bs(num_functions);
445
446 auto tensor_info = TensorInfo(input_shape, 1, _data_type);
447 auto weights_info = TensorInfo(weights_shape, 1, _data_type);
448 tensor_info.set_data_layout(_data_layout);
449 weights_info.set_data_layout(_data_layout);
450
Georgios Pinitas9aaf09e2019-11-07 17:22:06 +0000451 tensors[0].allocator()->init(tensor_info);
Georgios Pinitas3d426c52019-10-10 19:35:43 +0100452 for(unsigned int f = 0; f < num_functions; ++f)
453 {
Georgios Pinitas3d426c52019-10-10 19:35:43 +0100454 tensors[f + 1].allocator()->init(tensor_info);
455 ws[f].allocator()->init(weights_info);
456
457 _functions[f]->configure(&tensors[f], &ws[f], nullptr, &tensors[f + 1], info);
458
459 // Allocate tensors
460 tensors[f].allocator()->allocate();
461 ws[f].allocator()->allocate();
462 }
463 tensors[num_functions].allocator()->allocate();
464
465 // Populate and validate memory manager
466 _ms.clear();
467 _ms.populate(1);
468 _ms.mg.acquire();
469
470 // Run pipeline
471 for(unsigned int f = 0; f < num_functions; ++f)
472 {
473 _functions[f]->run();
474 }
475
476 // Release memory group
477 _ms.mg.release();
478 }
479 }
480
481private:
482 DataType _data_type{ DataType::UNKNOWN };
483 DataLayout _data_layout{ DataLayout::UNKNOWN };
484 std::vector<TensorShape> _input_shapes{};
485 MemoryManagementServiceType _ms{};
486 std::vector<std::unique_ptr<ComplexFunctionType>> _functions{};
487};
Michalis Spyroucaa7dee2019-09-09 19:23:39 +0100488} // namespace validation
489} // namespace test
490} // namespace arm_compute
491#endif /* ARM_COMPUTE_TEST_UNIT_DYNAMIC_TENSOR */