diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/CL/CLKernelLibrary.cpp | 2 | ||||
-rw-r--r-- | src/core/CL/cl_kernels/gemmlowp.cl | 571 | ||||
-rw-r--r-- | src/core/CL/kernels/CLGEMMLowpMatrixMultiplyReshapedKernel.cpp | 308 | ||||
-rw-r--r-- | src/runtime/CL/functions/CLGEMMLowpMatrixMultiplyCore.cpp | 168 | ||||
-rw-r--r-- | src/runtime/CL/gemm_reshaped/CLGEMMReshapedConfigurationBifrost.cpp | 156 |
5 files changed, 1057 insertions, 148 deletions
diff --git a/src/core/CL/CLKernelLibrary.cpp b/src/core/CL/CLKernelLibrary.cpp index 905a34a509..4635d11a3a 100644 --- a/src/core/CL/CLKernelLibrary.cpp +++ b/src/core/CL/CLKernelLibrary.cpp @@ -297,6 +297,8 @@ const std::map<std::string, std::string> CLKernelLibrary::_kernel_program_map = { "gemmlowp_mm_interleaved_transposed_bifrost", "gemmlowp.cl" }, { "gemmlowp_mm_interleaved_transposed_bifrost_dot8", "gemmlowp.cl" }, { "gemmlowp_mm_interleaved_transposed_midgard", "gemmlowp.cl" }, + { "gemmlowp_mm_reshaped_lhs_nt_rhs_t", "gemmlowp.cl" }, + { "gemmlowp_mm_reshaped_lhs_nt_rhs_t_dot8", "gemmlowp.cl" }, { "gemmlowp_offset_contribution", "gemmlowp.cl" }, { "gemmlowp_offset_contribution_quantize_down", "gemmlowp.cl" }, { "gemmlowp_offset_contribution_quantize_down_fixedpoint", "gemmlowp.cl" }, diff --git a/src/core/CL/cl_kernels/gemmlowp.cl b/src/core/CL/cl_kernels/gemmlowp.cl index 8c1fa548e4..277338bf08 100644 --- a/src/core/CL/cl_kernels/gemmlowp.cl +++ b/src/core/CL/cl_kernels/gemmlowp.cl @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 ARM Limited. + * Copyright (c) 2017-2019 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -23,6 +23,7 @@ */ #include "helpers.h" #include "helpers_asymm.h" +#include "repeat.h" #if defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8) #if defined(ARM_COMPUTE_OPENCL_DOT8_ACC_ENABLED) && defined(cl_arm_integer_dot_product_accumulate_int8) @@ -1943,6 +1944,574 @@ __kernel void gemmlowp_mm_bifrost_dot8(IMAGE_DECLARATION(src0), #endif // defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8) #endif // defined(NUM_ELEMS_PROCESSED_PER_THREAD_X) && defined(NUM_ELEMS_PROCESSED_PER_THREAD_Y) && defined(COLS_A) +#if defined(M0) && defined(N0) && defined(K0) && defined(V0) && defined(H0) + +#if defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8) + +#if K0 == 2 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + ARM_DOT((uchar4)(a, (uchar2)0), (uchar4)(b, (uchar2)0), c); \ + }) +#elif K0 == 3 // K0 == 3 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + ARM_DOT((uchar4)(a, (uchar)0), (uchar4)(b, (uchar)0), c); \ + }) +#elif K0 == 4 // K0 == 4 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + ARM_DOT(a, b, c); \ + }) +#elif K0 == 8 // K0 == 8 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + ARM_DOT(a.s0123, b.s0123, c); \ + ARM_DOT(a.s4567, b.s4567, c); \ + }) +#elif K0 == 16 // K0 == 16 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + ARM_DOT(a.s0123, b.s0123, c); \ + ARM_DOT(a.s4567, b.s4567, c); \ + ARM_DOT(a.s89AB, b.s89AB, c); \ + ARM_DOT(a.sCDEF, b.sCDEF, c); \ + }) +#else // K0 not supported +#error "K0 value not supported" +#endif // K0 + +#else // defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8) + +#if K0 == 2 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + c += (uint)a.s0 * b.s0; \ + c += (uint)a.s1 * b.s1; \ + }) +#elif K0 == 3 // K0 == 3 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + c += (uint)a.s0 * b.s0; \ + c += (uint)a.s1 * b.s1; \ + c += (uint)a.s2 * b.s2; \ + }) +#elif K0 == 4 // K0 == 4 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + c += (uint)a.s0 * b.s0; \ + c += (uint)a.s1 * b.s1; \ + c += (uint)a.s2 * b.s2; \ + c += (uint)a.s3 * b.s3; \ + }) +#elif K0 == 8 // K0 == 8 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + c += (uint)a.s0 * b.s0; \ + c += (uint)a.s1 * b.s1; \ + c += (uint)a.s2 * b.s2; \ + c += (uint)a.s3 * b.s3; \ + c += (uint)a.s4 * b.s4; \ + c += (uint)a.s5 * b.s5; \ + c += (uint)a.s6 * b.s6; \ + c += (uint)a.s7 * b.s7; \ + }) +#elif K0 == 16 // K0 == 16 +#define ARM_DOT_K0(a, b, c) \ + ({ \ + c += (uint)a.s0 * b.s0; \ + c += (uint)a.s1 * b.s1; \ + c += (uint)a.s2 * b.s2; \ + c += (uint)a.s3 * b.s3; \ + c += (uint)a.s4 * b.s4; \ + c += (uint)a.s5 * b.s5; \ + c += (uint)a.s6 * b.s6; \ + c += (uint)a.s7 * b.s7; \ + c += (uint)a.s8 * b.s8; \ + c += (uint)a.s9 * b.s9; \ + c += (uint)a.sA * b.sA; \ + c += (uint)a.sB * b.sB; \ + c += (uint)a.sC * b.sC; \ + c += (uint)a.sD * b.sD; \ + c += (uint)a.sE * b.sE; \ + c += (uint)a.sF * b.sF; \ + }) +#else // K0 not supported +#error "K0 value not supported" +#endif // K0 + +#endif //defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8) + +#if N0 == 2 +#define ARM_DOT_K0XN0(a, b, c) \ + ({ \ + ARM_DOT_K0((a), (b##0), (c.s0)); \ + ARM_DOT_K0((a), (b##1), (c.s1)); \ + }) +#elif N0 == 3 // N0 == 3 +#define ARM_DOT_K0XN0(a, b, c) \ + ({ \ + ARM_DOT_K0((a), (b##0), (c.s0)); \ + ARM_DOT_K0((a), (b##1), (c.s1)); \ + ARM_DOT_K0((a), (b##2), (c.s2)); \ + }) +#elif N0 == 4 // N0 == 4 +#define ARM_DOT_K0XN0(a, b, c) \ + ({ \ + ARM_DOT_K0((a), (b##0), (c.s0)); \ + ARM_DOT_K0((a), (b##1), (c.s1)); \ + ARM_DOT_K0((a), (b##2), (c.s2)); \ + ARM_DOT_K0((a), (b##3), (c.s3)); \ + }) +#elif N0 == 8 // N0 == 8 +#define ARM_DOT_K0XN0(a, b, c) \ + ({ \ + ARM_DOT_K0((a), (b##0), (c.s0)); \ + ARM_DOT_K0((a), (b##1), (c.s1)); \ + ARM_DOT_K0((a), (b##2), (c.s2)); \ + ARM_DOT_K0((a), (b##3), (c.s3)); \ + ARM_DOT_K0((a), (b##4), (c.s4)); \ + ARM_DOT_K0((a), (b##5), (c.s5)); \ + ARM_DOT_K0((a), (b##6), (c.s6)); \ + ARM_DOT_K0((a), (b##7), (c.s7)); \ + }) +#elif N0 == 16 // N0 == 16 +#define ARM_DOT_K0XN0(a, b, c) \ + ({ \ + ARM_DOT_K0((a), (b##0), (c.s0)); \ + ARM_DOT_K0((a), (b##1), (c.s1)); \ + ARM_DOT_K0((a), (b##2), (c.s2)); \ + ARM_DOT_K0((a), (b##3), (c.s3)); \ + ARM_DOT_K0((a), (b##4), (c.s4)); \ + ARM_DOT_K0((a), (b##5), (c.s5)); \ + ARM_DOT_K0((a), (b##6), (c.s6)); \ + ARM_DOT_K0((a), (b##7), (c.s7)); \ + ARM_DOT_K0((a), (b##8), (c.s8)); \ + ARM_DOT_K0((a), (b##9), (c.s9)); \ + ARM_DOT_K0((a), (b##A), (c.sA)); \ + ARM_DOT_K0((a), (b##B), (c.sB)); \ + ARM_DOT_K0((a), (b##C), (c.sC)); \ + ARM_DOT_K0((a), (b##D), (c.sD)); \ + ARM_DOT_K0((a), (b##E), (c.sE)); \ + ARM_DOT_K0((a), (b##F), (c.sF)); \ + }) +#else // N0 not supported +#error "N0 value not supported" +#endif // N0 conditions + +/** This OpenCL kernel computes the matrix multiplication between 2 matrices. + * The LHS matrix must be reshaped with @ref CLGEMMReshapeLHSMatrixKernel and the M0xK0 must be NOT transposed + * The RHS matrix must be reshaped with @ref CLGEMMReshapeRHSMatrixKernel and the K0xN0 must be transposed + * + * @note The block's dimensions used for reshaping the LHS matrix and the RHS matrix (M0, N0 and K0) must be passed at compile time using -DM0, -DN0 and -DK0 (i.e. -DM0=4, -DN0=8, -DK0=4). + * @note The number of M0xK0 vertical blocks stored on the same output row of the reshaped LHS matrix must be passed at compile time using -DV0 (i.e. -DV0=2) + * @note The number of K0xN0 horizontal blocks stored on the same output row of the reshaped RHS matrix must be passed at compile time using -DH0 (i.e. -DH0=2) + * @note If the M0xK0 blocks in the reshaped LHS matrix have been interleaved, the option -DLHS_INTERLEAVE must passed at compile time. + * @note If the K0xN0 blocks in the reshaped RHS matrix have been interleaved, the option -DRHS_INTERLEAVE must passed at compile time. + * @note Only the following configurations of M0, N0 and K0 are currently supported: + * - M0 = 2, 3, 4, 5, 6, 7, 8 + * - N0 = 2, 3, 4, 8, 16 + * - K0 = 2, 3, 4, 8, 16 + * + * @note In case the output has to be reinterpreted as a 3D tensor (i.e. output of convolution layer), the following information must be passed at compile time: + * -# REINTERPRET_OUTPUT_AS_3D: To reinterpret the output as 3D + * -# HEIGHT_GEMM3D: The height of the output in case it has to be reinterpreted as a 3D tensor. + * -# DEPTH_GEMM3D: The depth of the output in case it has to be reinterpreted as a 3D tensor + * (HEIGHT_GEMM3D * DEPTH_GEMM3D) = columns LHS matrix NOT reshaped + * + * @param[in] lhs_ptr Pointer to the LHS reshaped matrix. Supported data type: QASYMM8 + * @param[in] lhs_stride_x Stride of the LHS reshaped matrix in X dimension (in bytes) + * @param[in] lhs_step_x src_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] lhs_stride_y Stride of the LHS reshaped matrix in Y dimension (in bytes) + * @param[in] lhs_step_y src_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] lhs_offset_first_element_in_bytes The offset of the first element in the LHS reshaped matrix + * @param[in] rhs_ptr Pointer to the RHS reshaped matrix. Supported data type: same as @p lhs_ptr + * @param[in] rhs_stride_x Stride of the RHS reshaped matrix in X dimension (in bytes) + * @param[in] rhs_step_x src_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] rhs_stride_y Stride of the RHS reshaped matrix in Y dimension (in bytes) + * @param[in] rhs_step_y src_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] rhs_offset_first_element_in_bytes The offset of the first element in the RHS reshaped matrix + * @param[out] dst_ptr Pointer to the destination matrix Supported data type: same as @p lhs_ptr + * @param[in] dst_stride_x Stride of the destination matrix in X dimension (in bytes) + * @param[in] dst_step_x dst_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] dst_stride_y Stride of the destination matrix in Y dimension (in bytes) + * @param[in] dst_step_y dst_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] dst_offset_first_element_in_bytes The offset of the first element in the destination matrix + * @param[in] k Number of columns in LHS matrix and rows in RHS matrix not reshaped. + * @param[in] lhs_stride_z Stride of the LHS reshaped matrix in Z dimension (in bytes) + * @param[in] rhs_stride_z Stride of the RHS reshaped matrix in Z dimension (in bytes) + * @param[in] dst_stride_z Stride of the destination tensor in Z dimension (in bytes) + * @param[in] dst_cross_plane_pad (Optional) Bottom paddings in unit of elements (only if defined REINTERPRET_OUTPUT_AS_3D) + */ +__kernel void gemmlowp_mm_reshaped_lhs_nt_rhs_t(IMAGE_DECLARATION(lhs), + IMAGE_DECLARATION(rhs), + IMAGE_DECLARATION(dst), + uint k, + uint lhs_stride_z, + uint rhs_stride_z, + uint dst_stride_z +#if defined(REINTERPRET_OUTPUT_AS_3D) + , + uint dst_cross_plane_pad +#endif // REINTERPRET_OUTPUT_AS_3D + ) +{ + // Block size +#define LHS_BLOCK_SIZE ((K0) * (M0)) + +#if defined(LHS_INTERLEAVE) +#define LHS_OFFSET_X (K0) +#define LHS_STEP_X ((K0) * (V0)) +#define LHS_STEP_LOOP (1) +#else // defined(INTERLEAVE) +#define LHS_OFFSET_X (LHS_BLOCK_SIZE) +#define LHS_STEP_X (K0) +#define LHS_STEP_LOOP (V0) +#endif // defined(INTERLEAVE) + + // Block size +#define RHS_BLOCK_SIZE ((K0) * (N0)) + + // RHS offset and step X +#if defined(RHS_INTERLEAVE) +#define RHS_OFFSET_X (K0) +#define RHS_STEP_X ((K0) * (H0)) +#define RHS_STEP_LOOP (1) +#else // defined(RHS_INTERLEAVE) +#define RHS_OFFSET_X (RHS_BLOCK_SIZE) +#define RHS_STEP_X (K0) +#define RHS_STEP_LOOP (H0) +#endif // defined(RHS_INTERLEAVE) + + // Compute LHS matrix address + __global uchar *lhs_addr = lhs_ptr + lhs_offset_first_element_in_bytes + (get_global_id(1) % V0) * (uint)LHS_OFFSET_X + (get_global_id(1) / V0) * (uint)lhs_stride_y + (get_global_id( + 2) + * lhs_stride_z); + + // Compute RHS matrix address + __global uchar *rhs_addr = rhs_ptr + rhs_offset_first_element_in_bytes + (get_global_id(0) % H0) * (uint)RHS_OFFSET_X + (get_global_id(0) / (uint)H0) * rhs_stride_y; + +#if defined(MATRIX_B_DEPTH) + // Do not slide matrix B if the matrix B has 3 dimensions and matrix A more than 3 + rhs_addr += (get_global_id(2) % MATRIX_B_DEPTH) * rhs_stride_z; +#else // defined(MATRIX_B_DEPTH) + rhs_addr += get_global_id(2) * rhs_stride_z; +#endif // defined(MATRIX_B_DEPTH) + + // Initialize the accumulators + REPEAT_VAR_INIT_TO_CONST(M0, VEC_DATA_TYPE(uint, N0), c, 0); //VEC_DATA_TYPE(uint, N0) c0=0,c1=0,c2=0,... c(M0-1)=0; + + for(int i = 0; i < k; i += K0) + { + // Supported cases (M0, K0): + // 2,4 - 2,8 - 2,16 + // 3,4 - 3,8 - 3,16 + // 4,4 - 4,8 - 4,16 + // 5,4 - 5,8 - 5,16 + // 6,4 - 6,8 - 6,16 + // Load values from LHS matrix + VEC_DATA_TYPE(uchar, K0) + a0 = VLOAD(K0)(0, lhs_addr + 0 * LHS_STEP_X); +#if M0 > 1 + VEC_DATA_TYPE(uchar, K0) + a1 = VLOAD(K0)(0, lhs_addr + 1 * LHS_STEP_X); +#endif // M0 > 1 +#if M0 > 2 + VEC_DATA_TYPE(uchar, K0) + a2 = VLOAD(K0)(0, lhs_addr + 2 * LHS_STEP_X); +#endif // M0 > 2 +#if M0 > 3 + VEC_DATA_TYPE(uchar, K0) + a3 = VLOAD(K0)(0, lhs_addr + 3 * LHS_STEP_X); +#endif // M0 > 3 +#if M0 > 4 + VEC_DATA_TYPE(uchar, K0) + a4 = VLOAD(K0)(0, lhs_addr + 4 * LHS_STEP_X); +#endif // M0 > 4 +#if M0 > 5 + VEC_DATA_TYPE(uchar, K0) + a5 = VLOAD(K0)(0, lhs_addr + 5 * LHS_STEP_X); +#endif // M0 > 5 +#if M0 > 6 + VEC_DATA_TYPE(uchar, K0) + a6 = VLOAD(K0)(0, lhs_addr + 6 * LHS_STEP_X); +#endif // M0 > 6 +#if M0 > 7 + VEC_DATA_TYPE(uchar, K0) + a7 = VLOAD(K0)(0, lhs_addr + 7 * LHS_STEP_X); +#endif // M0 > 7 + + // Load values from RHS matrix + VEC_DATA_TYPE(uchar, K0) + b0 = VLOAD(K0)(0, rhs_addr + 0 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + b1 = VLOAD(K0)(0, rhs_addr + 1 * RHS_STEP_X); +#if N0 > 2 + VEC_DATA_TYPE(uchar, K0) + b2 = VLOAD(K0)(0, rhs_addr + 2 * RHS_STEP_X); +#endif // N0 > 2 +#if N0 > 3 + VEC_DATA_TYPE(uchar, K0) + b3 = VLOAD(K0)(0, rhs_addr + 3 * RHS_STEP_X); +#endif // N0 > 3 +#if N0 > 4 + VEC_DATA_TYPE(uchar, K0) + b4 = VLOAD(K0)(0, rhs_addr + 4 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + b5 = VLOAD(K0)(0, rhs_addr + 5 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + b6 = VLOAD(K0)(0, rhs_addr + 6 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + b7 = VLOAD(K0)(0, rhs_addr + 7 * RHS_STEP_X); +#endif // N0 > 4 +#if N0 > 8 + VEC_DATA_TYPE(uchar, K0) + b8 = VLOAD(K0)(0, rhs_addr + 8 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + b9 = VLOAD(K0)(0, rhs_addr + 9 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + bA = VLOAD(K0)(0, rhs_addr + 10 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + bB = VLOAD(K0)(0, rhs_addr + 11 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + bC = VLOAD(K0)(0, rhs_addr + 12 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + bD = VLOAD(K0)(0, rhs_addr + 13 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + bE = VLOAD(K0)(0, rhs_addr + 14 * RHS_STEP_X); + VEC_DATA_TYPE(uchar, K0) + bF = VLOAD(K0)(0, rhs_addr + 15 * RHS_STEP_X); +#endif // N0 > 8 + + // Accumulate + ARM_DOT_K0XN0(a0, b, c0); +#if M0 > 1 + ARM_DOT_K0XN0(a1, b, c1); +#endif // M0 > 1 +#if M0 > 2 + ARM_DOT_K0XN0(a2, b, c2); +#endif // M0 > 2 +#if M0 > 3 + ARM_DOT_K0XN0(a3, b, c3); +#endif // M0 > 3 +#if M0 > 4 + ARM_DOT_K0XN0(a4, b, c4); +#endif // M0 > 4 +#if M0 > 5 + ARM_DOT_K0XN0(a5, b, c5); +#endif // M0 > 5 +#if M0 > 6 + ARM_DOT_K0XN0(a6, b, c6); +#endif // M0 > 6 +#if M0 > 7 + ARM_DOT_K0XN0(a7, b, c7); +#endif // M0 > 7 + + lhs_addr += (M0 * LHS_STEP_X * LHS_STEP_LOOP); + rhs_addr += (N0 * RHS_STEP_X * RHS_STEP_LOOP); + } + + __global uchar *dst_addr = dst_ptr + dst_offset_first_element_in_bytes + (get_global_id(0) * (uint)N0 * sizeof(int)) + (get_global_id(1) * (uint)M0 * dst_stride_y); + + REPEAT_VAR_INIT_TO_CONST(8, uint, zout, 0); //uint zout0=0,zout1=0,zout2=0,... zout7=0; + +#if defined(REINTERPRET_OUTPUT_AS_3D) + // Since we store a 2D output tile in a 3D tensor, we need to check when the plane changes across the z dimension + // in order to take into account the presence of possible cross plane paddings + // + // | | + // | plane0 | + // | | + // |__________________| + // |******************| + // | cross_plane_pad | + // |******************| + // | | + // | plane1 | + // | | + // |__________________| + + // The plane (zin) is calculated dividing M (y * M0) by HEIGHT_GEMM3D + zout0 = (0 + (uint)(get_global_id(1) * (uint)M0)) / (uint)HEIGHT_GEMM3D; + zout0 = min((uint)(DEPTH_GEMM3D - 1), zout0); + zout0 *= (dst_cross_plane_pad * dst_stride_y); +#if M0 > 1 + zout1 = (1 + (uint)(get_global_id(1) * (uint)M0)) / (uint)HEIGHT_GEMM3D; + zout1 = min((uint)(DEPTH_GEMM3D - 1), zout1); + zout1 *= (dst_cross_plane_pad * dst_stride_y); +#endif // M0 > 1 +#if M0 > 2 + zout2 = (2 + (uint)(get_global_id(1) * (uint)M0)) / (uint)HEIGHT_GEMM3D; + zout2 = min((uint)(DEPTH_GEMM3D - 1), zout2); + zout2 *= (dst_cross_plane_pad * dst_stride_y); +#endif // M0 > 2 +#if M0 > 3 + zout3 = (3 + (uint)(get_global_id(1) * (uint)M0)) / (uint)HEIGHT_GEMM3D; + zout3 = min((uint)(DEPTH_GEMM3D - 1), zout3); + zout3 *= (dst_cross_plane_pad * dst_stride_y); +#endif // M0 > 3 +#if M0 > 4 + zout4 = (4 + (uint)(get_global_id(1) * (uint)M0)) / (uint)HEIGHT_GEMM3D; + zout4 = min((uint)(DEPTH_GEMM3D - 1), zout4); + zout4 *= (dst_cross_plane_pad * dst_stride_y); +#endif // M0 > 4 +#if M0 > 5 + zout5 = (5 + (uint)(get_global_id(1) * (uint)M0)) / (uint)HEIGHT_GEMM3D; + zout5 = min((uint)(DEPTH_GEMM3D - 1), zout5); + zout5 *= (dst_cross_plane_pad * dst_stride_y); +#endif // M0 > 5 +#if M0 > 6 + zout6 = (6 + (uint)(get_global_id(1) * (uint)M0)) / (uint)HEIGHT_GEMM3D; + zout6 = min((uint)(DEPTH_GEMM3D - 1), zout6); + zout6 *= (dst_cross_plane_pad * dst_stride_y); +#endif // M0 > 6 +#if M0 > 7 + zout7 = (7 + (uint)(get_global_id(1) * (uint)M0)) / (uint)HEIGHT_GEMM3D; + zout7 = min((uint)(DEPTH_GEMM3D - 1), zout7); + zout7 *= (dst_cross_plane_pad * dst_stride_y); +#endif // M0 > 7 + + // Add offset for batched GEMM. The batches will be in the fourth dimension and for this reason we + // multiply dst_stride_z by DEPTH_GEMM3D + dst_addr += get_global_id(2) * dst_stride_z * DEPTH_GEMM3D; + +#else // defined(REINTERPRET_OUTPUT_AS_3D) + + // Add offset for batched GEMM + dst_addr += get_global_id(2) * dst_stride_z; + +#endif // defined(REINTERPRET_OUTPUT_AS_3D) + + // Store output block + VSTORE(N0) + (CONVERT_SAT(c0, VEC_DATA_TYPE(int, N0)), 0, (__global int *)(dst_addr + 0 * dst_stride_y + zout0)); +#if M0 > 1 + VSTORE(N0) + (CONVERT_SAT(c1, VEC_DATA_TYPE(int, N0)), 0, (__global int *)(dst_addr + 1 * dst_stride_y + zout1)); +#endif // M0 > 1 +#if M0 > 2 + VSTORE(N0) + (CONVERT_SAT(c2, VEC_DATA_TYPE(int, N0)), 0, (__global int *)(dst_addr + 2 * dst_stride_y + zout2)); +#endif // M0 > 2 +#if M0 > 3 + VSTORE(N0) + (CONVERT_SAT(c3, VEC_DATA_TYPE(int, N0)), 0, (__global int *)(dst_addr + 3 * dst_stride_y + zout3)); +#endif // M0 > 3 +#if M0 > 4 + VSTORE(N0) + (CONVERT_SAT(c4, VEC_DATA_TYPE(int, N0)), 0, (__global int *)(dst_addr + 4 * dst_stride_y + zout4)); +#endif // M0 > 4 +#if M0 > 5 + VSTORE(N0) + (CONVERT_SAT(c5, VEC_DATA_TYPE(int, N0)), 0, (__global int *)(dst_addr + 5 * dst_stride_y + zout5)); +#endif // M0 > 5 +#if M0 > 6 + VSTORE(N0) + (CONVERT_SAT(c6, VEC_DATA_TYPE(int, N0)), 0, (__global int *)(dst_addr + 6 * dst_stride_y + zout6)); +#endif // M0 > 6 +#if M0 > 7 + VSTORE(N0) + (CONVERT_SAT(c7, VEC_DATA_TYPE(int, N0)), 0, (__global int *)(dst_addr + 7 * dst_stride_y + zout7)); +#endif // M0 > 7 + +#undef LHS_BLOCK_SIZE +#undef LHS_OFFSET_X +#undef LHS_STEP_X +#undef RHS_BLOCK_SIZE +#undef RHS_OFFSET_X +#undef RHS_STEP_X +} + +#if defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8) +/** This OpenCL kernel computes the matrix multiplication between 2 matrices unsing the dot8 instruction. + * The LHS matrix must be reshaped with @ref CLGEMMReshapeLHSMatrixKernel and the M0xK0 must be NOT transposed + * The RHS matrix must be reshaped with @ref CLGEMMReshapeRHSMatrixKernel and the K0xN0 must be transposed + * + * @note The block's dimensions used for reshaping the LHS matrix and the RHS matrix (M0, N0 and K0) must be passed at compile time using -DM0, -DN0 and -DK0 (i.e. -DM0=4, -DN0=8, -DK0=4). + * @note The number of M0xK0 vertical blocks stored on the same output row of the reshaped LHS matrix must be passed at compile time using -DV0 (i.e. -DV0=2) + * @note The number of K0xN0 horizontal blocks stored on the same output row of the reshaped RHS matrix must be passed at compile time using -DH0 (i.e. -DH0=2) + * @note If the M0xK0 blocks in the reshaped LHS matrix have been interleaved, the option -DLHS_INTERLEAVE must passed at compile time. + * @note If the K0xN0 blocks in the reshaped RHS matrix have been interleaved, the option -DRHS_INTERLEAVE must passed at compile time. + * @note Only the following configurations of M0, N0 and K0 are currently supported: + * - M0 = 2, 3, 4, 5, 6, 7, 8 + * - N0 = 2, 3, 4, 8, 16 + * - K0 = 2, 3, 4, 8, 16 + * + * @note In case the output has to be reinterpreted as a 3D tensor (i.e. output of convolution layer), the following information must be passed at compile time: + * -# REINTERPRET_OUTPUT_AS_3D: To reinterpret the output as 3D + * -# HEIGHT_GEMM3D: The height of the output in case it has to be reinterpreted as a 3D tensor. + * -# DEPTH_GEMM3D: The depth of the output in case it has to be reinterpreted as a 3D tensor + * (HEIGHT_GEMM3D * DEPTH_GEMM3D) = columns LHS matrix NOT reshaped + * + * @param[in] lhs_ptr Pointer to the LHS reshaped matrix. Supported data type: QASYMM8 + * @param[in] lhs_stride_x Stride of the LHS reshaped matrix in X dimension (in bytes) + * @param[in] lhs_step_x src_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] lhs_stride_y Stride of the LHS reshaped matrix in Y dimension (in bytes) + * @param[in] lhs_step_y src_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] lhs_offset_first_element_in_bytes The offset of the first element in the LHS reshaped matrix + * @param[in] rhs_ptr Pointer to the RHS reshaped matrix. Supported data type: same as @p lhs_ptr + * @param[in] rhs_stride_x Stride of the RHS reshaped matrix in X dimension (in bytes) + * @param[in] rhs_step_x src_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] rhs_stride_y Stride of the RHS reshaped matrix in Y dimension (in bytes) + * @param[in] rhs_step_y src_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] rhs_offset_first_element_in_bytes The offset of the first element in the RHS reshaped matrix + * @param[out] dst_ptr Pointer to the destination matrix Supported data type: same as @p lhs_ptr + * @param[in] dst_stride_x Stride of the destination matrix in X dimension (in bytes) + * @param[in] dst_step_x dst_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] dst_stride_y Stride of the destination matrix in Y dimension (in bytes) + * @param[in] dst_step_y dst_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] dst_offset_first_element_in_bytes The offset of the first element in the destination matrix + * @param[in] k Number of columns in LHS matrix and rows in RHS matrix not reshaped. + * @param[in] lhs_stride_z Stride of the LHS reshaped matrix in Z dimension (in bytes) + * @param[in] rhs_stride_z Stride of the RHS reshaped matrix in Z dimension (in bytes) + * @param[in] dst_stride_z Stride of the destination tensor in Z dimension (in bytes) + * @param[in] dst_cross_plane_pad (Optional) Bottom paddings in unit of elements (only if defined REINTERPRET_OUTPUT_AS_3D) + */ +__kernel void gemmlowp_mm_reshaped_lhs_nt_rhs_t_dot8(IMAGE_DECLARATION(lhs), + IMAGE_DECLARATION(rhs), + IMAGE_DECLARATION(dst), + uint k, + uint lhs_stride_z, + uint rhs_stride_z, + uint dst_stride_z +#if defined(REINTERPRET_OUTPUT_AS_3D) + , + uint dst_cross_plane_pad +#endif // REINTERPRET_OUTPUT_AS_3D + ) +{ + // Note: ARM_DOT_K0XN0 is generated with the dot8 instruction + gemmlowp_mm_reshaped_lhs_nt_rhs_t(lhs_ptr, + lhs_stride_x, + lhs_step_x, + lhs_stride_y, + lhs_step_y, + lhs_offset_first_element_in_bytes, + rhs_ptr, + rhs_stride_x, + rhs_step_x, + rhs_stride_y, + rhs_step_y, + rhs_offset_first_element_in_bytes, + dst_ptr, + dst_stride_x, + dst_step_x, + dst_stride_y, + dst_step_y, + dst_offset_first_element_in_bytes, + k, + lhs_stride_z, + rhs_stride_z, + dst_stride_z +#if defined(REINTERPRET_OUTPUT_AS_3D) + , + dst_cross_plane_pad +#endif // REINTERPRET_OUTPUT_AS_3D + ); +} +#endif // defined(ARM_COMPUTE_OPENCL_DOT8_ENABLED) && defined(cl_arm_integer_dot_product_int8) +#endif // defined(M0) && defined(N0) && defined(K0) && defined(V0) && defined(H0) && defined(K) + #if defined(COLS_A) /** OpenCL kernel used to compute the row-vectors of sums of all the entries in each row of Matrix A. * diff --git a/src/core/CL/kernels/CLGEMMLowpMatrixMultiplyReshapedKernel.cpp b/src/core/CL/kernels/CLGEMMLowpMatrixMultiplyReshapedKernel.cpp new file mode 100644 index 0000000000..e9be1a6dfc --- /dev/null +++ b/src/core/CL/kernels/CLGEMMLowpMatrixMultiplyReshapedKernel.cpp @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2019 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/core/CL/kernels/CLGEMMLowpMatrixMultiplyReshapedKernel.h" + +#include "arm_compute/core/AccessWindowStatic.h" +#include "arm_compute/core/CL/CLHelpers.h" +#include "arm_compute/core/CL/CLKernelLibrary.h" +#include "arm_compute/core/CL/ICLTensor.h" +#include "arm_compute/core/CL/OpenCL.h" +#include "arm_compute/core/Error.h" +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/TensorInfo.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/core/Utils.h" +#include "arm_compute/core/Validate.h" +#include "arm_compute/core/Window.h" +#include "arm_compute/core/utils/misc/ShapeCalculator.h" +#include "support/ToolchainSupport.h" + +#include <cstddef> +#include <cstdint> +#include <tuple> + +using namespace arm_compute; +using namespace arm_compute::misc::shape_calculator; + +namespace arm_compute +{ +class Coordinates; +} // namespace arm_compute + +namespace +{ +using ElementsProcessed = Steps; + +Status validate_arguments(const ITensorInfo *input0, const ITensorInfo *input1, const ITensorInfo *output, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info, + const GEMMReshapeInfo &gemm_info) +{ + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input0, input1, output); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input0, 1, DataType::QASYMM8); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input0, input1); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(input0->num_dimensions() > 4, "The number of dimensions for the LHS matrix must be <= 4"); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(input1->num_dimensions() > 3, "The number of dimensions for the RHS matrix must be <= 3"); + ARM_COMPUTE_RETURN_ERROR_ON(lhs_info.transpose); + ARM_COMPUTE_RETURN_ERROR_ON(!rhs_info.transpose); + ARM_COMPUTE_RETURN_ERROR_ON(lhs_info.k0 != rhs_info.k0); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(((lhs_info.k0 & (lhs_info.k0 - 1)) && lhs_info.k0 != 3), "Only 2,3,4,8,16 are supported for k0"); + ARM_COMPUTE_RETURN_ERROR_ON(lhs_info.k0 > 16); + ARM_COMPUTE_RETURN_ERROR_ON(lhs_info.m0 < 2 || lhs_info.m0 > 8); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(((rhs_info.n0 & (rhs_info.n0 - 1)) && rhs_info.n0 != 3), "Only 2,3,4,8,16 are supported for n0"); + + const int m = gemm_info.m(); + const int n = gemm_info.n(); + const int k = gemm_info.k(); + + TensorShape tensor_shape0{ input0->tensor_shape() }; + tensor_shape0.set(0, k); + tensor_shape0.set(1, m); + + TensorShape tensor_shape1{ input1->tensor_shape() }; + tensor_shape1.set(0, n); + tensor_shape1.set(1, k); + + const TensorInfo tensor_info0 = input0->clone()->set_tensor_shape(tensor_shape0); + const TensorInfo tensor_info1 = input1->clone()->set_tensor_shape(tensor_shape1); + + const TensorInfo tensor_info_reshaped0 = input0->clone()->set_tensor_shape(compute_lhs_reshaped_shape(tensor_info0, lhs_info)); + const TensorInfo tensor_info_reshaped1 = input1->clone()->set_tensor_shape(compute_rhs_reshaped_shape(tensor_info1, rhs_info)); + + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_SHAPES(input0, &tensor_info_reshaped0); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_SHAPES(input1, &tensor_info_reshaped1); + + if(output->total_size() != 0) + { + const TensorInfo tensor_info_output = output->clone()->set_tensor_shape(compute_mm_shape(*input0, *input1, gemm_info)); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_SHAPES(output, &tensor_info_output); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::S32); + } + + return Status{}; +} + +std::pair<Status, Window> validate_and_configure_window(ITensorInfo *input0, ITensorInfo *input1, ITensorInfo *output, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info, + const GEMMReshapeInfo &gemm_info, ElementsProcessed &num_elements_processed) +{ + unsigned int &num_elems_processed_per_iteration_x = num_elements_processed[0]; + unsigned int &num_elems_processed_per_iteration_y = num_elements_processed[1]; + bool reinterpret_output_as_3d = (gemm_info.depth_output_gemm3d() != 0); + + Window win{}; + Window win_out{}; + bool window_changed = false; + + // Output tensor auto initialization if not yet initialized + auto_init_if_empty(*output, input0->clone()->set_tensor_shape(compute_mm_shape(*input0, *input1, gemm_info)).set_data_type(DataType::S32)); + + TensorInfo tmp_info(*output); + + if(reinterpret_output_as_3d) + { + // Since the output tensor has to be reinterpreted as 3D and the execute window is based on a 2D GEMM, + // the window needs to be constructed on the 2D collapsed version of the tensor + TensorShape tmp_shape(output->tensor_shape()); + tmp_shape.collapse(2U, 1U); + tmp_info.set_tensor_shape(tmp_shape); + } + + // Configure kernel window + num_elems_processed_per_iteration_x = rhs_info.n0; + num_elems_processed_per_iteration_y = lhs_info.m0; + + // Note: bottom paddings are calculated manually as the output can be reinterpreted as 3D tensor + // The only way to set properly the paddings, it is to set those explicitly through the AccessWindowStatic + const int m = gemm_info.m(); + const int bottom_pad = (num_elems_processed_per_iteration_y - (m % num_elems_processed_per_iteration_y)) % num_elems_processed_per_iteration_y; + + win = calculate_max_window(tmp_info, Steps(num_elems_processed_per_iteration_x, num_elems_processed_per_iteration_y)); + win_out = calculate_max_window(*output, Steps(num_elems_processed_per_iteration_x, num_elems_processed_per_iteration_y)); + + AccessWindowStatic input0_access(input0, 0, 0, + ceil_to_multiple(input0->dimension(0), num_elems_processed_per_iteration_y), + input0->dimension(1)); + AccessWindowStatic input1_access(input1, 0, 0, + ceil_to_multiple(input1->dimension(0), num_elems_processed_per_iteration_x), + input1->dimension(1)); + AccessWindowStatic output_access(output, 0, 0, + ceil_to_multiple(output->dimension(0), num_elems_processed_per_iteration_x), + output->dimension(1) + bottom_pad); + + window_changed = update_window_and_padding(win, input0_access, input1_access) || // window used by the execute_window_loop + update_window_and_padding(win_out, output_access); // window used to update the padding requirements of output tensor + + output_access.set_valid_region(win_out, ValidRegion(Coordinates(0, 0), output->tensor_shape())); + + // Collapse along the Z direction + // This collapse needs to be here in order to tune the Z dimension of LWS + Window collapsed = win; + const unsigned int dimension_to_collapse = std::min(static_cast<unsigned int>(output->num_dimensions()), 2u); + collapsed = win.collapse(win, dimension_to_collapse); + + Status err = (window_changed) ? ARM_COMPUTE_CREATE_ERROR(ErrorCode::RUNTIME_ERROR, "Insufficient Padding!") : Status{}; + return std::make_pair(err, collapsed); +} +} // namespace + +CLGEMMLowpMatrixMultiplyReshapedKernel::CLGEMMLowpMatrixMultiplyReshapedKernel() + : _input0(nullptr), _input1(nullptr), _output(nullptr), _slide_matrix_b(true), _reinterpret_output_as_3d(false), _k(1) +{ +} + +void CLGEMMLowpMatrixMultiplyReshapedKernel::configure(const ICLTensor *input0, const ICLTensor *input1, ICLTensor *output, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info, + const GEMMReshapeInfo &gemm_info) +{ + ARM_COMPUTE_ERROR_ON_NULLPTR(input0, input1, output); + + ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input0->info(), input1->info(), output->info(), lhs_info, rhs_info, gemm_info)); + + _input0 = input0; + _input1 = input1; + _output = output; + _reinterpret_output_as_3d = (gemm_info.depth_output_gemm3d() != 0); + _k = gemm_info.k(); + + // Check if we need to slide the matrix B + const unsigned int num_dimensions_input0 = _input0->info()->num_dimensions(); + _slide_matrix_b = (_input1->info()->num_dimensions() >= num_dimensions_input0); + + ElementsProcessed num_elements_processed{}; + + // Configure kernel window + auto win_config = validate_and_configure_window(input0->info(), input1->info(), output->info(), lhs_info, rhs_info, gemm_info, num_elements_processed); + ARM_COMPUTE_ERROR_THROW_ON(win_config.first); + ICLKernel::configure_internal(win_config.second); + + // Create build options + CLBuildOptions build_opts; + build_opts.add_option_if(_reinterpret_output_as_3d, "-DREINTERPRET_OUTPUT_AS_3D"); + build_opts.add_option_if(_reinterpret_output_as_3d, "-DHEIGHT_GEMM3D=" + support::cpp11::to_string(output->info()->dimension(1))); + build_opts.add_option_if(_reinterpret_output_as_3d, "-DDEPTH_GEMM3D=" + support::cpp11::to_string(output->info()->dimension(2))); + build_opts.add_option_if(!_slide_matrix_b, "-DMATRIX_B_DEPTH=" + support::cpp11::to_string(input1->info()->dimension(2))); + build_opts.add_option_if(lhs_info.interleave, "-DLHS_INTERLEAVE"); + build_opts.add_option_if(rhs_info.interleave, "-DRHS_INTERLEAVE"); + build_opts.add_option("-DM0=" + support::cpp11::to_string(lhs_info.m0)); + build_opts.add_option("-DN0=" + support::cpp11::to_string(rhs_info.n0)); + build_opts.add_option("-DK0=" + support::cpp11::to_string(lhs_info.k0)); + build_opts.add_option("-DV0=" + support::cpp11::to_string(lhs_info.v0)); + build_opts.add_option("-DH0=" + support::cpp11::to_string(rhs_info.h0)); + + std::string kernel_name("gemmlowp_mm_reshaped_"); + kernel_name += lhs_info.transpose ? "lhs_t_" : "lhs_nt_"; + kernel_name += rhs_info.transpose ? "rhs_t" : "rhs_nt"; + kernel_name += dot8_supported(CLKernelLibrary::get().get_device()) ? "_dot8" : ""; + + // Create kernel + _kernel = static_cast<cl::Kernel>(CLKernelLibrary::get().create_kernel(kernel_name, build_opts.options())); + + // Set config_id for enabling LWS tuning + _config_id = kernel_name; + _config_id += "_"; + _config_id += (_reinterpret_output_as_3d ? "3do_" : ""); + _config_id += support::cpp11::to_string(output->info()->dimension(1)); + _config_id += "_"; + _config_id += support::cpp11::to_string(output->info()->dimension(0)); + _config_id += "_"; + _config_id += support::cpp11::to_string(gemm_info.k()); + _config_id += "_"; + _config_id += support::cpp11::to_string(output->info()->dimension(2)); + _config_id += "_"; + _config_id += support::cpp11::to_string(lhs_info.m0); + _config_id += "_"; + _config_id += support::cpp11::to_string(rhs_info.n0); + _config_id += "_"; + _config_id += support::cpp11::to_string(lhs_info.k0); + _config_id += "_"; + _config_id += support::cpp11::to_string(lhs_info.v0); + _config_id += "_"; + _config_id += support::cpp11::to_string(rhs_info.h0); + _config_id += "_"; + _config_id += support::cpp11::to_string(lhs_info.interleave); + _config_id += "_"; + _config_id += support::cpp11::to_string(rhs_info.interleave); +} + +Status CLGEMMLowpMatrixMultiplyReshapedKernel::validate(const ITensorInfo *input0, const ITensorInfo *input1, const ITensorInfo *output, const GEMMLHSMatrixInfo &lhs_info, + const GEMMRHSMatrixInfo &rhs_info, const GEMMReshapeInfo &gemm_info) +{ + ElementsProcessed num_elements_processed{}; + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input0, input1, output, lhs_info, rhs_info, gemm_info)); + ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window(input0->clone().get(), + input1->clone().get(), + output->clone().get(), + lhs_info, + rhs_info, + gemm_info, + num_elements_processed) + .first); + + return Status{}; +} + +void CLGEMMLowpMatrixMultiplyReshapedKernel::run(const Window &window, cl::CommandQueue &queue) +{ + ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); + ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICLKernel::window(), window); + + if(_input1->info()->num_dimensions() < 3) + { + // The stride_z for matrix B must be zero if we do not slice + ARM_COMPUTE_ERROR_ON(_input1->info()->strides_in_bytes()[3] != 0); + } + + Window slice = window.first_slice_window_3D(); + Window slice_matrix_b = slice; + + slice_matrix_b.set(Window::DimX, Window::Dimension(0, 1, 1)); + slice_matrix_b.set(Window::DimY, Window::Dimension(0, 1, 1)); + + if(_reinterpret_output_as_3d) + { + // Pass bottom paddings to the kernel if the output has to be reinterpreted as 3D tensor + const unsigned int idx0 = 3 * num_arguments_per_2D_tensor() + 4; + const unsigned int total_cross_plane_pad = _output->info()->padding().top + _output->info()->padding().bottom; + _kernel.setArg<cl_uint>(idx0, static_cast<unsigned int>(total_cross_plane_pad)); + } + + do + { + Window slice_b = slice; + // Don't slice matrix B along the z dimension if matrix B has just 2 dimensions and matrix A more than 2 + // This scenario can happen when the matrix multiplication is used to perform a convolution operation + if(!_slide_matrix_b) + { + slice_b = slice_matrix_b; + } + + unsigned int idx = 0; + add_2D_tensor_argument(idx, _input0, slice); + add_2D_tensor_argument(idx, _input1, slice_b); + add_2D_tensor_argument(idx, _output, slice); + _kernel.setArg<cl_uint>(idx++, static_cast<unsigned int>(_k)); + _kernel.setArg<cl_uint>(idx++, static_cast<unsigned int>(_input0->info()->strides_in_bytes()[2])); + _kernel.setArg<cl_uint>(idx++, static_cast<unsigned int>(_input1->info()->strides_in_bytes()[2])); + _kernel.setArg<cl_uint>(idx++, static_cast<unsigned int>(_output->info()->strides_in_bytes()[2])); + enqueue(queue, *this, slice, lws_hint()); + } + while(window.slide_window_slice_3D(slice)); +}
\ No newline at end of file diff --git a/src/runtime/CL/functions/CLGEMMLowpMatrixMultiplyCore.cpp b/src/runtime/CL/functions/CLGEMMLowpMatrixMultiplyCore.cpp index 4b72878b5f..2a01db7824 100644 --- a/src/runtime/CL/functions/CLGEMMLowpMatrixMultiplyCore.cpp +++ b/src/runtime/CL/functions/CLGEMMLowpMatrixMultiplyCore.cpp @@ -31,43 +31,25 @@ #include "arm_compute/core/Validate.h" #include "arm_compute/core/utils/misc/ShapeCalculator.h" #include "arm_compute/runtime/CL/CLScheduler.h" +#include "arm_compute/runtime/CL/gemm_reshaped/CLGEMMReshapedConfiguration.h" namespace arm_compute { using namespace arm_compute::misc::shape_calculator; +using namespace arm_compute::cl_gemm; namespace { -inline bool is_interleaved_transposed(int m, int n, int k, bool reshape_b_only_on_first_run, GPUTarget gpu_target) +inline bool is_gemm_reshaped(unsigned int m, bool reshape_b_only_on_first_run, GPUTarget gpu_target) { - bool flag = true; - - if(gpu_target_is_in(gpu_target, - GPUTarget::G71, GPUTarget::G72, - GPUTarget::G51, GPUTarget::G51BIG, GPUTarget::G51LIT)) - { - // COMPMID-852 - if(k > 256 && m > 4 && reshape_b_only_on_first_run) - { - flag = ((0.72f + n * 0.10766f) < (n * 0.1284f)); - } - else - { - flag = false; - } - } - else - { - flag = m > 1; - } - - return flag; + return (get_arch_from_target(gpu_target) != GPUTarget::MIDGARD) && (m > 1) && (reshape_b_only_on_first_run); } } // namespace CLGEMMLowpMatrixMultiplyCore::CLGEMMLowpMatrixMultiplyCore(std::shared_ptr<IMemoryManager> memory_manager) : _memory_group(std::move(memory_manager)), _mm_kernel(), + _mm_reshaped_kernel(), _mtx_a_reshape_kernel(), _mtx_b_reshape_kernel(), _mtx_a_reduction_kernel(), @@ -82,7 +64,7 @@ CLGEMMLowpMatrixMultiplyCore::CLGEMMLowpMatrixMultiplyCore(std::shared_ptr<IMemo _original_b(nullptr), _a_offset(0), _b_offset(0), - _is_interleaved_transposed(true), + _is_gemm_reshaped(true), _reshape_b_only_on_first_run(false), _is_prepared(false), _fuse_output_stage(false) @@ -115,29 +97,17 @@ void CLGEMMLowpMatrixMultiplyCore::configure(const ICLTensor *a, const ICLTensor // Arguments used by GEMMReshapeInfo // If we pass the matrix A and matrix B reshaped to CLGEMMMatrixMultiplyKernel, we need to pass m, n, k, mult_transpose1xW_width and mult_interleave4x4_height to CLGEMMReshapeInfo // in order to know how the matrices have been reshaped - bool reinterpret_input_as_3d = gemm_info.reinterpret_input_as_3d(); - const bool unroll_block = dot8_supported(CLKernelLibrary::get().get_device()); - const int m = reinterpret_input_as_3d ? (a->info()->dimension(1) * a->info()->dimension(2)) : a->info()->dimension(1); - const int n = b->info()->dimension(0); - const int k = a->info()->dimension(0); - const int depth_output_gemm3d = gemm_info.depth_output_gemm3d(); - constexpr int mult_transpose1xW_width = 1; - constexpr int mult_interleave4x4_height = 1; - rhs_info.n0 = 16 / b->info()->element_size(); - rhs_info.k0 = 1; - rhs_info.h0 = mult_transpose1xW_width; - rhs_info.interleave = false; - rhs_info.transpose = false; - lhs_info.m0 = 4; - lhs_info.k0 = 4; - lhs_info.v0 = mult_interleave4x4_height; - lhs_info.interleave = true; - lhs_info.transpose = !unroll_block; + bool reinterpret_input_as_3d = gemm_info.reinterpret_input_as_3d(); + const unsigned int m = reinterpret_input_as_3d ? (a->info()->dimension(1) * a->info()->dimension(2)) : a->info()->dimension(1); + const unsigned int n = b->info()->dimension(0); + const unsigned int k = a->info()->dimension(0); + const unsigned int batch_size = reinterpret_input_as_3d ? a->info()->dimension(3) : a->info()->dimension(2); + const int depth_output_gemm3d = gemm_info.depth_output_gemm3d(); // Check if we need to reshape the matrix A and matrix B - _is_interleaved_transposed = is_interleaved_transposed(m, n, k, _reshape_b_only_on_first_run, gpu_target); + _is_gemm_reshaped = is_gemm_reshaped(m, _reshape_b_only_on_first_run, gpu_target); - if(_is_interleaved_transposed) + if(_is_gemm_reshaped) { // if _is_interleaved_transposed is set, force reinterpret_input_as_3d to be false as the output of CLGEMMInterleaveKernel will be 2D reinterpret_input_as_3d = false; @@ -151,6 +121,9 @@ void CLGEMMLowpMatrixMultiplyCore::configure(const ICLTensor *a, const ICLTensor _memory_group.manage(&_tmp_b); } + // Pick up the GEMM configuration + std::tie(lhs_info, rhs_info) = CLGEMMReshapedConfigurationFactory::create()->configure(m, n, k, batch_size, DataType::QASYMM8); + // Configure interleave kernel _mtx_a_reshape_kernel.configure(a, &_tmp_a, lhs_info, gemm_info.reinterpret_input_as_3d()); @@ -190,10 +163,16 @@ void CLGEMMLowpMatrixMultiplyCore::configure(const ICLTensor *a, const ICLTensor _memory_group.manage(&_mm_result_s32); - // Configure matrix multiply kernel - _mm_kernel.configure(matrix_a, matrix_b, &_mm_result_s32, _is_interleaved_transposed, GEMMReshapeInfo(m, n, k, - mult_transpose1xW_width, mult_interleave4x4_height, - depth_output_gemm3d, reinterpret_input_as_3d)); + if(_is_gemm_reshaped) + { + // Configure and tune matrix multiply kernel + _mm_reshaped_kernel.configure(matrix_a, matrix_b, &_mm_result_s32, lhs_info, rhs_info, GEMMReshapeInfo(m, n, k, 1, 1, depth_output_gemm3d, reinterpret_input_as_3d)); + } + else + { + // Configure matrix multiply kernel + _mm_kernel.configure(matrix_a, matrix_b, &_mm_result_s32, false, GEMMReshapeInfo(m, n, k, 1, 1, depth_output_gemm3d, reinterpret_input_as_3d)); + } // Configure offset contribution kernel _offset_contribution_output_stage_kernel.configure(&_mm_result_s32, _a_offset == 0 ? nullptr : &_vector_sum_col, _b_offset == 0 ? nullptr : &_vector_sum_row, c, output, a->info()->dimension(0), @@ -203,17 +182,23 @@ void CLGEMMLowpMatrixMultiplyCore::configure(const ICLTensor *a, const ICLTensor } else { - // Configure matrix multiply kernel - _mm_kernel.configure(matrix_a, matrix_b, output, _is_interleaved_transposed, GEMMReshapeInfo(m, n, k, - mult_transpose1xW_width, mult_interleave4x4_height, - depth_output_gemm3d, reinterpret_input_as_3d)); + if(_is_gemm_reshaped) + { + // Configure and tune matrix multiply kernel + _mm_reshaped_kernel.configure(matrix_a, matrix_b, output, lhs_info, rhs_info, GEMMReshapeInfo(m, n, k, 1, 1, depth_output_gemm3d, reinterpret_input_as_3d)); + } + else + { + // Configure matrix multiply kernel + _mm_kernel.configure(matrix_a, matrix_b, output, false, GEMMReshapeInfo(m, n, k, 1, 1, depth_output_gemm3d, reinterpret_input_as_3d)); + } // Configure offset contribution kernel _offset_contribution_kernel.configure(output, _a_offset == 0 ? nullptr : &_vector_sum_col, _b_offset == 0 ? nullptr : &_vector_sum_row, c, a->info()->dimension(0), _a_offset, _b_offset); } // Allocate tensors - if(_is_interleaved_transposed) + if(_is_gemm_reshaped) { _tmp_a.allocator()->allocate(); if(!_reshape_b_only_on_first_run) @@ -251,26 +236,14 @@ Status CLGEMMLowpMatrixMultiplyCore::validate(const ITensorInfo *a, const ITenso GEMMRHSMatrixInfo rhs_info; GEMMLHSMatrixInfo lhs_info; - bool reinterpret_input_as_3d = gemm_info.reinterpret_input_as_3d(); - const bool unroll_block = dot8_supported(CLKernelLibrary::get().get_device()); - const int m = reinterpret_input_as_3d ? (a->dimension(1) * a->dimension(2)) : a->dimension(1); - const int n = b->dimension(0); - const int k = a->dimension(0); - constexpr int mult_transpose1xW_width = 1; - constexpr int mult_interleave4x4_height = 1; - const int depth_output_gemm3d = gemm_info.depth_output_gemm3d(); - rhs_info.n0 = 16 / b->element_size(); - rhs_info.k0 = 1; - rhs_info.h0 = mult_transpose1xW_width; - rhs_info.interleave = false; - rhs_info.transpose = false; - lhs_info.m0 = 4; - lhs_info.k0 = 4; - lhs_info.v0 = mult_interleave4x4_height; - lhs_info.interleave = true; - lhs_info.transpose = !unroll_block; - - bool reshape_matrices = is_interleaved_transposed(m, n, k, gemm_info.reshape_b_only_on_first_run(), CLScheduler::get().target()); + bool reinterpret_input_as_3d = gemm_info.reinterpret_input_as_3d(); + const unsigned int m = reinterpret_input_as_3d ? (a->dimension(1) * a->dimension(2)) : a->dimension(1); + const unsigned int n = b->dimension(0); + const unsigned int k = a->dimension(0); + const unsigned int batch_size = reinterpret_input_as_3d ? a->dimension(3) : a->dimension(2); + const int depth_output_gemm3d = gemm_info.depth_output_gemm3d(); + + bool reshape_matrices = is_gemm_reshaped(m, gemm_info.reshape_b_only_on_first_run(), CLScheduler::get().target()); // if reshape_matrices is set, force reinterpret_input_as_3d to be false as the output of CLGEMMInterleaveKernel will be 2D if(reshape_matrices) @@ -278,13 +251,16 @@ Status CLGEMMLowpMatrixMultiplyCore::validate(const ITensorInfo *a, const ITenso reinterpret_input_as_3d = false; } - const GEMMReshapeInfo reshape_info = GEMMReshapeInfo(m, n, k, mult_transpose1xW_width, mult_interleave4x4_height, depth_output_gemm3d, reinterpret_input_as_3d); + const GEMMReshapeInfo reshape_info = GEMMReshapeInfo(m, n, k, 1, 1, depth_output_gemm3d, reinterpret_input_as_3d); if(reshape_matrices) { matrix_a_info = &tmp_a_info; matrix_b_info = &tmp_b_info; + // Pick up the GEMM configuration + std::tie(lhs_info, rhs_info) = CLGEMMReshapedConfigurationFactory::create()->configure(m, n, k, batch_size, DataType::QASYMM8); + // Validate interleave kernel auto_init_if_empty(tmp_a_info, a->clone()->set_tensor_shape(compute_lhs_reshaped_shape(*a, lhs_info, gemm_info.reinterpret_input_as_3d()))); ARM_COMPUTE_RETURN_ON_ERROR(CLGEMMReshapeLHSMatrixKernel::validate(a, &tmp_a_info, lhs_info, gemm_info.reinterpret_input_as_3d())); @@ -319,12 +295,22 @@ Status CLGEMMLowpMatrixMultiplyCore::validate(const ITensorInfo *a, const ITenso { TensorInfo mm_result_s32_info{}; - // Output tensor auto inizialitation if not yet initialized - auto_init_if_empty(mm_result_s32_info, a->clone()->set_tensor_shape(compute_mm_shape(*matrix_a_info, *matrix_b_info, reshape_matrices, reshape_info)).set_data_type(DataType::S32)); + if(reshape_matrices) + { + // Output tensor auto inizialitation if not yet initialized + auto_init_if_empty(mm_result_s32_info, a->clone()->set_tensor_shape(compute_mm_shape(*matrix_a_info, *matrix_b_info, reshape_info)).set_data_type(DataType::S32)); - // Validate matrix multiply - ARM_COMPUTE_RETURN_ON_ERROR(CLGEMMLowpMatrixMultiplyKernel::validate(matrix_a_info, matrix_b_info, &mm_result_s32_info, reshape_matrices, reshape_info)); + // Validate matrix multiply + ARM_COMPUTE_RETURN_ON_ERROR(CLGEMMLowpMatrixMultiplyReshapedKernel::validate(matrix_a_info, matrix_b_info, &mm_result_s32_info, lhs_info, rhs_info, reshape_info)); + } + else + { + // Output tensor auto inizialitation if not yet initialized + auto_init_if_empty(mm_result_s32_info, a->clone()->set_tensor_shape(compute_mm_shape(*matrix_a_info, *matrix_b_info, false, reshape_info)).set_data_type(DataType::S32)); + // Validate matrix multiply + ARM_COMPUTE_RETURN_ON_ERROR(CLGEMMLowpMatrixMultiplyKernel::validate(matrix_a_info, matrix_b_info, &mm_result_s32_info, false, reshape_info)); + } // Validate offset contribution kernel ARM_COMPUTE_RETURN_ON_ERROR(CLGEMMLowpOffsetContributionOutputStageKernel::validate(&mm_result_s32_info, a_offset == 0 ? nullptr : &info_vector_sum_col, @@ -336,9 +322,16 @@ Status CLGEMMLowpMatrixMultiplyCore::validate(const ITensorInfo *a, const ITenso } else { - // Validate matrix multiply - ARM_COMPUTE_RETURN_ON_ERROR(CLGEMMLowpMatrixMultiplyKernel::validate(matrix_a_info, matrix_b_info, output, reshape_matrices, reshape_info)); - + if(reshape_matrices) + { + // Validate matrix multiply + ARM_COMPUTE_RETURN_ON_ERROR(CLGEMMLowpMatrixMultiplyReshapedKernel::validate(matrix_a_info, matrix_b_info, output, lhs_info, rhs_info, reshape_info)); + } + else + { + // Validate matrix multiply + ARM_COMPUTE_RETURN_ON_ERROR(CLGEMMLowpMatrixMultiplyKernel::validate(matrix_a_info, matrix_b_info, output, false, reshape_info)); + } // Validate offset contribution kernel ARM_COMPUTE_RETURN_ON_ERROR(CLGEMMLowpOffsetContributionKernel::validate(output, a_offset == 0 ? nullptr : &info_vector_sum_col, @@ -356,7 +349,7 @@ void CLGEMMLowpMatrixMultiplyCore::run() _memory_group.acquire(); - if(_is_interleaved_transposed) + if(_is_gemm_reshaped) { // Run reshape matrix A CLScheduler::get().enqueue(_mtx_a_reshape_kernel, false); @@ -375,7 +368,14 @@ void CLGEMMLowpMatrixMultiplyCore::run() } // Run matrix multiply - CLScheduler::get().enqueue(_mm_kernel, false); + if(_is_gemm_reshaped) + { + CLScheduler::get().enqueue(_mm_reshaped_kernel, false); + } + else + { + CLScheduler::get().enqueue(_mm_kernel, false); + } // Run matrix A reduction kernel only if _b_offset is not equal to 0 if(_b_offset != 0) @@ -401,7 +401,7 @@ void CLGEMMLowpMatrixMultiplyCore::prepare() { if(!_is_prepared) { - if(_is_interleaved_transposed && _reshape_b_only_on_first_run) + if(_is_gemm_reshaped && _reshape_b_only_on_first_run) { ARM_COMPUTE_ERROR_ON(!_original_b->is_used()); diff --git a/src/runtime/CL/gemm_reshaped/CLGEMMReshapedConfigurationBifrost.cpp b/src/runtime/CL/gemm_reshaped/CLGEMMReshapedConfigurationBifrost.cpp index 079a52e61c..cd97849712 100644 --- a/src/runtime/CL/gemm_reshaped/CLGEMMReshapedConfigurationBifrost.cpp +++ b/src/runtime/CL/gemm_reshaped/CLGEMMReshapedConfigurationBifrost.cpp @@ -32,18 +32,62 @@ namespace arm_compute { namespace cl_gemm { +namespace +{ +std::pair<GEMMLHSMatrixInfo, GEMMRHSMatrixInfo> configure_gemm_reshaped(unsigned int m, unsigned int n, unsigned int m0, unsigned int n0, unsigned int k0, unsigned int v0, unsigned int h0, + bool lhs_interleave, bool rhs_interleave) +{ + GEMMLHSMatrixInfo lhs_info; + GEMMRHSMatrixInfo rhs_info; + + // Configure GEMMLHSMatrixInfo + lhs_info.m0 = m0; + lhs_info.k0 = k0; + lhs_info.v0 = ((m / (lhs_info.m0 * v0)) == 0) ? 1 : v0; + lhs_info.interleave = lhs_interleave; + lhs_info.transpose = false; + + // Configure GEMMRHSMatrixInfo + rhs_info.n0 = n0; + rhs_info.k0 = lhs_info.k0; + rhs_info.h0 = ((n / (rhs_info.n0 * h0)) == 0) ? 1 : h0; + rhs_info.interleave = rhs_interleave; + rhs_info.transpose = true; + + return std::make_pair(lhs_info, rhs_info); +} + +} // namespace + std::pair<GEMMLHSMatrixInfo, GEMMRHSMatrixInfo> CLGEMMReshapedConfigurationBifrost::configure(unsigned int m, unsigned int n, unsigned int k, unsigned int b, DataType data_type) { - ARM_COMPUTE_ERROR_ON(data_type != DataType::F32); + ARM_COMPUTE_ERROR_ON(data_type != DataType::F32 && data_type != DataType::QASYMM8); ARM_COMPUTE_UNUSED(data_type); const GPUTarget gpu_target = CLScheduler::get().target(); + + using ConfigurationFunctionExecutorPtr = std::pair<GEMMLHSMatrixInfo, GEMMRHSMatrixInfo> (CLGEMMReshapedConfigurationBifrost::*)(unsigned int m, unsigned int n, unsigned int k, unsigned int b); + + // Configurations for Mali-G76 + static std::map<DataType, ConfigurationFunctionExecutorPtr> gemm_reshaped_configs_G76 = + { + { DataType::F32, &CLGEMMReshapedConfigurationBifrost::configure_G76_f32 }, + { DataType::QASYMM8, &CLGEMMReshapedConfigurationBifrost::configure_G76_u8 } + }; + + // Configurations for Mali-G7x + static std::map<DataType, ConfigurationFunctionExecutorPtr> gemm_reshaped_configs_G7x = + { + { DataType::F32, &CLGEMMReshapedConfigurationBifrost::configure_G7x_f32 }, + { DataType::QASYMM8, &CLGEMMReshapedConfigurationBifrost::configure_G7x_u8 } + }; + switch(gpu_target) { case GPUTarget::G76: - return configure_G76_f32(m, n, k, b); + return (this->*gemm_reshaped_configs_G76[data_type])(m, n, k, b); default: - return configure_G7x_f32(m, n, k, b); + return (this->*gemm_reshaped_configs_G7x[data_type])(m, n, k, b); } } @@ -52,43 +96,43 @@ std::pair<GEMMLHSMatrixInfo, GEMMRHSMatrixInfo> CLGEMMReshapedConfigurationBifro ARM_COMPUTE_UNUSED(k); ARM_COMPUTE_UNUSED(b); - GEMMLHSMatrixInfo lhs_info; - GEMMRHSMatrixInfo rhs_info; - if(n <= 4) { - // Configure GEMMLHSMatrixInfo - lhs_info.m0 = 4; - lhs_info.k0 = 8; - lhs_info.v0 = lhs_info.m0 * 16 < m ? 2 : 16; - lhs_info.interleave = true; - lhs_info.transpose = false; - - // Configure GEMMRHSMatrixInfo - rhs_info.n0 = 2; - rhs_info.k0 = lhs_info.k0; - rhs_info.h0 = rhs_info.n0 * 16 < n ? 2 : 16; - rhs_info.interleave = false; - rhs_info.transpose = true; + return configure_gemm_reshaped(m, n, 4, 2, 8, 16, 16, true, false); } else { - // Configure GEMMLHSMatrixInfo - lhs_info.m0 = 5; - lhs_info.k0 = 4; - lhs_info.v0 = lhs_info.m0 * 2 < m ? 1 : 2; - lhs_info.interleave = false; - lhs_info.transpose = false; - - // Configure GEMMRHSMatrixInfo - rhs_info.n0 = 4; - rhs_info.k0 = lhs_info.k0; - rhs_info.h0 = rhs_info.n0 * 16 < n ? 2 : 16; - rhs_info.interleave = true; - rhs_info.transpose = true; + return configure_gemm_reshaped(m, n, 5, 4, 4, 2, 16, false, true); } +} - return std::make_pair(lhs_info, rhs_info); +std::pair<GEMMLHSMatrixInfo, GEMMRHSMatrixInfo> CLGEMMReshapedConfigurationBifrost::configure_G7x_u8(unsigned int m, unsigned int n, unsigned int k, unsigned int b) +{ + ARM_COMPUTE_UNUSED(k); + ARM_COMPUTE_UNUSED(b); + + if(dot8_supported(CLKernelLibrary::get().get_device())) + { + if(n <= 4) + { + return configure_gemm_reshaped(m, n, 4, 2, 16, 2, 2, true, false); + } + else + { + return configure_gemm_reshaped(m, n, 4, 4, 16, 2, 2, true, false); + } + } + else + { + if(n <= 4) + { + return configure_gemm_reshaped(m, n, 4, 2, 8, 2, 2, true, false); + } + else + { + return configure_gemm_reshaped(m, n, 6, 4, 4, 2, 2, true, true); + } + } } std::pair<GEMMLHSMatrixInfo, GEMMRHSMatrixInfo> CLGEMMReshapedConfigurationBifrost::configure_G76_f32(unsigned int m, unsigned int n, unsigned int k, unsigned int b) @@ -96,43 +140,29 @@ std::pair<GEMMLHSMatrixInfo, GEMMRHSMatrixInfo> CLGEMMReshapedConfigurationBifro ARM_COMPUTE_UNUSED(k); ARM_COMPUTE_UNUSED(b); - GEMMLHSMatrixInfo lhs_info; - GEMMRHSMatrixInfo rhs_info; - if(n <= 4) { - // Configure GEMMLHSMatrixInfo - lhs_info.m0 = 4; - lhs_info.k0 = 8; - lhs_info.v0 = lhs_info.m0 * 16 < m ? 2 : 16; - lhs_info.interleave = true; - lhs_info.transpose = false; - - // Configure GEMMRHSMatrixInfo - rhs_info.n0 = 2; - rhs_info.k0 = lhs_info.k0; - rhs_info.h0 = rhs_info.n0 * 16 < n ? 2 : 16; - rhs_info.interleave = false; - rhs_info.transpose = true; + return configure_gemm_reshaped(m, n, 4, 2, 8, 16, 16, true, false); } else { - // Configure GEMMLHSMatrixInfo - lhs_info.m0 = 4; - lhs_info.k0 = 2; - lhs_info.v0 = lhs_info.m0 * 8 < m ? 2 : 8; - lhs_info.interleave = false; - lhs_info.transpose = false; - - // Configure GEMMRHSMatrixInfo - rhs_info.n0 = 4; - rhs_info.k0 = lhs_info.k0; - rhs_info.h0 = rhs_info.n0 * 16 < n ? 2 : 16; - rhs_info.interleave = false; - rhs_info.transpose = true; + return configure_gemm_reshaped(m, n, 4, 4, 2, 8, 16, false, false); } +} - return std::make_pair(lhs_info, rhs_info); +std::pair<GEMMLHSMatrixInfo, GEMMRHSMatrixInfo> CLGEMMReshapedConfigurationBifrost::configure_G76_u8(unsigned int m, unsigned int n, unsigned int k, unsigned int b) +{ + ARM_COMPUTE_UNUSED(k); + ARM_COMPUTE_UNUSED(b); + + if(n <= 4) + { + return configure_gemm_reshaped(m, n, 4, 2, 16, 4, 1, false, false); + } + else + { + return configure_gemm_reshaped(m, n, 4, 4, 16, 2, 2, false, true); + } } } // namespace cl_gemm } // namespace arm_compute
\ No newline at end of file |