/* * Copyright (c) 2017 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. */ #pragma once #include "arm_compute/core/NEON/kernels/convolution/common/alloc.hpp" #include "arm_compute/core/NEON/kernels/convolution/common/convolution.hpp" #include "gemm.hpp" #include "arm_compute/core/NEON/kernels/convolution/common/profiler.hpp" #include "arm_compute/core/NEON/kernels/convolution/common/shims.hpp" #include "arm_compute/core/NEON/kernels/convolution/common/tensor.hpp" #include "arm_compute/core/NEON/kernels/convolution/common/utils.hpp" #include #include #include // Generic Winograd implementation using GEMM namespace winograd { template class WinogradGEMM { public: // Information about the specific Winograd instance static constexpr int output_tile_rows = OutputTileRows; static constexpr int output_tile_cols = OutputTileCols; static constexpr int kernel_rows = KernelRows; static constexpr int kernel_cols = KernelCols; static constexpr int inner_tile_rows = output_tile_rows + kernel_rows - 1; // TODO Check static constexpr int inner_tile_cols = output_tile_cols + kernel_cols - 1; // TODO Check static constexpr int N_GEMMS = inner_tile_rows * inner_tile_cols; /** Transform weights from the spatial to the Winograd domain. */ template struct WeightsTransform { /** Get the bytes read during the transform. */ static inline size_t bytes_read(const KernelShape &shape) { return shape.size() * sizeof(T); } /** Get the bytes written during the transform. */ static inline size_t bytes_written(const KernelShape &shape) { const int inner_tile_size = inner_tile_rows * inner_tile_cols; return (inner_tile_size * shape.n_input_channels * shape.n_output_channels * sizeof(T)); } /** Get the count of operations performed by the transform. */ static int ops_performed(const KernelShape &shape); /** Apply the transform to a tensor. */ static void execute( const int n_output_channels, const int n_input_channels, const T* const input, T* const output, const int matrix_stride, const int matrix_row_stride ); /** Create a WeightsTransform operator fixed on a given problem and set * of pointers. */ WeightsTransform( const T* const input, T* const output, const int matrix_stride, /** Stride across matrices in the output. */ const int matrix_row_stride, /** Stride across rows of the matrix. */ const int n_output_channels, /** Number of filters. */ const int n_input_channels /** Number of channels in each filter. */ ); /** Get the window of work a given operator can perform. */ unsigned int get_window() const; /** Perform work upon a window of the input. */ void run(const unsigned int start, const unsigned int stop); private: const T* const inptr; /** Fixed pointer to input data. */ T* const outptr; /** Fixed pointer to output memory. */ const int matrix_stride; /** Stride between output matrices. */ const int matrix_row_stride; /** Stride within output matrices. */ const int n_output_channels; /** Number of filters. */ const int n_input_channels; /** Number of channels in each filter. */ }; /** Transform input feature maps from the spatial to the Winograd domain. */ template struct InputTransform { /** Get the bytes read during the transform. */ static size_t bytes_read(const Tensor4DShape &shape) { return shape.size() * sizeof(T); } /** Get the bytes written during the transform. */ static size_t bytes_written(const Tensor4DShape &shape) { const int M = iceildiv(shape.n_rows, inner_tile_rows) * iceildiv(shape.n_cols, inner_tile_cols); const int K = shape.n_channels; return inner_tile_rows * inner_tile_cols * M * K * sizeof(T); } /** Get the count of operations performed by the transform. */ static int ops_performed(const Tensor4DShape &shape); /** Apply the transform to a tensor. */ static void execute( const T *inptr, const Tensor4DShape& input_shape, const PaddingType padding_type, const int tile_M, const int tile_N, T *outptr_base, const int matrix_stride, const int matrix_batch_stride, const int matrix_row_stride ); /***********************************************************************/ /** Create an InputTransform operator fixed on a given problem and set of * pointers. */ InputTransform( const T* const input, /** Input tensor data */ const int n_batches, /** Number of batches in input tensor. */ const int n_rows, /** Number of rows in input tensor. */ const int n_cols, /** Number of columns in input tensor. */ const int n_channels, /** Number of channels in input tensor. */ const PaddingType padding, /** Padding type. */ T* const output, /** Base of output matrices. */ const int matrix_stride, /** Stride between output matrices. */ const int matrix_row_stride /** Stride within matrices. */ ); /** Get the winodw of work a given operator can perform. */ unsigned int get_window() const; /** Perform work upon a window of the input. */ void run(const unsigned int start, const unsigned int stop); /***********************************************************************/ private: static void process_tile_row( const int tile_N, int n_channels, const T* const input_base, const int input_row_stride, const int input_col_stride, T* const matrix_base, const int matrix_stride, const int matrix_row_stride, const int row_pad_top, const int row_pad_left, const int row_pad_bottom, const int n_cols ); static constexpr int max_pad_bottom = inner_tile_rows - 1; static constexpr int max_pad_right = inner_tile_cols - 1; /** Process a single tile of the input tensor. */ template static void process_tile(int, const T*, int, int, T*, int); // Array of methods to transform tiles of the input tensor. typedef void (*TileFn)(int, const T*, int, int, T*, int); static const TileFn tile_fns[2][2][max_pad_bottom][max_pad_right]; /* Member values for instance-based API. */ const T* const _inptr; T* const _outptr; const int _n_batches, _n_rows, _n_cols, _n_channels, _matrix_stride, _matrix_row_stride, _tiles_M, _tiles_N; const PaddingType _padding_type; }; /** Transform output feature maps from the Winograd to the spatial domain. */ template struct OutputTransform { /** Get the bytes read during the transform. */ static size_t bytes_read(const Tensor4DShape &shape); /** Get the bytes written during the transform. */ static size_t bytes_written(const Tensor4DShape &shape); /** Get the count of operations performed by the transform. */ static int ops_performed(const Tensor4DShape &shape); /** Apply the transform to create a tensor. */ static void execute( const Tensor4DShape &output_shape, const T* const matrix_base, const int matrix_stride, const int matrix_row_stride, const T* const biases, T* const output ); /***********************************************************************/ /** Create an OutputTransform operator fixed on a given problem and set * of pointers. */ OutputTransform( const T* const matrix_base, /** Pointer to base of matrices. */ const int matrix_stride, /** Stride between matrices. */ const int matrix_row_stride, /** Stride within a matrix. */ const T* const biases, /** Pointer to biases vector. */ T* const output, /** Pointer to output tensor. */ const int n_batches, /** Number of batches in output tensor. */ const int n_rows, /** Number of rows in output tensor. */ const int n_cols, /** Number of columns in output tensor. */ const int n_channels /** Number of channels in output tensor. */ ); /** Get the window of work a given operator can perform. */ unsigned int get_window() const; /** Perform work upon a window of the input. */ void run(const unsigned int start, const unsigned int stop); /***********************************************************************/ private: static void process_tile_row( const int tile_N, const int n_channels, const T* const matrix_base, const int matrix_stride, const int matrix_row_stride, const T* const biases, T* const output, const int output_row_stride, const int output_col_stride, const int row_pad_bottom, const int row_pad_right ); // Limits on the amount of anti-padding to be applied static constexpr int max_pad_bottom = output_tile_rows; static constexpr int max_pad_right = output_tile_cols; /** Prepare a single tile of the output tensor. */ template static void process_tile(int, const T*, int, const T*, T*, int, int); // Array of methods to produce tiles of output tensor. typedef void (*TileFn)(int, const T*, int, const T*, T*, int, int); static const TileFn tile_fns[max_pad_bottom][max_pad_right]; /** Member constants for instances of the transform. */ const T* const _matrix_base; const T* const _biases; const int _matrix_stride, _matrix_row_stride; T* const _outptr; const int _n_batches, _n_rows, _n_cols, _n_channels, _tile_M, _tile_N; }; /** Perform a convolution. */ template class Convolution { public: // Information about the typed Winograd instance typedef TOut OutputType; typedef TIn InputType; /** Create a new Winograd operator. */ Convolution( const KernelShape &kernel_shape, const Tensor4DShape &input_shape, const PaddingType padding, void *kernel_storage=NULL ); Convolution(const Convolution&) = delete; Convolution operator=(const Convolution&) = delete; /** Create a new Winograd operator and initialise the weights. */ Convolution( const KernelShape &kernel_shape, const Tensor4DShape &input_shape, const PaddingType padding, const TIn* const kernel, void *kernel_storage=NULL, void *transform_working_space=NULL ); /** Clean up a convolution engine. */ ~Convolution(); /** Transform the weights into the Winograd domain. */ template > void transform_weights( const TIn* const kernel, void *transform_working_space=NULL ); /* Apply the Winograd operator to some input. */ void execute( TOut* const output, const TIn* const input, const TOut* const biases, void* working_space=NULL, const int n_threads=1 ); /* Apply the Winograd operator to some input. */ void execute( TOut* const output, const TIn* const input, const TOut* const biases, const int n_threads ); /** Get the output shape of a convolution. */ static Tensor4DShape get_output_shape( const KernelShape &kernel_shape, const Tensor4DShape &in_shape, const PaddingType padding ); /* Get the memory required to transform the kernel. */ static size_t get_kernel_transform_working_size(const KernelShape &shape); /** Get the memory required to store the kernel transformed into the * Winograd domain. */ static size_t get_kernel_storage_size(const KernelShape &shape); /** Get the memory required to store the input tensor transformed into * the Winograd domain. */ static size_t get_input_storage_size( const KernelShape &kernel_shape, const Tensor4DShape &input_shape, const PaddingType padding_type ); /** Get the memory required to store the output tensor in the Winograd * domain. */ static size_t get_output_storage_size( const KernelShape &kernel_shape, const Tensor4DShape &input_shape, const PaddingType padding_type ); /** Get the memory required to apply a Winograd operator to some input. */ static size_t get_working_space_size( const KernelShape &kernel_shape, const Tensor4DShape &input_shape, const PaddingType padding_type ); /* Get the memory required by a single "input" matrix. */ static size_t get_input_matrix_size( const KernelShape &kernel_shape, const Tensor4DShape &input_shape, const PaddingType padding_type ); static int get_input_matrix_stride( const KernelShape &kernel_shape, const Tensor4DShape &input_shape, const PaddingType padding_type ); /* Get the memory required by a single "output" matrix. */ static size_t get_output_matrix_size( const KernelShape &kernel_shape, const Tensor4DShape &input_shape, const PaddingType padding_type ); static int get_output_matrix_stride( const KernelShape &kernel_shape, const Tensor4DShape &input_shape, const PaddingType padding_type ); /* Get the memory required by a single "kernel" matrix. */ static size_t get_kernel_matrix_size(const KernelShape &shape); static int get_kernel_matrix_stride(const KernelShape &shape); static constexpr int M_BLOCK = 4; /** Size of block used by GEMM. */ static constexpr int N_BLOCK = 16; /** Size of block used by GEMM. */ private: const KernelShape kernel_shape; /** Shape of the kernel to be applied. */ TIn *kernel_matrices[N_GEMMS]; /** Pointers into the kernel matrices. */ const int kernel_matrix_row_stride; /** Stride within the kernel matrices. */ const bool manage_kernel_storage; /** Kernel storage is managed by the instance. */ void* const _kernel_storage; /** Base pointer for kernel storage. */ const Tensor4DShape input_shape; /** Shape of the input tensor. */ const PaddingType padding; /** Padding applied by the operator. */ const Tensor4DShape output_shape; /** Output shape produced by the operator. */ const int tile_rows; /** Number of rows of tiles. */ const int tile_cols; /** Number of columns of tiles. */ const int M, K, N; /** Sizes of underlying fundamental matrix multiplications. */ profiler prof; }; }; } // namespace winograd