blob: 95dcd701044e2ea0c2ba8ce3eaf1e41953d92892 [file] [log] [blame]
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +01001/*
Gian Marco Iodice10e88a72021-11-29 12:49:19 +00002 * Copyright (c) 2017-2022 Arm Limited.
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +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_GEMM_FIXTURE
25#define ARM_COMPUTE_TEST_GEMM_FIXTURE
26
Gian Marco Iodice7026b302019-06-26 17:18:11 +010027#include "arm_compute/core/KernelDescriptors.h"
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010028#include "arm_compute/core/TensorShape.h"
29#include "arm_compute/core/Types.h"
SiCongLi1af54162021-10-06 15:25:57 +010030#include "arm_compute/core/experimental/IPostOp.h"
SiCongLi31778612021-11-12 17:33:45 +000031#include "src/core/experimental/PostOpUtils.h"
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010032#include "tests/AssetsLibrary.h"
33#include "tests/Globals.h"
34#include "tests/IAccessor.h"
Moritz Pflanzera09de0c2017-09-01 20:41:12 +010035#include "tests/framework/Asserts.h"
36#include "tests/framework/Fixture.h"
Moritz Pflanzera09de0c2017-09-01 20:41:12 +010037#include "tests/validation/Helpers.h"
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +010038#include "tests/validation/reference/ActivationLayer.h"
SiCongLi1af54162021-10-06 15:25:57 +010039#include "tests/validation/reference/ElementwiseOperations.h"
Georgios Pinitas5a7e7762017-12-01 16:27:29 +000040#include "tests/validation/reference/GEMM.h"
SiCongLi1af54162021-10-06 15:25:57 +010041#include "tests/validation/reference/PostOps.h"
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010042
43#include <random>
44
45namespace arm_compute
46{
47namespace test
48{
49namespace validation
50{
Gian Marco Iodicef3622be2019-07-29 14:27:16 +010051template <typename TensorType, typename AccessorType, typename FunctionType, typename T, bool disable_c = false, bool reinterpret_input_as_3d = false, bool reinterpret_output_as_3d = false>
Gian Marco Iodice68a3f562018-07-26 11:44:03 +010052class GEMMValidationFixture : public framework::Fixture
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010053{
54public:
55 template <typename...>
Pablo Tello0e37b5c2018-10-30 11:18:37 +000056 void setup(TensorShape shape_a, TensorShape shape_b, TensorShape shape_c, TensorShape output_shape, float alpha, float beta, bool pretranspose, DataType data_type)
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010057 {
Michalis Spyrou6bff1952019-10-02 17:22:11 +010058 ARM_COMPUTE_UNUSED(pretranspose);
59 _target = compute_target(shape_a, shape_b, shape_c, output_shape, alpha, beta, data_type);
60 _reference = compute_reference(shape_a, shape_b, output_shape, alpha, beta, data_type);
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010061 }
62
63protected:
64 template <typename U>
Pablo Tello0e37b5c2018-10-30 11:18:37 +000065 void fill(U &&tensor, int i, float lo = -1.f, float hi = 1.f)
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010066 {
67 switch(tensor.data_type())
68 {
69 case DataType::F16:
Giorgio Arena6aeb2172020-12-15 15:45:43 +000070 {
Giorgio Arenaa8e2aeb2021-01-06 11:34:57 +000071 arm_compute::utils::uniform_real_distribution_16bit<half> distribution{ float(lo), float(hi) };
Giorgio Arena6aeb2172020-12-15 15:45:43 +000072 library->fill(tensor, distribution, i);
73 break;
74 }
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010075 case DataType::F32:
76 {
Giorgio Arena6aeb2172020-12-15 15:45:43 +000077 std::uniform_real_distribution<float> distribution(lo, hi);
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010078 library->fill(tensor, distribution, i);
79 break;
80 }
81 default:
82 library->fill_tensor_uniform(tensor, i);
83 }
84 }
85
86 TensorType compute_target(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &shape_c, const TensorShape &output_shape, float alpha, float beta,
Michalis Spyrou6bff1952019-10-02 17:22:11 +010087 DataType data_type)
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010088 {
89 // Create tensors
Vidhya Sudhan Loganathan014333d2018-07-02 09:13:49 +010090 TensorType a = create_tensor<TensorType>(shape_a, data_type, 1);
91 TensorType b = create_tensor<TensorType>(shape_b, data_type, 1);
92 TensorType c = create_tensor<TensorType>(shape_c, data_type, 1);
93 TensorType dst = create_tensor<TensorType>(output_shape, data_type, 1);
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +010094
95 // Create and configure function
96 FunctionType gemm;
Isabella Gottardi8e74f442018-03-01 16:42:00 +000097 // The GEMMinfo includes the values of the depth in case of reinterpreted 3d output.
Gian Marco Iodice3139f032018-11-05 14:26:32 +000098 // If the output shape has the same number of dimensions of the input the method called is a 2D matrix multiplication (depth_output_reinterpreted_as_3D = 0),
Isabella Gottardi8e74f442018-03-01 16:42:00 +000099 // in the other case we have to use the reinterpreted version of GEMM (depth_output_reinterpreted_as_3D = depth of the 3D output).
Gian Marco Iodicef3622be2019-07-29 14:27:16 +0100100 gemm.configure(&a,
101 &b,
102 (disable_c) ? nullptr : &c,
103 &dst,
104 alpha, beta,
Georgios Pinitas4ee8b152021-07-16 16:16:43 +0100105 GEMMInfo(false, false, false, (reinterpret_output_as_3d ? output_shape[2] : 0), reinterpret_input_as_3d, false, GEMMLowpOutputStageInfo(), false, false, (reinterpret_input_as_3d
Gian Marco Iodicef3622be2019-07-29 14:27:16 +0100106 || reinterpret_output_as_3d)));
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100107 ARM_COMPUTE_ASSERT(a.info()->is_resizable());
108 ARM_COMPUTE_ASSERT(b.info()->is_resizable());
109 ARM_COMPUTE_ASSERT(c.info()->is_resizable());
110 ARM_COMPUTE_ASSERT(dst.info()->is_resizable());
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +0100111
Giorgio Arena63825e82021-03-25 14:54:50 +0000112 add_padding_x({ &a, &b, &c, &dst });
113
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +0100114 // Allocate tensors
115 a.allocator()->allocate();
116 b.allocator()->allocate();
117 c.allocator()->allocate();
118 dst.allocator()->allocate();
119
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100120 ARM_COMPUTE_ASSERT(!a.info()->is_resizable());
121 ARM_COMPUTE_ASSERT(!b.info()->is_resizable());
122 ARM_COMPUTE_ASSERT(!c.info()->is_resizable());
123 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +0100124
125 // Fill tensors
126 fill(AccessorType(a), 0);
127 fill(AccessorType(b), 1);
Pablo Tello0e37b5c2018-10-30 11:18:37 +0000128 if(!disable_c)
129 {
130 fill(AccessorType(c), 2);
131 }
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +0100132
133 // Compute GEMM function
134 gemm.run();
135
136 return dst;
137 }
138
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100139 SimpleTensor<T> compute_reference(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &output_shape, float alpha, float beta,
Vidhya Sudhan Loganathan014333d2018-07-02 09:13:49 +0100140 DataType data_type)
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +0100141 {
Gian Marco Iodice68a3f562018-07-26 11:44:03 +0100142 TensorShape shape_a_to_use = shape_a;
Gian Marco Iodicef3622be2019-07-29 14:27:16 +0100143
Gian Marco Iodice68a3f562018-07-26 11:44:03 +0100144 if(reinterpret_input_as_3d)
145 {
146 // Collapse the second and third dimension if the input is 3D
147 shape_a_to_use.collapse(2U, 1U);
148 }
149
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +0100150 // Create reference
Gian Marco Iodice68a3f562018-07-26 11:44:03 +0100151 SimpleTensor<T> a{ shape_a_to_use, data_type, 1 };
Vidhya Sudhan Loganathan014333d2018-07-02 09:13:49 +0100152 SimpleTensor<T> b{ shape_b, data_type, 1 };
Gian Marco Iodicef3622be2019-07-29 14:27:16 +0100153 SimpleTensor<T> c{ output_shape, data_type, 1 };
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +0100154
155 // Fill reference
156 fill(a, 0);
157 fill(b, 1);
Gian Marco Iodicef3622be2019-07-29 14:27:16 +0100158 fill(c, 2);
159
160 if(reinterpret_input_as_3d || reinterpret_output_as_3d)
Pablo Tello0e37b5c2018-10-30 11:18:37 +0000161 {
Gian Marco Iodicef3622be2019-07-29 14:27:16 +0100162 const int n = shape_b[0];
163 const int m = reinterpret_output_as_3d ? output_shape[1] * output_shape[2] : output_shape[1];
164 const int batch_size = reinterpret_output_as_3d ? output_shape[3] : output_shape[2];
165
166 // In case of broadcast, we need simply copy the first into the following "M" ones
167 for(int i = 1; i < m * batch_size; i++)
168 {
169 memcpy(c.data() + i * n, c.data(), n * sizeof(T));
170 }
Pablo Tello0e37b5c2018-10-30 11:18:37 +0000171 }
Gian Marco Iodicef3622be2019-07-29 14:27:16 +0100172
173 // Setting beta to 0 will effectively disable C for the
174 // computation of the reference: alpha * A * B + 0 * C
175 return reference::gemm<T>(a, b, c, alpha, disable_c ? 0.f : beta);
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +0100176 }
177
178 TensorType _target{};
179 SimpleTensor<T> _reference{};
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +0100180};
181
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100182template <typename TensorType, typename AccessorType, typename T, typename GEMMOperatorType>
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100183class GEMMMatrixMultiplyValidationFixture : public framework::Fixture
184{
185public:
186 template <typename...>
187 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, float alpha, float beta, bool broadcast_bias, bool fp16_mixed_precision, const ActivationLayerInfo &act_info,
188 DataType data_type, GPUTarget gpu_arch)
189 {
190 // Set the tensor shapes for LHS and RHS matrices
191 const TensorShape lhs_shape(k, m, batch_size);
192 const TensorShape rhs_shape(n, k, batch_size);
193 const TensorShape bias_shape(n,
194 broadcast_bias ? 1 : m,
195 broadcast_bias ? 1 : batch_size);
196
197 _target = compute_target(lhs_shape, rhs_shape, bias_shape, data_type, alpha, beta, broadcast_bias, fp16_mixed_precision, act_info, gpu_arch);
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100198 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, broadcast_bias, act_info);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100199 }
200
201protected:
202 template <typename U>
203 void fill(U &&tensor, int i)
204 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000205 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +0000206 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000207
208 DistributionType distribution{ T(-1.0f), T(1.0f) };
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100209 library->fill(tensor, distribution, i);
210
211 // Fill border with infinity in order to check the presence of NaN values (i.e. inf * 0)
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000212 DistributionType distribution_inf{ T(std::numeric_limits<float>::infinity()), T(std::numeric_limits<float>::infinity()) };
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100213 library->fill_borders_with_garbage(tensor, distribution_inf, i);
214 }
215
216 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, DataType data_type, float alpha, float beta, bool broadcast_bias,
217 bool fp16_mixed_precision, const ActivationLayerInfo &act_info, GPUTarget gpu_arch)
218 {
219 // Create tensors
220 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
221 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
222 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
223 TensorType dst;
224
225 const unsigned int m = lhs_shape[1];
226 const unsigned int n = rhs_shape[0];
227 const unsigned int k = lhs_shape[0];
228 GEMMReshapeInfo reshape_info(m, n, k, 1, 1, 0, false, broadcast_bias);
229
230 // The output tensor will be auto-initialized within the function
231
232 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100233 GEMMOperatorType gemm;
234 gemm.configure(gpu_arch, lhs.info(), rhs.info(), bias.info(), dst.info(), alpha, beta, false, reshape_info, fp16_mixed_precision, act_info);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100235
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100236 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
237 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
238 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100239
Giorgio Arena63825e82021-03-25 14:54:50 +0000240 add_padding_x({ &lhs, &rhs, &bias, &dst });
241
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100242 // Allocate tensors
243 lhs.allocator()->allocate();
244 rhs.allocator()->allocate();
245 bias.allocator()->allocate();
246 dst.allocator()->allocate();
247
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100248 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
249 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
250 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
251 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100252
253 // Fill tensors
254 fill(AccessorType(lhs), 0);
255 fill(AccessorType(rhs), 1);
256 fill(AccessorType(bias), 2);
257
258 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100259 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs },
260 { ACL_SRC_1, &rhs },
261 { ACL_SRC_2, &bias },
262 { ACL_DST, &dst }
263 });
264 gemm.run(gemm_pack);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100265
266 return dst;
267 }
268
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100269 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, bool broadcast_bias,
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100270 const ActivationLayerInfo &act_info)
271 {
272 TensorShape dst_shape = lhs_shape;
273 dst_shape[0] = rhs_shape[0];
274 dst_shape[1] = lhs_shape[1];
275
276 // Create reference
277 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
278 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
279 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
280
281 const int n = rhs_shape[0];
282 const int m = lhs_shape[1];
283 const int batch_size = lhs_shape[2];
284
285 // Fill reference
286 fill(lhs, 0);
287 fill(rhs, 1);
288 fill(bias, 2);
289
290 if(broadcast_bias)
291 {
292 // In case of broadcast, we need simply copy the first into the following "M" ones
293 for(int i = 1; i < m * batch_size; i++)
294 {
295 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
296 }
297 }
298
299 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
300 }
301
302 TensorType _target{};
303 SimpleTensor<T> _reference{};
304};
305
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100306template <typename TensorType, typename AccessorType, typename T, typename GEMMOperatorType>
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100307class GEMMMatrixMultiply3DValidationFixture : public framework::Fixture
308{
309public:
310 template <typename...>
311 void setup(unsigned int m_w, unsigned int m_h, unsigned int n, unsigned int k, unsigned int batch_size, float alpha, float beta, bool broadcast_bias, bool fp16_mixed_precision,
312 const ActivationLayerInfo &act_info, DataType data_type, GPUTarget gpu_arch)
313 {
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100314 ARM_COMPUTE_UNUSED(broadcast_bias);
315
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100316 // In case of GEMM3D, m is the product between m_w and m_h
317 const unsigned int m = m_w * m_h;
318
319 // Set the tensor shapes for LHS and RHS matrices
320 const TensorShape lhs_shape(k, m, batch_size);
321 const TensorShape rhs_shape(n, k, batch_size);
322 const TensorShape bias_shape(n, 1, 1);
323
324 _target = compute_target(lhs_shape, rhs_shape, bias_shape, data_type, alpha, beta, m_h, fp16_mixed_precision, act_info, gpu_arch);
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100325 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, m_h, act_info);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100326 }
327
328protected:
329 template <typename U>
330 void fill(U &&tensor, int i)
331 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000332 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +0000333 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000334
335 DistributionType distribution{ T(-1.0f), T(1.0f) };
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100336 library->fill(tensor, distribution, i);
337 }
338
339 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, DataType data_type, float alpha, float beta, unsigned int m_h,
340 bool fp16_mixed_precision, const ActivationLayerInfo &act_info, GPUTarget gpu_arch)
341 {
342 // Create tensors
343 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
344 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
345 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
346 TensorType dst;
347
348 const unsigned int m = lhs_shape[1];
349 const unsigned int n = rhs_shape[0];
350 const unsigned int k = lhs_shape[0];
351 GEMMReshapeInfo reshape_info(m, n, k, 1, 1, m_h, false, true);
352
353 // The output tensor will be auto-initialized within the function
354
355 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100356 GEMMOperatorType gemm;
357 gemm.configure(gpu_arch, lhs.info(), rhs.info(), bias.info(), dst.info(), alpha, beta, false, reshape_info, fp16_mixed_precision, act_info);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100358
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100359 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
360 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
361 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100362
Giorgio Arena63825e82021-03-25 14:54:50 +0000363 add_padding_x({ &lhs, &rhs, &bias, &dst });
364
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100365 // Allocate tensors
366 lhs.allocator()->allocate();
367 rhs.allocator()->allocate();
368 bias.allocator()->allocate();
369 dst.allocator()->allocate();
370
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100371 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
372 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
373 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
374 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100375
376 // Fill tensors
377 fill(AccessorType(lhs), 0);
378 fill(AccessorType(rhs), 1);
379 fill(AccessorType(bias), 2);
380
381 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100382 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs },
383 { ACL_SRC_1, &rhs },
384 { ACL_SRC_2, &bias },
385 { ACL_DST, &dst }
386 });
387 gemm.run(gemm_pack);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100388
389 return dst;
390 }
391
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100392 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, unsigned int m_h,
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100393 const ActivationLayerInfo &act_info)
394 {
395 TensorShape dst_shape = lhs_shape;
396 dst_shape.set(0, rhs_shape[0]);
397 dst_shape.set(1, lhs_shape[1] / m_h);
398 dst_shape.set(2, m_h);
399 dst_shape.set(3, lhs_shape[2]);
400
401 // Create reference
402 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
403 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
404 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
405
406 const int n = rhs_shape[0];
407 const int m = lhs_shape[1];
408 const int batch_size = lhs_shape[2];
409
410 // Fill reference
411 fill(lhs, 0);
412 fill(rhs, 1);
413 fill(bias, 2);
414
415 // In case of broadcast, we need simply copy the first into the following "M" ones
416 for(int i = 1; i < m * batch_size; i++)
417 {
418 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
419 }
420
421 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
422 }
423
424 TensorType _target{};
425 SimpleTensor<T> _reference{};
426};
427
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100428template <typename TensorType, typename AccessorType, typename T, typename ReshapeLHSOperatorType, typename ReshapeRHSOperatorType, typename GEMMOperatorType>
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100429class GEMMMatrixMultiplyInterleavedTransposedValidationFixture : public framework::Fixture
430{
431public:
432 template <typename...>
433 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, float alpha, float beta, unsigned int v0, unsigned int h0, bool broadcast_bias, bool fp16_mixed_precision,
434 const ActivationLayerInfo &act_info, DataType data_type, GPUTarget gpu_arch)
435 {
436 GEMMLHSMatrixInfo lhs_info;
437 lhs_info.m0 = 4;
438 lhs_info.k0 = 4;
439 lhs_info.v0 = v0;
440 lhs_info.interleave = true;
441 lhs_info.transpose = true;
442
443 GEMMRHSMatrixInfo rhs_info;
444 rhs_info.n0 = 16 / sizeof(T);
445 rhs_info.k0 = 1;
446 rhs_info.h0 = h0;
447 rhs_info.interleave = false;
448 rhs_info.transpose = false;
449
450 // Set the tensor shapes for LHS and RHS matrices
451 const TensorShape lhs_shape(k, m, batch_size);
452 const TensorShape rhs_shape(n, k, batch_size);
453 const TensorShape bias_shape(n,
454 broadcast_bias ? 1 : m,
455 broadcast_bias ? 1 : batch_size);
456
457 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, broadcast_bias, fp16_mixed_precision, act_info, gpu_arch);
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100458 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, broadcast_bias, act_info);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100459 }
460
461protected:
462 template <typename U>
463 void fill(U &&tensor, int i)
464 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000465 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +0000466 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000467
468 DistributionType distribution{ T(-1.0f), T(1.0f) };
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100469 library->fill(tensor, distribution, i);
470
471 // Fill border with infinity in order to check the presence of NaN values (i.e. inf * 0)
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000472 DistributionType distribution_inf{ T(std::numeric_limits<float>::infinity()), T(std::numeric_limits<float>::infinity()) };
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100473 library->fill_borders_with_garbage(tensor, distribution_inf, i);
474 }
475
476 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
477 DataType data_type, float alpha, float beta, bool broadcast_bias, bool fp16_mixed_precision, const ActivationLayerInfo &act_info, GPUTarget gpu_arch)
478 {
479 // Create tensors
480 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
481 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
482 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
483 TensorType lhs_reshaped;
484 TensorType rhs_reshaped;
485 TensorType dst;
486
487 const unsigned int m = lhs_shape[1];
488 const unsigned int n = rhs_shape[0];
489 const unsigned int k = lhs_shape[0];
490 GEMMReshapeInfo reshape_info(m, n, k, rhs_info.h0, lhs_info.v0, 0, false, broadcast_bias);
491
492 // The output tensor will be auto-initialized within the function
493
494 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100495 ReshapeLHSOperatorType reshape_lhs;
496 ReshapeRHSOperatorType reshape_rhs;
497 GEMMOperatorType gemm;
498 reshape_lhs.configure(lhs.info(), lhs_reshaped.info(), lhs_info);
499 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
500 gemm.configure(gpu_arch, lhs_reshaped.info(), rhs_reshaped.info(), bias.info(), dst.info(), alpha, beta, true, reshape_info, fp16_mixed_precision, act_info);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100501
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100502 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
503 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
504 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100505
Georgios Pinitas3dca91b2021-04-13 13:35:58 +0100506 // We do not pad when using image as it needs to comply to strict pitch alignment restrictions
Giorgio Arena63825e82021-03-25 14:54:50 +0000507 if(!rhs_info.export_to_cl_image)
508 {
509 add_padding_x({ &lhs, &rhs, &lhs_reshaped, &rhs_reshaped, &bias, &dst });
510 }
511
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100512 // Allocate tensors
513 lhs.allocator()->allocate();
514 rhs.allocator()->allocate();
515 lhs_reshaped.allocator()->allocate();
516 rhs_reshaped.allocator()->allocate();
517 bias.allocator()->allocate();
518 dst.allocator()->allocate();
519
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100520 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
521 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
522 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
523 ARM_COMPUTE_ASSERT(!lhs_reshaped.info()->is_resizable());
524 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
525 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100526
527 // Fill tensors
528 fill(AccessorType(lhs), 0);
529 fill(AccessorType(rhs), 1);
530 fill(AccessorType(bias), 2);
531
532 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100533 ITensorPack reshape_lhs_pack = { { ACL_SRC, &lhs }, { ACL_DST, &lhs_reshaped } };
534 reshape_lhs.run(reshape_lhs_pack);
535 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
536 reshape_rhs.run(reshape_rhs_pack);
537 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs_reshaped },
538 { ACL_SRC_1, &rhs_reshaped },
539 { ACL_SRC_2, &bias },
540 { ACL_DST, &dst }
541 });
542 gemm.run(gemm_pack);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100543
544 return dst;
545 }
546
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100547 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, bool broadcast_bias,
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100548 const ActivationLayerInfo &act_info)
549 {
550 TensorShape dst_shape = lhs_shape;
551 dst_shape[0] = rhs_shape[0];
552 dst_shape[1] = lhs_shape[1];
553
554 // Create reference
555 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
556 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
557 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
558
559 const int n = rhs_shape[0];
560 const int m = lhs_shape[1];
561 const int batch_size = lhs_shape[2];
562
563 // Fill reference
564 fill(lhs, 0);
565 fill(rhs, 1);
566 fill(bias, 2);
567
568 if(broadcast_bias)
569 {
570 // In case of broadcast, we need simply copy the first into the following "M" ones
571 for(int i = 1; i < m * batch_size; i++)
572 {
573 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
574 }
575 }
576
577 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
578 }
579
580 TensorType _target{};
581 SimpleTensor<T> _reference{};
582};
583
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100584template <typename TensorType, typename AccessorType, typename T, typename ReshapeLHSOperatorType, typename ReshapeRHSOperatorType, typename GEMMOperatorType>
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100585class GEMMMatrixMultiplyInterleavedTransposed3DValidationFixture : public framework::Fixture
586{
587public:
588 template <typename...>
589 void setup(unsigned int m_w, unsigned int m_h, unsigned int n, unsigned int k, unsigned int batch_size, float alpha, float beta, unsigned int v0, unsigned int h0, bool broadcast_bias,
590 bool fp16_mixed_precision, const ActivationLayerInfo &act_info, DataType data_type, GPUTarget gpu_arch)
591 {
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100592 ARM_COMPUTE_UNUSED(broadcast_bias);
593
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100594 GEMMLHSMatrixInfo lhs_info;
595 lhs_info.m0 = 4;
596 lhs_info.k0 = 4;
597 lhs_info.v0 = v0;
598 lhs_info.interleave = true;
599 lhs_info.transpose = true;
600
601 GEMMRHSMatrixInfo rhs_info;
602 rhs_info.n0 = 16 / sizeof(T);
603 rhs_info.k0 = 1;
604 rhs_info.h0 = h0;
605 rhs_info.interleave = false;
606 rhs_info.transpose = false;
607
608 // In case of GEMM3D, m is the product between m_w and m_h
609 const unsigned int m = m_w * m_h;
610
611 // Set the tensor shapes for LHS and RHS matrices
612 const TensorShape lhs_shape(k, m, batch_size);
613 const TensorShape rhs_shape(n, k, batch_size);
614 const TensorShape bias_shape(n, 1, 1);
615
616 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, m_h, fp16_mixed_precision, act_info, gpu_arch);
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100617 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, m_h, act_info);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100618 }
619
620protected:
621 template <typename U>
622 void fill(U &&tensor, int i)
623 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000624 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +0000625 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000626
627 DistributionType distribution{ T(-1.0f), T(1.0f) };
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100628 library->fill(tensor, distribution, i);
629 }
630
631 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
632 DataType data_type, float alpha, float beta, unsigned int m_h, bool fp16_mixed_precision, const ActivationLayerInfo &act_info, GPUTarget gpu_arch)
633 {
634 // Create tensors
635 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
636 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
637 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
638 TensorType lhs_reshaped;
639 TensorType rhs_reshaped;
640 TensorType dst;
641
642 const unsigned int m = lhs_shape[1];
643 const unsigned int n = rhs_shape[0];
644 const unsigned int k = lhs_shape[0];
645 GEMMReshapeInfo reshape_info(m, n, k, rhs_info.h0, lhs_info.v0, m_h, false, true);
646
647 // The output tensor will be auto-initialized within the function
648
649 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100650 ReshapeLHSOperatorType reshape_lhs;
651 ReshapeRHSOperatorType reshape_rhs;
652 GEMMOperatorType gemm;
653 reshape_lhs.configure(lhs.info(), lhs_reshaped.info(), lhs_info);
654 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
655 gemm.configure(gpu_arch, lhs_reshaped.info(), rhs_reshaped.info(), bias.info(), dst.info(), alpha, beta, true, reshape_info, fp16_mixed_precision, act_info);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100656
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100657 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
658 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
659 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100660
Georgios Pinitas3dca91b2021-04-13 13:35:58 +0100661 // We do not pad when using image as it needs to comply to strict pitch alignment restrictions
Giorgio Arena63825e82021-03-25 14:54:50 +0000662 if(!rhs_info.export_to_cl_image)
663 {
664 add_padding_x({ &lhs, &rhs, &lhs_reshaped, &rhs_reshaped, &bias, &dst });
665 }
666
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100667 // Allocate tensors
668 lhs.allocator()->allocate();
669 rhs.allocator()->allocate();
670 lhs_reshaped.allocator()->allocate();
671 rhs_reshaped.allocator()->allocate();
672 bias.allocator()->allocate();
673 dst.allocator()->allocate();
674
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100675 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
676 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
677 ARM_COMPUTE_ASSERT(!lhs_reshaped.info()->is_resizable());
678 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
679 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
680 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100681
682 // Fill tensors
683 fill(AccessorType(lhs), 0);
684 fill(AccessorType(rhs), 1);
685 fill(AccessorType(bias), 2);
686
687 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100688 ITensorPack reshape_lhs_pack = { { ACL_SRC, &lhs }, { ACL_DST, &lhs_reshaped } };
689 reshape_lhs.run(reshape_lhs_pack);
690 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
691 reshape_rhs.run(reshape_rhs_pack);
692 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs_reshaped },
693 { ACL_SRC_1, &rhs_reshaped },
694 { ACL_SRC_2, &bias },
695 { ACL_DST, &dst }
696 });
697 gemm.run(gemm_pack);
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100698
699 return dst;
700 }
701
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100702 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, unsigned int m_h,
Gian Marco Iodiced1f54762019-07-19 09:54:47 +0100703 const ActivationLayerInfo &act_info)
704 {
705 TensorShape dst_shape = lhs_shape;
706 dst_shape.set(0, rhs_shape[0]);
707 dst_shape.set(1, lhs_shape[1] / m_h);
708 dst_shape.set(2, m_h);
709 dst_shape.set(3, lhs_shape[2]);
710
711 // Create reference
712 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
713 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
714 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
715
716 const int n = rhs_shape[0];
717 const int m = lhs_shape[1];
718 const int batch_size = lhs_shape[2];
719
720 // Fill reference
721 fill(lhs, 0);
722 fill(rhs, 1);
723 fill(bias, 2);
724
725 // In case of broadcast, we need simply copy the first into the following "M" ones
726 for(int i = 1; i < m * batch_size; i++)
727 {
728 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
729 }
730
731 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
732 }
733
734 TensorType _target{};
735 SimpleTensor<T> _reference{};
736};
737
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100738template <typename TensorType, typename AccessorType, typename T, typename ReshapeLHSOperatorType, typename ReshapeRHSOperatorType, typename GEMMOperatorType, bool fp_mixed_precision = false>
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000739class GEMMMatrixMultiplyReshapedValidationFixture : public framework::Fixture
740{
741public:
742 template <typename...>
743 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, unsigned int v0, unsigned int h0, bool interleave_lhs,
Gian Marco Iodicee3a849a2020-06-10 17:59:30 +0100744 bool interleave_rhs, bool export_to_cl_image, DataType data_type, float alpha, float beta, bool broadcast_bias, bool lhs_transpose, const ActivationLayerInfo &act_info)
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000745 {
746 GEMMLHSMatrixInfo lhs_info;
747 lhs_info.m0 = m0;
748 lhs_info.k0 = k0;
749 lhs_info.v0 = v0;
750 lhs_info.interleave = interleave_lhs;
Giorgio Arenaae99b6e2019-08-01 14:22:12 +0100751 lhs_info.transpose = lhs_transpose;
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000752
753 GEMMRHSMatrixInfo rhs_info;
Gian Marco Iodicee3a849a2020-06-10 17:59:30 +0100754 rhs_info.n0 = n0;
755 rhs_info.k0 = k0;
756 rhs_info.h0 = h0;
757 rhs_info.interleave = interleave_rhs;
758 rhs_info.transpose = !lhs_transpose;
759 rhs_info.export_to_cl_image = export_to_cl_image;
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000760
761 // Set the tensor shapes for LHS and RHS matrices
762 const TensorShape lhs_shape(k, m, batch_size);
763 const TensorShape rhs_shape(n, k, batch_size);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +0100764 const TensorShape bias_shape(n,
765 broadcast_bias ? 1 : m,
766 broadcast_bias ? 1 : batch_size);
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000767
Sheri Zhangcc3e53c2020-11-16 21:17:28 +0000768 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, broadcast_bias, act_info);
769 if(validate_result)
770 {
771 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, broadcast_bias, act_info);
772 }
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000773 }
774
775protected:
776 template <typename U>
777 void fill(U &&tensor, int i)
778 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000779 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +0000780 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000781
782 DistributionType distribution{ T(-1.0f), T(1.0f) };
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000783 library->fill(tensor, distribution, i);
Gian Marco Iodiceb87b95e2019-01-21 17:14:31 +0000784
785 // Fill border with infinity in order to check the presence of NaN values (i.e. inf * 0)
Giorgio Arena4bdd1772020-12-17 16:47:07 +0000786 DistributionType distribution_inf{ T(std::numeric_limits<float>::infinity()), T(std::numeric_limits<float>::infinity()) };
Gian Marco Iodiceb87b95e2019-01-21 17:14:31 +0000787 library->fill_borders_with_garbage(tensor, distribution_inf, i);
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000788 }
789
Gian Marco Iodicee16c8902019-06-14 16:11:10 +0100790 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +0100791 DataType data_type, float alpha, float beta, bool broadcast_bias, const ActivationLayerInfo &act_info)
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000792 {
793 // Create tensors
Gian Marco Iodicee16c8902019-06-14 16:11:10 +0100794 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
795 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
796 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000797 TensorType lhs_reshaped;
798 TensorType rhs_reshaped;
799 TensorType dst;
800
801 const unsigned int M = lhs_shape[1];
802 const unsigned int N = rhs_shape[0];
803 const unsigned int K = lhs_shape[0];
Gian Marco Iodice7026b302019-06-26 17:18:11 +0100804 GEMMKernelInfo kernel_info;
805 kernel_info.m = M;
806 kernel_info.n = N;
807 kernel_info.k = K;
808 kernel_info.depth_output_gemm3d = 0;
809 kernel_info.reinterpret_input_as_3d = false;
810 kernel_info.broadcast_bias = broadcast_bias;
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +0100811 kernel_info.activation_info = act_info;
Gian Marco Iodice0c17aa22019-09-27 09:23:15 +0100812 kernel_info.fp_mixed_precision = fp_mixed_precision;
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000813
814 // The output tensor will be auto-initialized within the function
815
816 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100817 ReshapeLHSOperatorType reshape_lhs;
818 ReshapeRHSOperatorType reshape_rhs;
819 GEMMOperatorType gemm;
Sheri Zhangcc3e53c2020-11-16 21:17:28 +0000820
821 validate_result = bool(reshape_rhs.validate(rhs.info(), rhs_reshaped.info(), rhs_info));
822 validate_result = validate_result || !rhs_info.export_to_cl_image;
823 if(!validate_result)
824 {
825 return nullptr;
826 }
827
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100828 reshape_lhs.configure(lhs.info(), lhs_reshaped.info(), lhs_info);
829 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
830 gemm.configure(lhs_reshaped.info(), rhs_reshaped.info(), bias.info(), dst.info(), alpha, beta, lhs_info, rhs_info, kernel_info);
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000831
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100832 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
833 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
834 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000835
Georgios Pinitas3dca91b2021-04-13 13:35:58 +0100836 // We do not pad when using image as it needs to comply to strict pitch alignment restrictions
Giorgio Arena63825e82021-03-25 14:54:50 +0000837 if(!rhs_info.export_to_cl_image)
838 {
839 add_padding_x({ &lhs, &rhs, &lhs_reshaped, &rhs_reshaped, &bias, &dst });
840 }
841
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000842 // Allocate tensors
843 lhs.allocator()->allocate();
844 rhs.allocator()->allocate();
845 lhs_reshaped.allocator()->allocate();
846 rhs_reshaped.allocator()->allocate();
Gian Marco Iodicee16c8902019-06-14 16:11:10 +0100847 bias.allocator()->allocate();
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000848 dst.allocator()->allocate();
849
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100850 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
851 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
852 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
853 ARM_COMPUTE_ASSERT(!lhs_reshaped.info()->is_resizable());
854 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
855 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000856
857 // Fill tensors
858 fill(AccessorType(lhs), 0);
859 fill(AccessorType(rhs), 1);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +0100860 fill(AccessorType(bias), 2);
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000861
862 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +0100863 ITensorPack reshape_lhs_pack = { { ACL_SRC, &lhs }, { ACL_DST, &lhs_reshaped } };
864 reshape_lhs.run(reshape_lhs_pack);
865 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
866 reshape_rhs.run(reshape_rhs_pack);
867 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs_reshaped },
868 { ACL_SRC_1, &rhs_reshaped },
869 { ACL_SRC_2, &bias },
870 { ACL_DST, &dst }
871 });
872 gemm.run(gemm_pack);
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000873
874 return dst;
875 }
876
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100877 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, bool broadcast_bias,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +0100878 const ActivationLayerInfo &act_info)
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000879 {
880 TensorShape dst_shape = lhs_shape;
881 dst_shape[0] = rhs_shape[0];
882 dst_shape[1] = lhs_shape[1];
883
884 // Create reference
Gian Marco Iodice9382ab32018-12-17 15:12:07 +0000885 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
886 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
Gian Marco Iodicee16c8902019-06-14 16:11:10 +0100887 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
888
889 const int n = rhs_shape[0];
890 const int m = lhs_shape[1];
891 const int batch_size = lhs_shape[2];
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000892
893 // Fill reference
894 fill(lhs, 0);
895 fill(rhs, 1);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +0100896 fill(bias, 2);
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000897
Gian Marco Iodicee16c8902019-06-14 16:11:10 +0100898 if(broadcast_bias)
899 {
900 // In case of broadcast, we need simply copy the first into the following "M" ones
901 for(int i = 1; i < m * batch_size; i++)
902 {
903 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
904 }
905 }
906
Gian Marco Iodice0c17aa22019-09-27 09:23:15 +0100907 if(fp_mixed_precision)
908 {
909 return reference::activation_layer(reference::gemm_mixed_precision<T>(lhs, rhs, bias, alpha, beta), act_info);
910 }
911 else
912 {
913 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
914 }
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +0000915 }
916
Sheri Zhangcc3e53c2020-11-16 21:17:28 +0000917 bool validate_result = true;
Gian Marco Iodice9382ab32018-12-17 15:12:07 +0000918 TensorType _target{};
919 SimpleTensor<T> _reference{};
920};
921
SiCongLi1af54162021-10-06 15:25:57 +0100922/** (EXPERIMENTAL_POST_OPS)*/
923template <typename TensorType, typename AccessorType, typename T, typename ReshapeLHSOperatorType, typename ReshapeRHSOperatorType, typename GEMMOperatorType, bool fp_mixed_precision = false>
924class GEMMMatrixMultiplyReshapedWithPostOpsValidationFixture : public framework::Fixture
925{
926public:
927 using PostOpArgBroadcast = std::tuple<bool, bool, bool>; // Instruct fixture if we need broadcasting in dimension 0, 1, 2 of each PostOp argument
928public:
929 template <typename...>
930 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, unsigned int v0, unsigned int h0, bool interleave_lhs,
931 bool interleave_rhs, bool export_to_cl_image, DataType data_type, float alpha, float beta, bool broadcast_bias, bool lhs_transpose, const ActivationLayerInfo &act_info,
932 const experimental::PostOpList<PostOpArgBroadcast> &post_ops)
933 {
934 GEMMLHSMatrixInfo lhs_info;
935 lhs_info.m0 = m0;
936 lhs_info.k0 = k0;
937 lhs_info.v0 = v0;
938 lhs_info.interleave = interleave_lhs;
939 lhs_info.transpose = lhs_transpose;
940
941 GEMMRHSMatrixInfo rhs_info;
942 rhs_info.n0 = n0;
943 rhs_info.k0 = k0;
944 rhs_info.h0 = h0;
945 rhs_info.interleave = interleave_rhs;
946 rhs_info.transpose = !lhs_transpose;
947 rhs_info.export_to_cl_image = export_to_cl_image;
948
949 // Set the tensor shapes for LHS and RHS matrices
950 const TensorShape lhs_shape(k, m, batch_size);
951 const TensorShape rhs_shape(n, k, batch_size);
952 const TensorShape bias_shape(n,
953 broadcast_bias ? 1 : m,
954 broadcast_bias ? 1 : batch_size);
955 auto post_ops_with_shapes = experimental::transform_post_op_list_arguments<PostOpArgBroadcast, TensorShape>(post_ops,
956 [ = ](auto broadcast)
957 {
958 return TensorShape
959 {
960 std::get<0>(broadcast) ? 1 : n,
961 std::get<1>(broadcast) ? 1 : m,
962 std::get<2>(broadcast) ? 1 : batch_size,
963 };
964 });
965
966 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, broadcast_bias, act_info, post_ops_with_shapes);
967 if(validate_result)
968 {
969 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, broadcast_bias, act_info, post_ops_with_shapes);
970 }
971 }
972
973protected:
974 template <typename U>
975 void fill(U &&tensor, int i)
976 {
977 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
978 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
979
980 DistributionType distribution{ T(-1.0f), T(1.0f) };
981 library->fill(tensor, distribution, i);
982
983 // Fill border with infinity in order to check the presence of NaN values (i.e. inf * 0)
984 DistributionType distribution_inf{ T(std::numeric_limits<float>::infinity()), T(std::numeric_limits<float>::infinity()) };
985 library->fill_borders_with_garbage(tensor, distribution_inf, i);
986 }
987
988 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
989 DataType data_type, float alpha, float beta, bool broadcast_bias, const ActivationLayerInfo &act_info, const experimental::PostOpList<TensorShape> &post_ops)
990 {
991 // Create tensors
992 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
993 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
994 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
995
996 // Create post op tensors and populate post op with them
997 std::vector<TensorType> post_op_tensors_holder{};
998 auto populated_post_ops = experimental::transform_post_op_list_arguments<TensorShape, ITensorInfo *>(post_ops,
999 [&post_op_tensors_holder, &data_type](auto shape)
1000 {
1001 auto t = create_tensor<TensorType>(shape, data_type, 1);
1002 post_op_tensors_holder.push_back(std::move(t));
1003 return post_op_tensors_holder.back().info();
1004 });
1005 TensorType lhs_reshaped;
1006 TensorType rhs_reshaped;
1007 TensorType dst;
1008
1009 const unsigned int M = lhs_shape[1];
1010 const unsigned int N = rhs_shape[0];
1011 const unsigned int K = lhs_shape[0];
1012 GEMMKernelInfo kernel_info;
1013 kernel_info.m = M;
1014 kernel_info.n = N;
1015 kernel_info.k = K;
1016 kernel_info.depth_output_gemm3d = 0;
1017 kernel_info.reinterpret_input_as_3d = false;
1018 kernel_info.broadcast_bias = broadcast_bias;
1019 kernel_info.activation_info = act_info;
1020 kernel_info.fp_mixed_precision = fp_mixed_precision;
1021 kernel_info.post_ops = populated_post_ops;
1022
1023 // The output tensor will be auto-initialized within the function
1024
1025 // Create and configure function
1026 ReshapeLHSOperatorType reshape_lhs;
1027 ReshapeRHSOperatorType reshape_rhs;
1028 GEMMOperatorType gemm;
1029
1030 validate_result = bool(reshape_rhs.validate(rhs.info(), rhs_reshaped.info(), rhs_info));
1031 validate_result = validate_result || !rhs_info.export_to_cl_image;
1032 if(!validate_result)
1033 {
1034 return nullptr;
1035 }
1036
1037 reshape_lhs.configure(lhs.info(), lhs_reshaped.info(), lhs_info);
1038 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
1039 gemm.configure(lhs_reshaped.info(), rhs_reshaped.info(), bias.info(), dst.info(), alpha, beta, lhs_info, rhs_info, kernel_info);
1040
1041 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1042 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
1043 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
1044 for(const auto &tensor : post_op_tensors_holder)
1045 {
1046 ARM_COMPUTE_ASSERT(tensor.info()->is_resizable());
1047 }
1048
1049 // We do not pad when using image as it needs to comply to strict pitch alignment restrictions
1050 if(!rhs_info.export_to_cl_image)
1051 {
1052 add_padding_x({ &lhs, &rhs, &lhs_reshaped, &rhs_reshaped, &bias, &dst });
1053 for(auto &tensor : post_op_tensors_holder)
1054 {
1055 add_padding_x({ &tensor });
1056 }
1057 }
1058
1059 // Allocate tensors
1060 lhs.allocator()->allocate();
1061 rhs.allocator()->allocate();
1062 lhs_reshaped.allocator()->allocate();
1063 rhs_reshaped.allocator()->allocate();
1064 bias.allocator()->allocate();
1065 dst.allocator()->allocate();
1066 for(auto &tensor : post_op_tensors_holder)
1067 {
1068 tensor.allocator()->allocate();
1069 }
1070
1071 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1072 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1073 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
1074 ARM_COMPUTE_ASSERT(!lhs_reshaped.info()->is_resizable());
1075 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1076 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
1077 for(const auto &tensor : post_op_tensors_holder)
1078 {
1079 ARM_COMPUTE_ASSERT(!tensor.info()->is_resizable());
1080 }
1081
1082 // Fill tensors
1083 fill(AccessorType(lhs), 0);
1084 fill(AccessorType(rhs), 1);
1085 fill(AccessorType(bias), 2);
1086 for(size_t i = 0; i < post_op_tensors_holder.size(); ++i)
1087 {
1088 fill(AccessorType(post_op_tensors_holder.at(i)), 3 + i);
1089 }
1090
1091 // Compute GEMM
1092 ITensorPack reshape_lhs_pack = { { ACL_SRC, &lhs }, { ACL_DST, &lhs_reshaped } };
1093 reshape_lhs.run(reshape_lhs_pack);
1094 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1095 reshape_rhs.run(reshape_rhs_pack);
1096 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs_reshaped },
1097 { ACL_SRC_1, &rhs_reshaped },
1098 { ACL_SRC_2, &bias },
1099 { ACL_DST, &dst }
1100 });
1101 for(size_t i = 0; i < post_op_tensors_holder.size(); ++i)
1102 {
1103 gemm_pack.add_tensor(experimental::get_post_op_arg_type(i), &post_op_tensors_holder.at(i));
1104 }
1105 gemm.run(gemm_pack);
1106
1107 return dst;
1108 }
1109
1110 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, bool broadcast_bias,
1111 const ActivationLayerInfo &act_info, const experimental::PostOpList<TensorShape> &post_ops)
1112 {
1113 TensorShape dst_shape = lhs_shape;
1114 dst_shape[0] = rhs_shape[0];
1115 dst_shape[1] = lhs_shape[1];
1116
1117 // Create reference
1118 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
1119 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
1120 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
1121 // Create post op tensors and populate post op with them
1122 auto populated_post_ops = experimental::transform_post_op_list_arguments<TensorShape, SimpleTensor<T>>(post_ops, [&data_type](auto shape)
1123 {
1124 return SimpleTensor<T> { shape, data_type, 1 };
1125 });
1126
1127 const int n = rhs_shape[0];
1128 const int m = lhs_shape[1];
1129 const int batch_size = lhs_shape[2];
1130
1131 // Fill reference
1132 int tensor_idx = 0;
1133 fill(lhs, tensor_idx++);
1134 fill(rhs, tensor_idx++);
1135 fill(bias, tensor_idx++);
1136 for(auto &op : populated_post_ops.get_list())
1137 {
1138 for(auto tensor : op->arguments())
1139 {
1140 fill(*tensor, tensor_idx++);
1141 }
1142 }
1143
1144 if(broadcast_bias)
1145 {
1146 // In case of broadcast, we need simply copy the first into the following "M" ones
1147 for(int i = 1; i < m * batch_size; i++)
1148 {
1149 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
1150 }
1151 }
1152
1153 SimpleTensor<T> out;
1154 if(fp_mixed_precision)
1155 {
1156 out = reference::gemm_mixed_precision<T>(lhs, rhs, bias, alpha, beta);
1157 }
1158 else
1159 {
1160 out = reference::gemm<T>(lhs, rhs, bias, alpha, beta);
1161 }
1162 // Ignore activation info if post ops are used instead
1163 if(populated_post_ops.size() > 0)
1164 {
1165 out = reference::post_ops<T>(out, populated_post_ops);
1166 }
1167 else
1168 {
1169 out = reference::activation_layer(out, act_info);
1170 }
1171 return out;
1172 }
1173
1174 bool validate_result = true;
1175 TensorType _target{};
1176 SimpleTensor<T> _reference{};
1177};
1178
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001179template <typename TensorType, typename AccessorType, typename T, typename ReshapeLHSOperatorType, typename ReshapeRHSOperatorType, typename GEMMOperatorType, bool fp_mixed_precision = false>
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001180class GEMMMatrixMultiplyReshaped3DValidationFixture : public framework::Fixture
1181{
1182public:
1183 template <typename...>
1184 void setup(unsigned int m_w, unsigned int m_h, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, unsigned int v0, unsigned int h0,
Gian Marco Iodicee3a849a2020-06-10 17:59:30 +01001185 bool interleave_lhs, bool interleave_rhs, bool export_to_cl_image, DataType data_type, float alpha, float beta, bool lhs_transpose, const ActivationLayerInfo &act_info)
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001186 {
1187 GEMMLHSMatrixInfo lhs_info;
1188 lhs_info.m0 = m0;
1189 lhs_info.k0 = k0;
1190 lhs_info.v0 = v0;
1191 lhs_info.interleave = interleave_lhs;
Giorgio Arenaae99b6e2019-08-01 14:22:12 +01001192 lhs_info.transpose = lhs_transpose;
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001193
1194 GEMMRHSMatrixInfo rhs_info;
Gian Marco Iodicee3a849a2020-06-10 17:59:30 +01001195 rhs_info.n0 = n0;
1196 rhs_info.k0 = k0;
1197 rhs_info.h0 = h0;
1198 rhs_info.interleave = interleave_rhs;
1199 rhs_info.transpose = !lhs_transpose;
1200 rhs_info.export_to_cl_image = export_to_cl_image;
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001201
1202 // In case of GEMM3D, m is the product between m_w and m_h
1203 const unsigned int m = m_w * m_h;
1204
1205 // Set the tensor shapes for LHS and RHS matrices
1206 const TensorShape lhs_shape(k, m, batch_size);
1207 const TensorShape rhs_shape(n, k, batch_size);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001208 const TensorShape bias_shape(n, 1, 1);
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001209
Sheri Zhangcc3e53c2020-11-16 21:17:28 +00001210 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, m_h, act_info);
1211 if(validate_result)
1212 {
1213 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, m_h, act_info);
1214 }
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001215 }
1216
1217protected:
1218 template <typename U>
1219 void fill(U &&tensor, int i)
1220 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001221 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +00001222 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001223
1224 DistributionType distribution{ T(-1.0f), T(1.0f) };
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001225 library->fill(tensor, distribution, i);
1226 }
1227
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001228 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001229 DataType data_type, float alpha, float beta, unsigned int m_h, const ActivationLayerInfo &act_info)
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001230 {
1231 // Create tensors
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001232 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1233 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
1234 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001235 TensorType lhs_reshaped;
1236 TensorType rhs_reshaped;
1237 TensorType dst;
1238
1239 const unsigned int M = lhs_shape[1];
1240 const unsigned int N = rhs_shape[0];
1241 const unsigned int K = lhs_shape[0];
Gian Marco Iodice7026b302019-06-26 17:18:11 +01001242 GEMMKernelInfo kernel_info;
1243 kernel_info.m = M;
1244 kernel_info.n = N;
1245 kernel_info.k = K;
1246 kernel_info.depth_output_gemm3d = m_h;
1247 kernel_info.reinterpret_input_as_3d = false;
1248 kernel_info.broadcast_bias = true;
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001249 kernel_info.activation_info = act_info;
Gian Marco Iodice0c17aa22019-09-27 09:23:15 +01001250 kernel_info.fp_mixed_precision = fp_mixed_precision;
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001251
1252 // The output tensor will be auto-initialized within the function
1253
1254 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001255 ReshapeLHSOperatorType reshape_lhs;
1256 ReshapeRHSOperatorType reshape_rhs;
1257 GEMMOperatorType gemm;
Sheri Zhangcc3e53c2020-11-16 21:17:28 +00001258
1259 validate_result = bool(reshape_rhs.validate(rhs.info(), rhs_reshaped.info(), rhs_info));
1260 validate_result = validate_result || !rhs_info.export_to_cl_image;
1261 if(!validate_result)
1262 {
1263 return nullptr;
1264 }
1265
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001266 reshape_lhs.configure(lhs.info(), lhs_reshaped.info(), lhs_info);
1267 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
1268 gemm.configure(lhs_reshaped.info(), rhs_reshaped.info(), bias.info(), dst.info(), alpha, beta, lhs_info, rhs_info, kernel_info);
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001269
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001270 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1271 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
1272 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001273
Georgios Pinitas3dca91b2021-04-13 13:35:58 +01001274 // We do not pad when using image as it needs to comply to strict pitch alignment restrictions
Giorgio Arena63825e82021-03-25 14:54:50 +00001275 if(!rhs_info.export_to_cl_image)
1276 {
1277 add_padding_x({ &lhs, &rhs, &lhs_reshaped, &rhs_reshaped, &bias, &dst });
1278 }
1279
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001280 // Allocate tensors
1281 lhs.allocator()->allocate();
1282 rhs.allocator()->allocate();
1283 lhs_reshaped.allocator()->allocate();
1284 rhs_reshaped.allocator()->allocate();
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001285 bias.allocator()->allocate();
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001286 dst.allocator()->allocate();
1287
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001288 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1289 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1290 ARM_COMPUTE_ASSERT(!lhs_reshaped.info()->is_resizable());
1291 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1292 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
1293 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001294
1295 // Fill tensors
1296 fill(AccessorType(lhs), 0);
1297 fill(AccessorType(rhs), 1);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001298 fill(AccessorType(bias), 2);
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001299
1300 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001301 ITensorPack reshape_lhs_pack = { { ACL_SRC, &lhs }, { ACL_DST, &lhs_reshaped } };
1302 reshape_lhs.run(reshape_lhs_pack);
1303 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1304 reshape_rhs.run(reshape_rhs_pack);
1305 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs_reshaped },
1306 { ACL_SRC_1, &rhs_reshaped },
1307 { ACL_SRC_2, &bias },
1308 { ACL_DST, &dst }
1309 });
1310 gemm.run(gemm_pack);
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001311
1312 return dst;
1313 }
1314
Michalis Spyrou6bff1952019-10-02 17:22:11 +01001315 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, unsigned int m_h,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001316 const ActivationLayerInfo &act_info)
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001317 {
1318 TensorShape dst_shape = lhs_shape;
1319 dst_shape.set(0, rhs_shape[0]);
1320 dst_shape.set(1, lhs_shape[1] / m_h);
1321 dst_shape.set(2, m_h);
1322 dst_shape.set(3, lhs_shape[2]);
1323
1324 // Create reference
1325 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
1326 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001327 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
1328
1329 const int n = rhs_shape[0];
1330 const int m = lhs_shape[1];
1331 const int batch_size = lhs_shape[2];
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001332
1333 // Fill reference
1334 fill(lhs, 0);
1335 fill(rhs, 1);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001336 fill(bias, 2);
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001337
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001338 // In case of broadcast, we need simply copy the first into the following "M" ones
1339 for(int i = 1; i < m * batch_size; i++)
1340 {
1341 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
1342 }
1343
Gian Marco Iodice0c17aa22019-09-27 09:23:15 +01001344 if(fp_mixed_precision)
1345 {
1346 return reference::activation_layer(reference::gemm_mixed_precision<T>(lhs, rhs, bias, alpha, beta), act_info);
1347 }
1348 else
1349 {
1350 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
1351 }
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001352 }
1353
Sheri Zhangcc3e53c2020-11-16 21:17:28 +00001354 bool validate_result = true;
Gian Marco Iodice9382ab32018-12-17 15:12:07 +00001355 TensorType _target{};
1356 SimpleTensor<T> _reference{};
Gian Marco Iodicebf9731e2018-12-12 10:18:04 +00001357};
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001358
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001359template <typename TensorType, typename AccessorType, typename T, typename ReshapeRHSOperatorType, typename GEMMOperatorType>
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001360class GEMMMatrixMultiplyReshapedOnlyRHSValidationFixture : public framework::Fixture
1361{
1362public:
1363 template <typename...>
1364 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, unsigned int h0,
Gian Marco Iodice781cba72020-06-19 16:56:57 +01001365 bool interleave_rhs, bool transpose_rhs, bool export_to_cl_image, DataType data_type, float alpha, float beta, bool broadcast_bias, const ActivationLayerInfo &act_info)
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001366 {
1367 GEMMLHSMatrixInfo lhs_info;
1368 lhs_info.m0 = m0;
1369 lhs_info.k0 = k0;
1370
1371 GEMMRHSMatrixInfo rhs_info;
Gian Marco Iodice781cba72020-06-19 16:56:57 +01001372 rhs_info.n0 = n0;
1373 rhs_info.k0 = k0;
1374 rhs_info.h0 = h0;
1375 rhs_info.interleave = interleave_rhs;
1376 rhs_info.transpose = transpose_rhs;
1377 rhs_info.export_to_cl_image = export_to_cl_image;
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001378
1379 // Set the tensor shapes for LHS and RHS matrices
1380 const TensorShape lhs_shape(k, m, batch_size);
1381 const TensorShape rhs_shape(n, k, batch_size);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001382 const TensorShape bias_shape(n,
1383 broadcast_bias ? 1 : m,
1384 broadcast_bias ? 1 : batch_size);
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001385
Sheri Zhangcc3e53c2020-11-16 21:17:28 +00001386 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, broadcast_bias, act_info);
1387 if(validate_result)
1388 {
1389 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, broadcast_bias, act_info);
1390 }
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001391 }
1392
1393protected:
1394 template <typename U>
1395 void fill(U &&tensor, int i)
1396 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001397 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +00001398 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001399
1400 DistributionType distribution{ T(-1.0f), T(1.0f) };
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001401 library->fill(tensor, distribution, i);
1402
1403 // Fill border with infinity in order to check the presence of NaN values (i.e. inf * 0)
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001404 DistributionType distribution_inf{ T(std::numeric_limits<float>::infinity()), T(std::numeric_limits<float>::infinity()) };
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001405 library->fill_borders_with_garbage(tensor, distribution_inf, i);
1406 }
1407
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001408 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001409 DataType data_type, float alpha, float beta, bool broadcast_bias, const ActivationLayerInfo &act_info)
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001410 {
1411 // Create tensors
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001412 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1413 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
1414 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001415 TensorType rhs_reshaped;
1416 TensorType dst;
1417
1418 const unsigned int M = lhs_shape[1];
1419 const unsigned int N = rhs_shape[0];
1420 const unsigned int K = lhs_shape[0];
Gian Marco Iodice7026b302019-06-26 17:18:11 +01001421 GEMMKernelInfo kernel_info;
1422 kernel_info.m = M;
1423 kernel_info.n = N;
1424 kernel_info.k = K;
1425 kernel_info.depth_output_gemm3d = 0;
1426 kernel_info.reinterpret_input_as_3d = false;
1427 kernel_info.broadcast_bias = broadcast_bias;
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001428 kernel_info.activation_info = act_info;
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001429
1430 // The output tensor will be auto-initialized within the function
1431
1432 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001433 ReshapeRHSOperatorType reshape_rhs;
1434 GEMMOperatorType gemm;
Sheri Zhangcc3e53c2020-11-16 21:17:28 +00001435
1436 validate_result = bool(reshape_rhs.validate(rhs.info(), rhs_reshaped.info(), rhs_info));
1437 validate_result = validate_result || !rhs_info.export_to_cl_image;
1438 if(!validate_result)
1439 {
1440 return nullptr;
1441 }
1442
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001443 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
1444 gemm.configure(lhs.info(), rhs_reshaped.info(), bias.info(), dst.info(), alpha, beta, lhs_info, rhs_info, kernel_info);
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001445
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001446 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1447 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
1448 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001449
Georgios Pinitas3dca91b2021-04-13 13:35:58 +01001450 // We do not pad when using image as it needs to comply to strict pitch alignment restrictions
Giorgio Arena63825e82021-03-25 14:54:50 +00001451 if(!rhs_info.export_to_cl_image)
1452 {
1453 add_padding_x({ &lhs, &rhs, &rhs_reshaped, &bias, &dst });
1454 }
1455
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001456 // Allocate tensors
1457 lhs.allocator()->allocate();
1458 rhs.allocator()->allocate();
1459 rhs_reshaped.allocator()->allocate();
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001460 bias.allocator()->allocate();
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001461 dst.allocator()->allocate();
1462
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001463 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1464 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1465 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1466 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
1467 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001468
1469 // Fill tensors
1470 fill(AccessorType(lhs), 0);
1471 fill(AccessorType(rhs), 1);
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001472 fill(AccessorType(bias), 2);
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001473
1474 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001475 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1476 reshape_rhs.run(reshape_rhs_pack);
1477 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs },
1478 { ACL_SRC_1, &rhs_reshaped },
1479 { ACL_SRC_2, &bias },
1480 { ACL_DST, &dst }
1481 });
1482 gemm.run(gemm_pack);
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001483
1484 return dst;
1485 }
1486
Michalis Spyrou6bff1952019-10-02 17:22:11 +01001487 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, bool broadcast_bias,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001488 const ActivationLayerInfo &act_info)
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001489 {
1490 TensorShape dst_shape = lhs_shape;
1491 dst_shape[0] = rhs_shape[0];
1492 dst_shape[1] = lhs_shape[1];
1493
1494 // Create reference
1495 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
1496 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001497 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
1498
1499 const int n = rhs_shape[0];
1500 const int m = lhs_shape[1];
1501 const int batch_size = lhs_shape[2];
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001502
1503 // Fill reference
1504 fill(lhs, 0);
1505 fill(rhs, 1);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001506 fill(bias, 2);
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001507
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001508 if(broadcast_bias)
1509 {
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001510 // In case of broadcast, we need simply copy the first into the following "M" ones
1511 for(int i = 1; i < m * batch_size; i++)
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001512 {
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001513 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001514 }
1515 }
Georgios Pinitasb0f342e2019-05-21 13:32:43 +01001516
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001517 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001518 }
1519
Sheri Zhangcc3e53c2020-11-16 21:17:28 +00001520 bool validate_result = true;
Gian Marco Iodiceadc53952019-02-15 11:10:31 +00001521 TensorType _target{};
1522 SimpleTensor<T> _reference{};
1523};
1524
SiCongLiafa19722021-10-24 19:12:33 +01001525/** (EXPERIMENTAL_POST_OPS)*/
1526template <typename TensorType, typename AccessorType, typename T, typename ReshapeRHSOperatorType, typename GEMMOperatorType>
1527class GEMMMatrixMultiplyReshapedOnlyRHSWithPostOpsValidationFixture : public framework::Fixture
1528{
1529public:
1530 using PostOpArgBroadcast = std::tuple<bool, bool, bool>; // Instruct fixture if we need broadcasting in dimension 0, 1, 2 of each PostOp argument
1531 template <typename...>
1532 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, unsigned int h0,
1533 bool interleave_rhs, bool transpose_rhs, bool export_to_cl_image, DataType data_type, float alpha, float beta, bool broadcast_bias, const ActivationLayerInfo &act_info,
1534 const experimental::PostOpList<PostOpArgBroadcast> &post_ops)
1535 {
1536 GEMMLHSMatrixInfo lhs_info;
1537 lhs_info.m0 = m0;
1538 lhs_info.k0 = k0;
1539
1540 GEMMRHSMatrixInfo rhs_info;
1541 rhs_info.n0 = n0;
1542 rhs_info.k0 = k0;
1543 rhs_info.h0 = h0;
1544 rhs_info.interleave = interleave_rhs;
1545 rhs_info.transpose = transpose_rhs;
1546 rhs_info.export_to_cl_image = export_to_cl_image;
1547
1548 // Set the tensor shapes for LHS and RHS matrices
1549 const TensorShape lhs_shape(k, m, batch_size);
1550 const TensorShape rhs_shape(n, k, batch_size);
1551 const TensorShape bias_shape(n,
1552 broadcast_bias ? 1 : m,
1553 broadcast_bias ? 1 : batch_size);
Gian Marco Iodice10e88a72021-11-29 12:49:19 +00001554
SiCongLiafa19722021-10-24 19:12:33 +01001555 auto post_ops_with_shapes = experimental::transform_post_op_list_arguments<PostOpArgBroadcast, TensorShape>(post_ops,
1556 [ = ](auto broadcast)
1557 {
1558 return TensorShape
1559 {
1560 std::get<0>(broadcast) ? 1 : n,
1561 std::get<1>(broadcast) ? 1 : m,
1562 std::get<2>(broadcast) ? 1 : batch_size,
1563 };
1564 });
1565
1566 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, broadcast_bias, act_info, post_ops_with_shapes);
1567 if(validate_result)
1568 {
1569 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, broadcast_bias, act_info, post_ops_with_shapes);
1570 }
1571 }
1572
1573protected:
1574 template <typename U>
1575 void fill(U &&tensor, int i)
1576 {
1577 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
1578 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
1579
1580 DistributionType distribution{ T(-1.0f), T(1.0f) };
1581 library->fill(tensor, distribution, i);
1582
1583 // Fill border with infinity in order to check the presence of NaN values (i.e. inf * 0)
1584 DistributionType distribution_inf{ T(std::numeric_limits<float>::infinity()), T(std::numeric_limits<float>::infinity()) };
1585 library->fill_borders_with_garbage(tensor, distribution_inf, i);
1586 }
1587
1588 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
1589 DataType data_type, float alpha, float beta, bool broadcast_bias, const ActivationLayerInfo &act_info, const experimental::PostOpList<TensorShape> &post_ops)
1590 {
1591 // Create tensors
1592 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1593 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
1594 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
1595 TensorType rhs_reshaped;
1596 TensorType dst;
1597 // Create post op tensors and populate post op with them
1598 std::vector<TensorType> post_op_tensors_holder{};
1599 auto populated_post_ops = experimental::transform_post_op_list_arguments<TensorShape, ITensorInfo *>(post_ops,
1600 [&post_op_tensors_holder, &data_type](auto shape)
1601 {
1602 auto t = create_tensor<TensorType>(shape, data_type, 1);
1603 post_op_tensors_holder.push_back(std::move(t));
1604 return post_op_tensors_holder.back().info();
1605 });
1606
1607 const unsigned int M = lhs_shape[1];
1608 const unsigned int N = rhs_shape[0];
1609 const unsigned int K = lhs_shape[0];
1610 GEMMKernelInfo kernel_info;
1611 kernel_info.m = M;
1612 kernel_info.n = N;
1613 kernel_info.k = K;
1614 kernel_info.depth_output_gemm3d = 0;
1615 kernel_info.reinterpret_input_as_3d = false;
1616 kernel_info.broadcast_bias = broadcast_bias;
1617 kernel_info.activation_info = act_info;
1618 kernel_info.post_ops = populated_post_ops;
1619
1620 // The output tensor will be auto-initialized within the function
1621
1622 // Create and configure function
1623 ReshapeRHSOperatorType reshape_rhs;
1624 GEMMOperatorType gemm;
1625
1626 validate_result = bool(reshape_rhs.validate(rhs.info(), rhs_reshaped.info(), rhs_info));
1627 validate_result = validate_result || !rhs_info.export_to_cl_image;
1628 if(!validate_result)
1629 {
1630 return nullptr;
1631 }
1632
1633 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
1634 gemm.configure(lhs.info(), rhs_reshaped.info(), bias.info(), dst.info(), alpha, beta, lhs_info, rhs_info, kernel_info);
1635
1636 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1637 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
1638 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
1639 for(const auto &tensor : post_op_tensors_holder)
1640 {
1641 ARM_COMPUTE_ASSERT(tensor.info()->is_resizable());
1642 }
1643
1644 // We do not pad when using image as it needs to comply to strict pitch alignment restrictions
1645 if(!rhs_info.export_to_cl_image)
1646 {
1647 add_padding_x({ &lhs, &rhs, &rhs_reshaped, &bias, &dst });
1648 for(auto &tensor : post_op_tensors_holder)
1649 {
1650 add_padding_x({ &tensor });
1651 }
1652 }
1653
1654 // Allocate tensors
1655 lhs.allocator()->allocate();
1656 rhs.allocator()->allocate();
1657 rhs_reshaped.allocator()->allocate();
1658 bias.allocator()->allocate();
1659 dst.allocator()->allocate();
1660 for(auto &tensor : post_op_tensors_holder)
1661 {
1662 tensor.allocator()->allocate();
1663 }
1664
1665 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1666 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1667 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1668 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
1669 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
1670 for(const auto &tensor : post_op_tensors_holder)
1671 {
1672 ARM_COMPUTE_ASSERT(!tensor.info()->is_resizable());
1673 }
1674
1675 // Fill tensors
1676 fill(AccessorType(lhs), 0);
1677 fill(AccessorType(rhs), 1);
1678 fill(AccessorType(bias), 2);
1679 for(size_t i = 0; i < post_op_tensors_holder.size(); ++i)
1680 {
1681 fill(AccessorType(post_op_tensors_holder.at(i)), 3 + i);
1682 }
1683
1684 // Compute GEMM
1685 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1686 reshape_rhs.run(reshape_rhs_pack);
1687 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs },
1688 { ACL_SRC_1, &rhs_reshaped },
1689 { ACL_SRC_2, &bias },
1690 { ACL_DST, &dst }
1691 });
1692 for(size_t i = 0; i < post_op_tensors_holder.size(); ++i)
1693 {
1694 gemm_pack.add_tensor(experimental::get_post_op_arg_type(i), &post_op_tensors_holder.at(i));
1695 }
1696 gemm.run(gemm_pack);
1697
1698 return dst;
1699 }
1700
1701 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, bool broadcast_bias,
1702 const ActivationLayerInfo &act_info, const experimental::PostOpList<TensorShape> &post_ops)
1703 {
1704 TensorShape dst_shape = lhs_shape;
1705 dst_shape[0] = rhs_shape[0];
1706 dst_shape[1] = lhs_shape[1];
1707
1708 // Create reference
1709 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
1710 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
1711 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
1712 // Create post op tensors and populate post op with them
1713 auto populated_post_ops = experimental::transform_post_op_list_arguments<TensorShape, SimpleTensor<T>>(post_ops, [&data_type](auto shape)
1714 {
1715 return SimpleTensor<T> { shape, data_type, 1 };
1716 });
1717
1718 const int n = rhs_shape[0];
1719 const int m = lhs_shape[1];
1720 const int batch_size = lhs_shape[2];
1721
1722 // Fill reference
1723 int tensor_idx = 0;
1724 fill(lhs, tensor_idx++);
1725 fill(rhs, tensor_idx++);
1726 fill(bias, tensor_idx++);
1727 for(auto &op : populated_post_ops.get_list())
1728 {
1729 for(auto tensor : op->arguments())
1730 {
1731 fill(*tensor, tensor_idx++);
1732 }
1733 }
1734
1735 if(broadcast_bias)
1736 {
1737 // In case of broadcast, we need simply copy the first into the following "M" ones
1738 for(int i = 1; i < m * batch_size; i++)
1739 {
1740 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
1741 }
1742 }
1743
1744 SimpleTensor<T> out;
1745 out = reference::gemm<T>(lhs, rhs, bias, alpha, beta);
1746 // Ignore activation info if post ops are used instead
1747 if(populated_post_ops.size() > 0)
1748 {
1749 out = reference::post_ops<T>(out, populated_post_ops);
1750 }
1751 else
1752 {
1753 out = reference::activation_layer(out, act_info);
1754 }
1755 return out;
1756 }
1757
1758 bool validate_result = true;
1759 TensorType _target{};
1760 SimpleTensor<T> _reference{};
1761};
1762
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001763template <typename TensorType, typename AccessorType, typename T, typename ReshapeRHSOperatorType, typename GEMMOperatorType>
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001764class GEMMMatrixMultiplyReshapedOnlyRHS3DValidationFixture : public framework::Fixture
1765{
1766public:
1767 template <typename...>
1768 void setup(unsigned int m_w, unsigned int m_h, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, unsigned int h0,
Gian Marco Iodice9ae06d42020-10-22 16:37:12 +01001769 bool interleave_rhs, bool transpose_rhs, bool export_to_cl_image, bool has_pad_y, DataType data_type, float alpha, float beta, const ActivationLayerInfo &act_info)
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001770 {
1771 GEMMLHSMatrixInfo lhs_info;
1772 lhs_info.m0 = m0;
1773 lhs_info.k0 = k0;
1774
1775 GEMMRHSMatrixInfo rhs_info;
Gian Marco Iodice781cba72020-06-19 16:56:57 +01001776 rhs_info.n0 = n0;
1777 rhs_info.k0 = k0;
1778 rhs_info.h0 = h0;
1779 rhs_info.interleave = interleave_rhs;
1780 rhs_info.transpose = transpose_rhs;
1781 rhs_info.export_to_cl_image = export_to_cl_image;
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001782
1783 // In case of GEMM3D, m is the product between m_w and m_h
1784 const unsigned int m = m_w * m_h;
1785
1786 // Set the tensor shapes for LHS and RHS matrices
1787 const TensorShape lhs_shape(k, m, batch_size);
1788 const TensorShape rhs_shape(n, k, batch_size);
1789 const TensorShape bias_shape(n, 1, 1);
1790
Sheri Zhangcc3e53c2020-11-16 21:17:28 +00001791 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, m_h, act_info, has_pad_y);
1792 if(validate_result)
1793 {
1794 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, m_h, act_info);
1795 }
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001796 }
1797
1798protected:
1799 template <typename U>
1800 void fill(U &&tensor, int i)
1801 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001802 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +00001803 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001804
1805 DistributionType distribution{ T(-1.0f), T(1.0f) };
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001806 library->fill(tensor, distribution, i);
1807 }
1808
1809 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
1810 DataType data_type, float alpha, float beta,
Gian Marco Iodice9ae06d42020-10-22 16:37:12 +01001811 unsigned int m_h, const ActivationLayerInfo &act_info, bool has_pad_y)
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001812 {
1813 // Create tensors
1814 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1815 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
1816 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
1817 TensorType rhs_reshaped;
1818 TensorType dst;
1819
1820 const unsigned int M = lhs_shape[1];
1821 const unsigned int N = rhs_shape[0];
1822 const unsigned int K = lhs_shape[0];
Gian Marco Iodice7026b302019-06-26 17:18:11 +01001823 GEMMKernelInfo kernel_info;
1824 kernel_info.m = M;
1825 kernel_info.n = N;
1826 kernel_info.k = K;
1827 kernel_info.depth_output_gemm3d = m_h;
1828 kernel_info.reinterpret_input_as_3d = false;
1829 kernel_info.broadcast_bias = true;
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001830 kernel_info.activation_info = act_info;
Gian Marco Iodice9ae06d42020-10-22 16:37:12 +01001831 kernel_info.has_pad_y = has_pad_y;
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001832
1833 // The output tensor will be auto-initialized within the function
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001834 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001835 ReshapeRHSOperatorType reshape_rhs;
1836 GEMMOperatorType gemm;
Sheri Zhangcc3e53c2020-11-16 21:17:28 +00001837
1838 validate_result = bool(reshape_rhs.validate(rhs.info(), rhs_reshaped.info(), rhs_info));
1839 validate_result = validate_result || !rhs_info.export_to_cl_image;
1840 if(!validate_result)
1841 {
1842 return nullptr;
1843 }
1844
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001845 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
1846 gemm.configure(lhs.info(), rhs_reshaped.info(), bias.info(), dst.info(), alpha, beta, lhs_info, rhs_info, kernel_info);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001847
Gian Marco Iodice9ae06d42020-10-22 16:37:12 +01001848 if(has_pad_y)
1849 {
1850 // Add dummy padding into lhs to validate has_pad_y path
1851 lhs.info()->extend_padding(PaddingSize(2, 0, 2, 0));
1852 dst.info()->extend_padding(PaddingSize(2, 0, 1, 0));
1853 }
1854
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001855 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1856 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
1857 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001858
Georgios Pinitas3dca91b2021-04-13 13:35:58 +01001859 // We do not pad when using image as it needs to comply to strict pitch alignment restrictions
Giorgio Arena63825e82021-03-25 14:54:50 +00001860 if(!rhs_info.export_to_cl_image)
1861 {
1862 add_padding_x({ &lhs, &rhs, &rhs_reshaped, &bias, &dst });
1863 }
1864
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001865 // Allocate tensors
1866 lhs.allocator()->allocate();
1867 rhs.allocator()->allocate();
1868 rhs_reshaped.allocator()->allocate();
1869 bias.allocator()->allocate();
1870 dst.allocator()->allocate();
1871
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001872 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1873 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1874 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1875 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
1876 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001877
1878 // Fill tensors
1879 fill(AccessorType(lhs), 0);
1880 fill(AccessorType(rhs), 1);
1881 fill(AccessorType(bias), 2);
1882
1883 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001884 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1885 reshape_rhs.run(reshape_rhs_pack);
1886 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs },
1887 { ACL_SRC_1, &rhs_reshaped },
1888 { ACL_SRC_2, &bias },
1889 { ACL_DST, &dst }
1890 });
1891 gemm.run(gemm_pack);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001892
1893 return dst;
1894 }
1895
Michalis Spyrou6bff1952019-10-02 17:22:11 +01001896 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, unsigned int m_h,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001897 const ActivationLayerInfo &act_info)
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001898 {
1899 TensorShape dst_shape = lhs_shape;
1900 dst_shape.set(0, rhs_shape[0]);
1901 dst_shape.set(1, lhs_shape[1] / m_h);
1902 dst_shape.set(2, m_h);
1903 dst_shape.set(3, lhs_shape[2]);
1904
1905 // Create reference
1906 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
1907 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
1908 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
1909
1910 const int n = rhs_shape[0];
1911 const int m = lhs_shape[1];
1912 const int batch_size = lhs_shape[2];
1913
1914 // Fill reference
1915 fill(lhs, 0);
1916 fill(rhs, 1);
1917 fill(bias, 2);
1918
1919 // In case of broadcast, we need simply copy the first into the following "M" ones
1920 for(int i = 1; i < m * batch_size; i++)
1921 {
1922 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
1923 }
1924
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001925 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001926 }
1927
Sheri Zhangcc3e53c2020-11-16 21:17:28 +00001928 bool validate_result = true;
Gian Marco Iodicee16c8902019-06-14 16:11:10 +01001929 TensorType _target{};
1930 SimpleTensor<T> _reference{};
1931};
1932
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001933template <typename TensorType, typename AccessorType, typename T, typename GEMMOperatorType>
giuros01b3204e72019-04-01 13:50:22 +01001934class GEMMMatrixMultiplyNativeValidationFixture : public framework::Fixture
1935{
1936public:
1937 template <typename...>
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001938 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, DataType data_type, float alpha, float beta, bool broadcast_bias,
1939 const ActivationLayerInfo &act_info)
giuros01b3204e72019-04-01 13:50:22 +01001940 {
1941 GEMMLHSMatrixInfo lhs_info;
1942 lhs_info.m0 = m0;
1943 lhs_info.k0 = k0;
1944
1945 GEMMRHSMatrixInfo rhs_info;
1946 rhs_info.n0 = n0;
1947 rhs_info.k0 = k0;
1948
1949 // Set the tensor shapes for LHS and RHS matrices
1950 const TensorShape lhs_shape(k, m, batch_size);
1951 const TensorShape rhs_shape(n, k, batch_size);
Gian Marco Iodice944170e2019-06-24 14:40:30 +01001952 const TensorShape bias_shape(n,
1953 broadcast_bias ? 1 : m,
1954 broadcast_bias ? 1 : batch_size);
giuros01b3204e72019-04-01 13:50:22 +01001955
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001956 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, broadcast_bias, act_info);
Michalis Spyrou6bff1952019-10-02 17:22:11 +01001957 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, broadcast_bias, act_info);
giuros01b3204e72019-04-01 13:50:22 +01001958 }
1959
1960protected:
1961 template <typename U>
1962 void fill(U &&tensor, int i)
1963 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001964 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +00001965 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001966
1967 DistributionType distribution{ T(-1.0f), T(1.0f) };
giuros01b3204e72019-04-01 13:50:22 +01001968 library->fill(tensor, distribution, i);
1969
1970 // Fill border with infinity in order to check the presence of NaN values (i.e. inf * 0)
Giorgio Arena4bdd1772020-12-17 16:47:07 +00001971 DistributionType distribution_inf{ T(std::numeric_limits<float>::infinity()), T(std::numeric_limits<float>::infinity()) };
giuros01b3204e72019-04-01 13:50:22 +01001972 library->fill_borders_with_garbage(tensor, distribution_inf, i);
1973 }
1974
Gian Marco Iodice944170e2019-06-24 14:40:30 +01001975 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001976 DataType data_type, float alpha, float beta, bool broadcast_bias, const ActivationLayerInfo &act_info)
giuros01b3204e72019-04-01 13:50:22 +01001977 {
1978 // Create tensors
Gian Marco Iodice944170e2019-06-24 14:40:30 +01001979 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1980 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
1981 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
giuros01b3204e72019-04-01 13:50:22 +01001982 TensorType dst;
1983
1984 const unsigned int M = lhs_shape[1];
1985 const unsigned int N = rhs_shape[0];
1986 const unsigned int K = lhs_shape[0];
Gian Marco Iodice7026b302019-06-26 17:18:11 +01001987 GEMMKernelInfo kernel_info;
1988 kernel_info.m = M;
1989 kernel_info.n = N;
1990 kernel_info.k = K;
1991 kernel_info.depth_output_gemm3d = 0;
1992 kernel_info.reinterpret_input_as_3d = false;
1993 kernel_info.broadcast_bias = broadcast_bias;
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01001994 kernel_info.activation_info = act_info;
giuros01b3204e72019-04-01 13:50:22 +01001995
1996 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001997 GEMMOperatorType gemm;
1998 gemm.configure(lhs.info(), rhs.info(), bias.info(), dst.info(), alpha, beta, lhs_info, rhs_info, kernel_info);
giuros01b3204e72019-04-01 13:50:22 +01001999
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01002000 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
2001 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
2002 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
giuros01b3204e72019-04-01 13:50:22 +01002003
Giorgio Arena63825e82021-03-25 14:54:50 +00002004 add_padding_x({ &lhs, &rhs, &bias, &dst });
2005
giuros01b3204e72019-04-01 13:50:22 +01002006 // Allocate tensors
2007 lhs.allocator()->allocate();
2008 rhs.allocator()->allocate();
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002009 bias.allocator()->allocate();
giuros01b3204e72019-04-01 13:50:22 +01002010 dst.allocator()->allocate();
2011
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01002012 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
2013 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
2014 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
2015 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
giuros01b3204e72019-04-01 13:50:22 +01002016
2017 // Fill tensors
2018 fill(AccessorType(lhs), 0);
2019 fill(AccessorType(rhs), 1);
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002020 fill(AccessorType(bias), 2);
giuros01b3204e72019-04-01 13:50:22 +01002021
2022 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +01002023 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs },
2024 { ACL_SRC_1, &rhs },
2025 { ACL_SRC_2, &bias },
2026 { ACL_DST, &dst }
2027 });
2028 gemm.run(gemm_pack);
giuros01b3204e72019-04-01 13:50:22 +01002029
2030 return dst;
2031 }
2032
Michalis Spyrou6bff1952019-10-02 17:22:11 +01002033 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, bool broadcast_bias,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01002034 const ActivationLayerInfo &act_info)
giuros01b3204e72019-04-01 13:50:22 +01002035 {
2036 TensorShape dst_shape = lhs_shape;
2037 dst_shape[0] = rhs_shape[0];
2038 dst_shape[1] = lhs_shape[1];
2039
2040 // Create reference
2041 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
2042 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002043 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
2044
2045 const int n = rhs_shape[0];
2046 const int m = lhs_shape[1];
2047 const int batch_size = lhs_shape[2];
giuros01b3204e72019-04-01 13:50:22 +01002048
2049 // Fill reference
2050 fill(lhs, 0);
2051 fill(rhs, 1);
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002052 fill(bias, 2);
giuros01b3204e72019-04-01 13:50:22 +01002053
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002054 if(broadcast_bias)
2055 {
2056 // In case of broadcast, we need simply copy the first into the following "M" ones
2057 for(int i = 1; i < m * batch_size; i++)
2058 {
2059 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
2060 }
2061 }
2062
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01002063 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
giuros01b3204e72019-04-01 13:50:22 +01002064 }
2065
2066 TensorType _target{};
2067 SimpleTensor<T> _reference{};
2068};
2069
Georgios Pinitas856f66e2021-04-22 21:13:21 +01002070template <typename TensorType, typename AccessorType, typename T, typename GEMMOperatorType>
SiCongLiafa19722021-10-24 19:12:33 +01002071class GEMMMatrixMultiplyNativeWithPostOpsValidationFixture : public framework::Fixture
2072{
2073public:
2074 using PostOpArgBroadcast = std::tuple<bool, bool, bool>; // Instruct fixture if we need broadcasting in dimension 0, 1, 2 of each PostOp argument
2075public:
2076 template <typename...>
2077 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, DataType data_type, float alpha, float beta, bool broadcast_bias,
2078 const ActivationLayerInfo &act_info, const experimental::PostOpList<PostOpArgBroadcast> &post_ops)
2079 {
2080 GEMMLHSMatrixInfo lhs_info;
2081 lhs_info.m0 = m0;
2082 lhs_info.k0 = k0;
2083
2084 GEMMRHSMatrixInfo rhs_info;
2085 rhs_info.n0 = n0;
2086 rhs_info.k0 = k0;
2087
2088 // Set the tensor shapes for LHS and RHS matrices
2089 const TensorShape lhs_shape(k, m, batch_size);
2090 const TensorShape rhs_shape(n, k, batch_size);
2091 const TensorShape bias_shape(n,
2092 broadcast_bias ? 1 : m,
2093 broadcast_bias ? 1 : batch_size);
2094 const auto post_ops_with_shapes = experimental::transform_post_op_list_arguments<PostOpArgBroadcast, TensorShape>(post_ops,
2095 [ = ](auto broadcast)
2096 {
2097 return TensorShape
2098 {
2099 std::get<0>(broadcast) ? 1 : n,
2100 std::get<1>(broadcast) ? 1 : m,
2101 std::get<2>(broadcast) ? 1 : batch_size,
2102 };
2103 });
2104
2105 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, broadcast_bias, act_info, post_ops_with_shapes);
2106 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, broadcast_bias, act_info, post_ops_with_shapes);
2107 }
2108
2109protected:
2110 template <typename U>
2111 void fill(U &&tensor, int i)
2112 {
2113 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
2114 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
2115
2116 DistributionType distribution{ T(-1.0f), T(1.0f) };
2117 library->fill(tensor, distribution, i);
2118
2119 // Fill border with infinity in order to check the presence of NaN values (i.e. inf * 0)
2120 DistributionType distribution_inf{ T(std::numeric_limits<float>::infinity()), T(std::numeric_limits<float>::infinity()) };
2121 library->fill_borders_with_garbage(tensor, distribution_inf, i);
2122 }
2123
2124 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
2125 DataType data_type, float alpha, float beta, bool broadcast_bias, const ActivationLayerInfo &act_info, const experimental::PostOpList<TensorShape> &post_ops)
2126 {
2127 // Create tensors
2128 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
2129 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
2130 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
2131 TensorType dst;
2132 // Create post op tensors and populate post op with them
2133 std::vector<TensorType> post_op_tensors_holder{};
2134 auto populated_post_ops = experimental::transform_post_op_list_arguments<TensorShape, ITensorInfo *>(post_ops,
2135 [&post_op_tensors_holder, &data_type](auto shape)
2136 {
2137 auto t = create_tensor<TensorType>(shape, data_type, 1);
2138 post_op_tensors_holder.push_back(std::move(t));
2139 return post_op_tensors_holder.back().info();
2140 });
2141
2142 const unsigned int M = lhs_shape[1];
2143 const unsigned int N = rhs_shape[0];
2144 const unsigned int K = lhs_shape[0];
2145 GEMMKernelInfo kernel_info;
2146 kernel_info.m = M;
2147 kernel_info.n = N;
2148 kernel_info.k = K;
2149 kernel_info.depth_output_gemm3d = 0;
2150 kernel_info.reinterpret_input_as_3d = false;
2151 kernel_info.broadcast_bias = broadcast_bias;
2152 kernel_info.activation_info = act_info;
2153 kernel_info.post_ops = populated_post_ops;
2154
2155 // Create and configure function
2156 GEMMOperatorType gemm;
2157 gemm.configure(lhs.info(), rhs.info(), bias.info(), dst.info(), alpha, beta, lhs_info, rhs_info, kernel_info);
2158
2159 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
2160 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
2161 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
2162 for(const auto &tensor : post_op_tensors_holder)
2163 {
2164 ARM_COMPUTE_ASSERT(tensor.info()->is_resizable());
2165 }
2166
2167 add_padding_x({ &lhs, &rhs, &bias, &dst });
2168 for(auto &tensor : post_op_tensors_holder)
2169 {
2170 add_padding_x({ &tensor });
2171 }
2172
2173 // Allocate tensors
2174 lhs.allocator()->allocate();
2175 rhs.allocator()->allocate();
2176 bias.allocator()->allocate();
2177 dst.allocator()->allocate();
2178 for(auto &tensor : post_op_tensors_holder)
2179 {
2180 tensor.allocator()->allocate();
2181 }
2182
2183 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
2184 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
2185 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
2186 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
2187 for(const auto &tensor : post_op_tensors_holder)
2188 {
2189 ARM_COMPUTE_ASSERT(!tensor.info()->is_resizable());
2190 }
2191
2192 // Fill tensors
2193 fill(AccessorType(lhs), 0);
2194 fill(AccessorType(rhs), 1);
2195 fill(AccessorType(bias), 2);
2196 for(size_t i = 0; i < post_op_tensors_holder.size(); ++i)
2197 {
2198 fill(AccessorType(post_op_tensors_holder.at(i)), 3 + i);
2199 }
2200
2201 // Compute GEMM
2202 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs },
2203 { ACL_SRC_1, &rhs },
2204 { ACL_SRC_2, &bias },
2205 { ACL_DST, &dst }
2206 });
2207 for(size_t i = 0; i < post_op_tensors_holder.size(); ++i)
2208 {
2209 gemm_pack.add_tensor(experimental::get_post_op_arg_type(i), &post_op_tensors_holder.at(i));
2210 }
2211 gemm.run(gemm_pack);
2212
2213 return dst;
2214 }
2215
2216 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, bool broadcast_bias,
2217 const ActivationLayerInfo &act_info, const experimental::PostOpList<TensorShape> &post_ops)
2218 {
2219 TensorShape dst_shape = lhs_shape;
2220 dst_shape[0] = rhs_shape[0];
2221 dst_shape[1] = lhs_shape[1];
2222
2223 // Create reference
2224 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
2225 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
2226 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
2227 // Create post op tensors and populate post op with them
2228 auto populated_post_ops = experimental::transform_post_op_list_arguments<TensorShape, SimpleTensor<T>>(post_ops, [&data_type](auto shape)
2229 {
2230 return SimpleTensor<T> { shape, data_type, 1 };
2231 });
2232
2233 const int n = rhs_shape[0];
2234 const int m = lhs_shape[1];
2235 const int batch_size = lhs_shape[2];
2236
2237 // Fill reference
2238 int tensor_idx = 0;
2239 fill(lhs, tensor_idx++);
2240 fill(rhs, tensor_idx++);
2241 fill(bias, tensor_idx++);
2242 for(auto &op : populated_post_ops.get_list())
2243 {
2244 for(auto tensor : op->arguments())
2245 {
2246 fill(*tensor, tensor_idx++);
2247 }
2248 }
2249
2250 if(broadcast_bias)
2251 {
2252 // In case of broadcast, we need simply copy the first into the following "M" ones
2253 for(int i = 1; i < m * batch_size; i++)
2254 {
2255 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
2256 }
2257 }
2258
2259 SimpleTensor<T> out;
2260 out = reference::gemm<T>(lhs, rhs, bias, alpha, beta);
2261 // Ignore activation info if post ops are used instead
2262 if(populated_post_ops.size() > 0)
2263 {
2264 out = reference::post_ops<T>(out, populated_post_ops);
2265 }
2266 else
2267 {
2268 out = reference::activation_layer(out, act_info);
2269 }
2270 return out;
2271 }
2272
2273 TensorType _target{};
2274 SimpleTensor<T> _reference{};
2275};
2276
2277template <typename TensorType, typename AccessorType, typename T, typename GEMMOperatorType>
giuros01b3204e72019-04-01 13:50:22 +01002278class GEMMMatrixMultiplyNative3DValidationFixture : public framework::Fixture
2279{
2280public:
2281 template <typename...>
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01002282 void setup(unsigned int m_w, unsigned int m_h, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, DataType data_type, float alpha, float beta,
2283 const ActivationLayerInfo &act_info)
giuros01b3204e72019-04-01 13:50:22 +01002284 {
2285 GEMMLHSMatrixInfo lhs_info;
2286 lhs_info.m0 = m0;
2287 lhs_info.k0 = k0;
2288
2289 GEMMRHSMatrixInfo rhs_info;
2290 rhs_info.n0 = n0;
2291 rhs_info.k0 = k0;
2292
2293 // In case of GEMM3D, m is the product between m_w and m_h
2294 const unsigned int m = m_w * m_h;
2295
2296 // Set the tensor shapes for LHS and RHS matrices
2297 const TensorShape lhs_shape(k, m, batch_size);
2298 const TensorShape rhs_shape(n, k, batch_size);
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002299 const TensorShape bias_shape(n, 1, 1);
giuros01b3204e72019-04-01 13:50:22 +01002300
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01002301 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, alpha, beta, m_h, act_info);
Michalis Spyrou6bff1952019-10-02 17:22:11 +01002302 _reference = compute_reference(lhs_shape, rhs_shape, data_type, alpha, beta, m_h, act_info);
giuros01b3204e72019-04-01 13:50:22 +01002303 }
2304
2305protected:
2306 template <typename U>
2307 void fill(U &&tensor, int i)
2308 {
Giorgio Arena4bdd1772020-12-17 16:47:07 +00002309 static_assert(std::is_floating_point<T>::value || std::is_same<T, half>::value, "Only floating point data types supported.");
Giorgio Arena33b103b2021-01-08 10:37:15 +00002310 using DistributionType = typename std::conditional<std::is_same<T, half>::value, arm_compute::utils::uniform_real_distribution_16bit<T>, std::uniform_real_distribution<T>>::type;
Giorgio Arena4bdd1772020-12-17 16:47:07 +00002311
2312 DistributionType distribution{ T(-1.0f), T(1.0f) };
giuros01b3204e72019-04-01 13:50:22 +01002313 library->fill(tensor, distribution, i);
2314 }
2315
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002316 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01002317 DataType data_type, float alpha, float beta, unsigned int m_h, const ActivationLayerInfo &act_info)
giuros01b3204e72019-04-01 13:50:22 +01002318 {
2319 // Create tensors
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002320 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
2321 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
2322 TensorType bias = create_tensor<TensorType>(bias_shape, data_type, 1);
giuros01b3204e72019-04-01 13:50:22 +01002323 TensorType dst;
2324
2325 const unsigned int M = lhs_shape[1];
2326 const unsigned int N = rhs_shape[0];
2327 const unsigned int K = lhs_shape[0];
Gian Marco Iodice7026b302019-06-26 17:18:11 +01002328 GEMMKernelInfo kernel_info;
2329 kernel_info.m = M;
2330 kernel_info.n = N;
2331 kernel_info.k = K;
2332 kernel_info.depth_output_gemm3d = m_h;
2333 kernel_info.reinterpret_input_as_3d = false;
2334 kernel_info.broadcast_bias = true;
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01002335 kernel_info.activation_info = act_info;
giuros01b3204e72019-04-01 13:50:22 +01002336
2337 // The output tensor will be auto-initialized within the function
2338
2339 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +01002340 GEMMOperatorType gemm;
2341 gemm.configure(lhs.info(), rhs.info(), bias.info(), dst.info(), alpha, beta, lhs_info, rhs_info, kernel_info);
giuros01b3204e72019-04-01 13:50:22 +01002342
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01002343 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
2344 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
2345 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
giuros01b3204e72019-04-01 13:50:22 +01002346
Giorgio Arena63825e82021-03-25 14:54:50 +00002347 add_padding_x({ &lhs, &rhs, &bias, &dst });
2348
giuros01b3204e72019-04-01 13:50:22 +01002349 // Allocate tensors
2350 lhs.allocator()->allocate();
2351 rhs.allocator()->allocate();
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002352 bias.allocator()->allocate();
giuros01b3204e72019-04-01 13:50:22 +01002353 dst.allocator()->allocate();
2354
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01002355 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
2356 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
2357 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
2358 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
giuros01b3204e72019-04-01 13:50:22 +01002359
2360 // Fill tensors
2361 fill(AccessorType(lhs), 0);
2362 fill(AccessorType(rhs), 1);
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002363 fill(AccessorType(bias), 2);
giuros01b3204e72019-04-01 13:50:22 +01002364
2365 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +01002366 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs },
2367 { ACL_SRC_1, &rhs },
2368 { ACL_SRC_2, &bias },
2369 { ACL_DST, &dst }
2370 });
2371 gemm.run(gemm_pack);
giuros01b3204e72019-04-01 13:50:22 +01002372
2373 return dst;
2374 }
2375
Michalis Spyrou6bff1952019-10-02 17:22:11 +01002376 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type, float alpha, float beta, unsigned int m_h,
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01002377 const ActivationLayerInfo &act_info)
giuros01b3204e72019-04-01 13:50:22 +01002378 {
2379 TensorShape dst_shape = lhs_shape;
2380 dst_shape.set(0, rhs_shape[0]);
2381 dst_shape.set(1, lhs_shape[1] / m_h);
2382 dst_shape.set(2, m_h);
2383 dst_shape.set(3, lhs_shape[2]);
2384
2385 // Create reference
2386 SimpleTensor<T> lhs{ lhs_shape, data_type, 1 };
2387 SimpleTensor<T> rhs{ rhs_shape, data_type, 1 };
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002388 SimpleTensor<T> bias{ dst_shape, data_type, 1 };
2389
2390 const int n = rhs_shape[0];
2391 const int m = lhs_shape[1];
2392 const int batch_size = lhs_shape[2];
giuros01b3204e72019-04-01 13:50:22 +01002393
2394 // Fill reference
2395 fill(lhs, 0);
2396 fill(rhs, 1);
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002397 fill(bias, 2);
giuros01b3204e72019-04-01 13:50:22 +01002398
Gian Marco Iodice944170e2019-06-24 14:40:30 +01002399 // In case of broadcast, we need simply copy the first into the following "M" ones
2400 for(int i = 1; i < m * batch_size; i++)
2401 {
2402 memcpy(bias.data() + i * n, bias.data(), n * sizeof(T));
2403 }
2404
Gian Marco Iodiceca1f4602019-07-16 15:46:48 +01002405 return reference::activation_layer(reference::gemm<T>(lhs, rhs, bias, alpha, beta), act_info);
giuros01b3204e72019-04-01 13:50:22 +01002406 }
2407
2408 TensorType _target{};
2409 SimpleTensor<T> _reference{};
2410};
2411
Moritz Pflanzer4dfc2352017-08-02 14:51:36 +01002412} // namespace validation
2413} // namespace test
2414} // namespace arm_compute
2415#endif /* ARM_COMPUTE_TEST_GEMM_FIXTURE */