xref: /aosp_15_r20/external/ComputeLibrary/tests/validation/fixtures/UNIT/DynamicTensorFixture.h (revision c217d954acce2dbc11938adb493fc0abd69584f3)
1 /*
2  * Copyright (c) 2019-2021 Arm Limited.
3  *
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"
35 #include "tests/validation/reference/ConvolutionLayer.h"
36 #include "tests/validation/reference/NormalizationLayer.h"
37 
38 namespace arm_compute
39 {
40 namespace test
41 {
42 namespace validation
43 {
44 template <typename AllocatorType,
45           typename LifetimeMgrType,
46           typename PoolMgrType,
47           typename MemoryMgrType>
48 struct MemoryManagementService
49 {
50 public:
51     using LftMgrType = LifetimeMgrType;
52 
53 public:
MemoryManagementServiceMemoryManagementService54     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 
populateMemoryManagementService63     void populate(size_t pools)
64     {
65         mm->populate(allocator, pools);
66         num_pools = pools;
67     }
68 
clearMemoryManagementService69     void clear()
70     {
71         mm->clear();
72         num_pools = 0;
73     }
74 
validateMemoryManagementService75     void validate(bool validate_finalized) const
76     {
77         ARM_COMPUTE_ASSERT(mm->pool_manager() != nullptr);
78         ARM_COMPUTE_ASSERT(mm->lifetime_manager() != nullptr);
79 
80         if(validate_finalized)
81         {
82             ARM_COMPUTE_ASSERT(mm->lifetime_manager()->are_all_finalized());
83         }
84         ARM_COMPUTE_ASSERT(mm->pool_manager()->num_pools() == num_pools);
85     }
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 };
94 
95 template <typename MemoryMgrType, typename FuncType, typename ITensorType>
96 class SimpleFunctionWrapper
97 {
98 public:
SimpleFunctionWrapper(std::shared_ptr<MemoryMgrType> mm)99     SimpleFunctionWrapper(std::shared_ptr<MemoryMgrType> mm)
100         : _func(mm)
101     {
102     }
configure(ITensorType * src,ITensorType * dst)103     void configure(ITensorType *src, ITensorType *dst)
104     {
105         ARM_COMPUTE_UNUSED(src, dst);
106     }
run()107     void run()
108     {
109         _func.run();
110     }
111 
112 private:
113     FuncType _func;
114 };
115 
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  */
121 template <typename TensorType,
122           typename AccessorType,
123           typename MemoryManagementServiceType,
124           typename SimpleFunctionWrapperType>
125 class DynamicTensorType3SingleFunction : public framework::Fixture
126 {
127     using T = float;
128 
129 public:
130     template <typename...>
setup(TensorShape input_level0,TensorShape input_level1)131     void setup(TensorShape input_level0, TensorShape input_level1)
132     {
133         input_l0 = input_level0;
134         input_l1 = input_level1;
135         run();
136     }
137 
138 protected:
run()139     void run()
140     {
141         MemoryManagementServiceType serv_internal;
142         MemoryManagementServiceType serv_cross;
143         const size_t                num_pools          = 1;
144         const bool                  validate_finalized = true;
145 
146         // Create Tensor shapes.
147         TensorShape level_0 = TensorShape(input_l0);
148         TensorShape level_1 = TensorShape(input_l1);
149 
150         // Level 0
151         // Create tensors
152         TensorType src = create_tensor<TensorType>(level_0, DataType::F32, 1);
153         TensorType dst = create_tensor<TensorType>(level_0, DataType::F32, 1);
154 
155         serv_cross.mg.manage(&src);
156         serv_cross.mg.manage(&dst);
157 
158         // Create and configure function
159         SimpleFunctionWrapperType layer(serv_internal.mm);
160         layer.configure(&src, &dst);
161 
162         ARM_COMPUTE_ASSERT(src.info()->is_resizable());
163         ARM_COMPUTE_ASSERT(dst.info()->is_resizable());
164 
165         // Allocate tensors
166         src.allocator()->allocate();
167         dst.allocator()->allocate();
168 
169         ARM_COMPUTE_ASSERT(!src.info()->is_resizable());
170         ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
171 
172         // Populate and validate memory manager
173         serv_cross.populate(num_pools);
174         serv_internal.populate(num_pools);
175         serv_cross.validate(validate_finalized);
176         serv_internal.validate(validate_finalized);
177 
178         // Extract lifetime manager meta-data information
179         internal_l0 = serv_internal.lifetime_mgr->info();
180         cross_l0    = serv_cross.lifetime_mgr->info();
181 
182         // Acquire memory manager, fill tensors and compute functions
183         serv_cross.mg.acquire();
184         arm_compute::test::library->fill_tensor_value(AccessorType(src), 12.f);
185         layer.run();
186         serv_cross.mg.release();
187 
188         // Clear manager
189         serv_cross.clear();
190         serv_internal.clear();
191         serv_cross.validate(validate_finalized);
192         serv_internal.validate(validate_finalized);
193 
194         // Level 1
195         // Update the tensor shapes
196         src.info()->set_tensor_shape(level_1);
197         dst.info()->set_tensor_shape(level_1);
198         src.info()->set_is_resizable(true);
199         dst.info()->set_is_resizable(true);
200 
201         serv_cross.mg.manage(&src);
202         serv_cross.mg.manage(&dst);
203 
204         // Re-configure the function
205         layer.configure(&src, &dst);
206 
207         // Allocate tensors
208         src.allocator()->allocate();
209         dst.allocator()->allocate();
210 
211         // Populate and validate memory manager
212         serv_cross.populate(num_pools);
213         serv_internal.populate(num_pools);
214         serv_cross.validate(validate_finalized);
215         serv_internal.validate(validate_finalized);
216 
217         // Extract lifetime manager meta-data information
218         internal_l1 = serv_internal.lifetime_mgr->info();
219         cross_l1    = serv_cross.lifetime_mgr->info();
220 
221         // Compute functions
222         serv_cross.mg.acquire();
223         arm_compute::test::library->fill_tensor_value(AccessorType(src), 12.f);
224         layer.run();
225         serv_cross.mg.release();
226 
227         // Clear manager
228         serv_cross.clear();
229         serv_internal.clear();
230         serv_cross.validate(validate_finalized);
231         serv_internal.validate(validate_finalized);
232     }
233 
234 public:
235     TensorShape                                                 input_l0{}, input_l1{};
236     typename MemoryManagementServiceType::LftMgrType::info_type internal_l0{}, internal_l1{};
237     typename MemoryManagementServiceType::LftMgrType::info_type cross_l0{}, cross_l1{};
238 };
239 
240 /** Simple test case to run a single function with different shapes twice.
241  *
242  * Runs a specified function twice, where the second time the size of the input/output is different
243  * Internal memory of the function and input/output are managed by different services
244  */
245 template <typename TensorType,
246           typename AccessorType,
247           typename MemoryManagementServiceType,
248           typename ComplexFunctionType>
249 class DynamicTensorType3ComplexFunction : public framework::Fixture
250 {
251     using T = float;
252 
253 public:
254     template <typename...>
setup(std::vector<TensorShape> input_shapes,TensorShape weights_shape,TensorShape bias_shape,std::vector<TensorShape> output_shapes,PadStrideInfo info)255     void setup(std::vector<TensorShape> input_shapes, TensorShape weights_shape, TensorShape bias_shape, std::vector<TensorShape> output_shapes, PadStrideInfo info)
256     {
257         num_iterations = input_shapes.size();
258         _data_type     = DataType::F32;
259         _data_layout   = DataLayout::NHWC;
260         _input_shapes  = input_shapes;
261         _output_shapes = output_shapes;
262         _weights_shape = weights_shape;
263         _bias_shape    = bias_shape;
264         _info          = info;
265 
266         // Create function
267         _f_target = std::make_unique<ComplexFunctionType>(_ms.mm);
268     }
269 
run_iteration(unsigned int idx)270     void run_iteration(unsigned int idx)
271     {
272         auto input_shape  = _input_shapes[idx];
273         auto output_shape = _output_shapes[idx];
274 
275         dst_ref    = run_reference(input_shape, _weights_shape, _bias_shape, output_shape, _info);
276         dst_target = run_target(input_shape, _weights_shape, _bias_shape, output_shape, _info, WeightsInfo());
277     }
278 
279 protected:
280     template <typename U>
fill(U && tensor,int i)281     void fill(U &&tensor, int i)
282     {
283         switch(tensor.data_type())
284         {
285             case DataType::F32:
286             {
287                 std::uniform_real_distribution<> distribution(-1.0f, 1.0f);
288                 library->fill(tensor, distribution, i);
289                 break;
290             }
291             default:
292                 library->fill_tensor_uniform(tensor, i);
293         }
294     }
295 
run_target(TensorShape input_shape,TensorShape weights_shape,TensorShape bias_shape,TensorShape output_shape,PadStrideInfo info,WeightsInfo weights_info)296     TensorType run_target(TensorShape input_shape, TensorShape weights_shape, TensorShape bias_shape, TensorShape output_shape,
297                           PadStrideInfo info, WeightsInfo weights_info)
298     {
299         if(_data_layout == DataLayout::NHWC)
300         {
301             permute(input_shape, PermutationVector(2U, 0U, 1U));
302             permute(weights_shape, PermutationVector(2U, 0U, 1U));
303             permute(output_shape, PermutationVector(2U, 0U, 1U));
304         }
305 
306         _weights_target = create_tensor<TensorType>(weights_shape, _data_type, 1, QuantizationInfo(), _data_layout);
307         _bias_target    = create_tensor<TensorType>(bias_shape, _data_type, 1);
308 
309         // Create tensors
310         TensorType src = create_tensor<TensorType>(input_shape, _data_type, 1, QuantizationInfo(), _data_layout);
311         TensorType dst = create_tensor<TensorType>(output_shape, _data_type, 1, QuantizationInfo(), _data_layout);
312 
313         // Create and configure function
314         _f_target->configure(&src, &_weights_target, &_bias_target, &dst, info, weights_info);
315 
316         ARM_COMPUTE_ASSERT(src.info()->is_resizable());
317         ARM_COMPUTE_ASSERT(dst.info()->is_resizable());
318 
319         // Allocate tensors
320         src.allocator()->allocate();
321         dst.allocator()->allocate();
322         _weights_target.allocator()->allocate();
323         _bias_target.allocator()->allocate();
324 
325         ARM_COMPUTE_ASSERT(!src.info()->is_resizable());
326         ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
327 
328         // Fill tensors
329         fill(AccessorType(src), 0);
330         fill(AccessorType(_weights_target), 1);
331         fill(AccessorType(_bias_target), 2);
332 
333         // Populate and validate memory manager
334         _ms.clear();
335         _ms.populate(1);
336         _ms.mg.acquire();
337 
338         // Compute NEConvolutionLayer function
339         _f_target->run();
340         _ms.mg.release();
341 
342         return dst;
343     }
344 
run_reference(TensorShape input_shape,TensorShape weights_shape,TensorShape bias_shape,TensorShape output_shape,PadStrideInfo info)345     SimpleTensor<T> run_reference(TensorShape input_shape, TensorShape weights_shape, TensorShape bias_shape, TensorShape output_shape, PadStrideInfo info)
346     {
347         // Create reference
348         SimpleTensor<T> src{ input_shape, _data_type, 1 };
349         SimpleTensor<T> weights{ weights_shape, _data_type, 1 };
350         SimpleTensor<T> bias{ bias_shape, _data_type, 1 };
351 
352         // Fill reference
353         fill(src, 0);
354         fill(weights, 1);
355         fill(bias, 2);
356 
357         return reference::convolution_layer<T>(src, weights, bias, output_shape, info);
358     }
359 
360 public:
361     unsigned int    num_iterations{ 0 };
362     SimpleTensor<T> dst_ref{};
363     TensorType      dst_target{};
364 
365 private:
366     DataType                             _data_type{ DataType::UNKNOWN };
367     DataLayout                           _data_layout{ DataLayout::UNKNOWN };
368     PadStrideInfo                        _info{};
369     std::vector<TensorShape>             _input_shapes{};
370     std::vector<TensorShape>             _output_shapes{};
371     TensorShape                          _weights_shape{};
372     TensorShape                          _bias_shape{};
373     MemoryManagementServiceType          _ms{};
374     TensorType                           _weights_target{};
375     TensorType                           _bias_target{};
376     std::unique_ptr<ComplexFunctionType> _f_target{};
377 };
378 
379 /** Fixture that create a pipeline of Convolutions and changes the inputs dynamically
380  *
381  * Runs a list of convolutions and then resizes the inputs and reruns.
382  * Updates the memory manager and allocated memory.
383  */
384 template <typename TensorType,
385           typename AccessorType,
386           typename MemoryManagementServiceType,
387           typename ComplexFunctionType>
388 class DynamicTensorType2PipelineFunction : public framework::Fixture
389 {
390     using T = float;
391 
392 public:
393     template <typename...>
setup(std::vector<TensorShape> input_shapes)394     void setup(std::vector<TensorShape> input_shapes)
395     {
396         _data_type    = DataType::F32;
397         _data_layout  = DataLayout::NHWC;
398         _input_shapes = input_shapes;
399 
400         run();
401     }
402 
403 protected:
404     template <typename U>
fill(U && tensor,int i)405     void fill(U &&tensor, int i)
406     {
407         switch(tensor.data_type())
408         {
409             case DataType::F32:
410             {
411                 std::uniform_real_distribution<float> distribution(-1.0f, 1.0f);
412                 library->fill(tensor, distribution, i);
413                 break;
414             }
415             default:
416                 library->fill_tensor_uniform(tensor, i);
417         }
418     }
419 
run()420     void run()
421     {
422         const unsigned int num_functions = 5;
423         const unsigned int num_tensors   = num_functions + 1;
424         const unsigned int num_resizes   = _input_shapes.size();
425 
426         for(unsigned int i = 0; i < num_functions; ++i)
427         {
428             _functions.emplace_back(std::make_unique<ComplexFunctionType>(_ms.mm));
429         }
430 
431         for(unsigned int i = 0; i < num_resizes; ++i)
432         {
433             TensorShape   input_shape   = _input_shapes[i];
434             TensorShape   weights_shape = TensorShape(3U, 3U, input_shape[2], input_shape[2]);
435             TensorShape   output_shape  = input_shape;
436             PadStrideInfo info(1U, 1U, 1U, 1U);
437 
438             if(_data_layout == DataLayout::NHWC)
439             {
440                 permute(input_shape, PermutationVector(2U, 0U, 1U));
441                 permute(weights_shape, PermutationVector(2U, 0U, 1U));
442                 permute(output_shape, PermutationVector(2U, 0U, 1U));
443             }
444 
445             std::vector<TensorType> tensors(num_tensors);
446             std::vector<TensorType> ws(num_functions);
447             std::vector<TensorType> bs(num_functions);
448 
449             auto tensor_info  = TensorInfo(input_shape, 1, _data_type);
450             auto weights_info = TensorInfo(weights_shape, 1, _data_type);
451             tensor_info.set_data_layout(_data_layout);
452             weights_info.set_data_layout(_data_layout);
453 
454             tensors[0].allocator()->init(tensor_info);
455             for(unsigned int f = 0; f < num_functions; ++f)
456             {
457                 tensors[f + 1].allocator()->init(tensor_info);
458                 ws[f].allocator()->init(weights_info);
459 
460                 _functions[f]->configure(&tensors[f], &ws[f], nullptr, &tensors[f + 1], info);
461 
462                 // Allocate tensors
463                 tensors[f].allocator()->allocate();
464                 ws[f].allocator()->allocate();
465             }
466             tensors[num_functions].allocator()->allocate();
467 
468             // Populate and validate memory manager
469             _ms.clear();
470             _ms.populate(1);
471             _ms.mg.acquire();
472 
473             // Run pipeline
474             for(unsigned int f = 0; f < num_functions; ++f)
475             {
476                 _functions[f]->run();
477             }
478 
479             // Release memory group
480             _ms.mg.release();
481         }
482     }
483 
484 private:
485     DataType                                          _data_type{ DataType::UNKNOWN };
486     DataLayout                                        _data_layout{ DataLayout::UNKNOWN };
487     std::vector<TensorShape>                          _input_shapes{};
488     MemoryManagementServiceType                       _ms{};
489     std::vector<std::unique_ptr<ComplexFunctionType>> _functions{};
490 };
491 } // namespace validation
492 } // namespace test
493 } // namespace arm_compute
494 #endif /* ARM_COMPUTE_TEST_UNIT_DYNAMIC_TENSOR */
495