COMPMID-2063: New Winograd implementation

Refactoring of winograd code reducing the size of the binaries
about 8X.

Change-Id: If8845bda324573e1a5cf436f354ac8603e88a92e
Signed-off-by: Pablo Tello <pablo.tello@arm.com>
Reviewed-on: https://review.mlplatform.org/c/959
Comments-Addressed: Arm Jenkins <bsgcomp@arm.com>
Tested-by: Anthony Barbier <Anthony.barbier@arm.com>
Reviewed-by: Georgios Pinitas <georgios.pinitas@arm.com>
diff --git a/arm_compute/core/NEON/kernels/NEWinogradConvolutionLayerKernel.h b/arm_compute/core/NEON/kernels/NEWinogradConvolutionLayerKernel.h
index 9658005..f6b189c 100644
--- a/arm_compute/core/NEON/kernels/NEWinogradConvolutionLayerKernel.h
+++ b/arm_compute/core/NEON/kernels/NEWinogradConvolutionLayerKernel.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2018 ARM Limited.
+ * Copyright (c) 2017-2019 ARM Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -27,8 +27,7 @@
 #include "arm_compute/core/NEON/INEKernel.h"
 #include "arm_compute/core/NEON/kernels/convolution/common/convolution.hpp"
 #include "arm_compute/core/NEON/kernels/convolution/common/tensor.hpp"
-#include "arm_compute/core/NEON/kernels/convolution/winograd/batched_blocked_gemm.hpp"
-#include "arm_compute/core/NEON/kernels/convolution/winograd/winograd_gemm.hpp"
+#include "arm_compute/core/NEON/kernels/convolution/winograd/winograd_layer.hpp"
 
 namespace arm_compute
 {
@@ -39,6 +38,17 @@
 class INEWinogradLayerTransformInputKernel : public INEKernel
 {
 public:
+    /** Get the working space required to perform the transformation.
+     *
+     * Note, the working space is only required when performing the
+     * transformation - hence it can be reused whenever the transformation is
+     * not running.
+     *
+     * @param num_threads The greatest number of threads that will be used to execute the transform.
+     * @return Size of working space required in bytes.
+     */
+    virtual unsigned int get_working_space_size(unsigned int num_threads) const = 0;
+
     /** Determine how much memory (in units of TIn) to allocate for the
      * transformed input.
      *
@@ -72,9 +82,10 @@
      * @param[in]  padding       Padding type.
      * @param[out] output        Base of output matrices.
      * @param[in]  matrix_stride Stride between output matrices.
+     * @param[in]  workspace     Tensor to be used as the working space during the computation.
      */
     virtual void configure(const ITensor *input_nhwc, const int num_batches, const int num_rows, const int num_cols, const int num_channels,
-                           const PaddingType padding, ITensor *output, const int matrix_stride) = 0;
+                           const PaddingType padding, ITensor *output, const int matrix_stride, ITensor *workspace) = 0;
 
     /** Destructor */
     virtual ~INEWinogradLayerTransformInputKernel()
@@ -116,6 +127,18 @@
         int  num_cols,
         bool same_padding) const override;
 
+    /** Get the working space required to perform the transformation.
+     *
+     * Note, the working space is only required when performing the
+     * transformation - hence it can be reused whenever the transformation is
+     * not running.
+     *
+     * @param[in] num_threads The greatest number of threads that will be used to execute the transform.
+     *
+     * @return Size of working space required in bytes.
+     */
+    unsigned int get_working_space_size(unsigned int num_threads) const override;
+
     /** Gets the stride between matrices in the input worspace
      *
      * @param[in] kernel_shape The shape of the weights tensor.
@@ -144,6 +167,7 @@
      * @param[in]  padding       Padding type.
      * @param[out] output        Base of output matrices.
      * @param[in]  matrix_stride Stride between output matrices.
+     * @param[in]  workspace     Tensor to be used as the working space during the computation.
      */
     void configure(
         const ITensor    *input_nhwc,
@@ -153,13 +177,14 @@
         const int         num_channels,
         const PaddingType padding,
         ITensor          *output,
-        const int         matrix_stride) override;
+        const int         matrix_stride,
+        ITensor          *workspace) override;
 
     // Inherited methods overridden:
     void run(const Window &window, const ThreadInfo &info) override;
 
     /** Winograd base kernel */
-    using WinogradBase = winograd::WinogradGEMM<OutputTileRows, OutputTileCols, KernelRows, KernelCols>;
+    using WinogradBase = winograd::WinogradGEMM<OutputTileRows, OutputTileCols, KernelRows, KernelCols, winograd::WinogradRoots::Integers>;
     /** Winograd convolution kernel */
     using WinogradConv = typename WinogradBase::template Convolution<T, T>;
 
@@ -174,15 +199,22 @@
     static Status validate(const ITensorInfo *input, const ITensorInfo *output, const WinogradInfo &winograd_info);
 
 private:
-    using InputTransform = typename WinogradBase::template InputTransform<T>;
-    const ITensor *_input_nhwc;
-    int            _num_batches;   /**< Number of batches in input tensor. */
-    int            _num_rows;      /**< Number of rows in input tensor. */
-    int            _num_cols;      /**< Number of columns in input tensor. */
-    int            _num_channels;  /**< Number of channels in input tensor. */
-    PaddingType    _padding;       /**< Padding type. */
-    ITensor       *_output;        /**< Base of output matrices. */
-    int            _matrix_stride; /**< Stride between output matrices. */
+    using InputTransform = typename WinogradBase::template InputTransform<T, T>;
+
+    std::unique_ptr<InputTransform> _transform{ nullptr };
+    const ITensor                  *_input_nhwc;
+    int                             _num_batches;    /**< Number of batches in input tensor. */
+    int                             _num_rows;       /**< Number of rows in input tensor. */
+    int                             _num_cols;       /**< Number of columns in input tensor. */
+    int                             _num_channels;   /**< Number of channels in input tensor. */
+    PaddingType                     _padding;        /**< Padding type. */
+    ITensor                        *_output;         /**< Base of output matrices. */
+    int                             _matrix_stride;  /**< Stride between output matrices. */
+    int                             _padding_top;    /**< Padding to apply to the top of the image. */
+    int                             _padding_left;   /**< Padding to apply to the left of the image. */
+    int                             _padding_right;  /**< Padding to apply to the right of the image. */
+    int                             _padding_bottom; /**< Padding to apply to the bottom of the image. */
+    ITensor                        *_workspace;
 };
 
 /** Interface for the NEON kernel to perform Winograd output transform. */
@@ -190,6 +222,18 @@
 class INEWinogradLayerTransformOutputKernel : public INEKernel
 {
 public:
+    /** Get the working space required to perform the transformation.
+     *
+     * Note, the working space is only required when performing the
+     * transformation - hence it can be reused whenever the transformation is
+     * not running.
+     *
+     * @param[in] num_threads The greatest number of threads that will be used to execute the transform.
+     *
+     * @return Size of working space required in bytes.
+     */
+    virtual unsigned int get_working_space_size(unsigned int num_threads) const = 0;
+
     /** Determine how much memory (in units of TOut) to allocate for the
      * (Winograd domain) output.
      *
@@ -225,24 +269,26 @@
 
     /** Configure the output transform kernel.
      *
-     * @param[in]  biases              Pointer to the biases tensor.
-     * @param[in]  output_workingspace Pointer to working space for the output tensor in the Winograd domain.
-     * @param[in]  matrix_stride       Output matrix stride, can be computed with winograd::WinogradGEMM<2, 2, 3, 3>::Convolution<float, float>::get_output_matrix_stride()
-     * @param[out] output_nhwc         Pointer to a tensor in NHWC data layout ordered output tensor, in the spatial domain.
-     * @param[in]  num_batches         Number of batches in the input tensor.
-     * @param[in]  num_rows            Number of rows in output tensor.
-     * @param[in]  num_cols            Number of columns in output tensor.
-     * @param[in]  num_channels        Number of feature maps in the output tensor.
+     * @param[in]  biases             Pointer to the biases tensor.
+     * @param[in]  transformed_output Pointer to working space for the output tensor in the Winograd domain.
+     * @param[in]  matrix_stride      Output matrix stride, can be computed with winograd::WinogradGEMM<2, 2, 3, 3>::Convolution<float, float>::get_output_matrix_stride()
+     * @param[out] output_nhwc        Pointer to a tensor in NHWC data layout ordered output tensor, in the spatial domain.
+     * @param[in]  num_batches        Number of batches in the input tensor.
+     * @param[in]  num_rows           Number of rows in output tensor.
+     * @param[in]  num_cols           Number of columns in output tensor.
+     * @param[in]  num_channels       Number of feature maps in the output tensor.
+     * @param[in]  workspace          Tensor to be used as the working space during the computation.
      */
     virtual void configure(
         const ITensor *biases,
-        const ITensor *output_workingspace,
+        const ITensor *transformed_output,
         const int      matrix_stride,
         ITensor       *output_nhwc,
         const int      num_batches,
         const int      num_rows,
         const int      num_cols,
-        const int      num_channels) = 0;
+        const int      num_channels,
+        ITensor       *workspace) = 0;
 
     virtual ~INEWinogradLayerTransformOutputKernel()
     {
@@ -305,54 +351,70 @@
      */
     Tensor4DShape get_output_shape(const KernelShape &kernel_shape, const Tensor4DShape &in_shape, const PaddingType padding) const override;
 
+    /** Get the working space required to perform the transformation.
+     *
+     * Note, the working space is only required when performing the
+     * transformation - hence it can be reused whenever the transformation is
+     * not running.
+     *
+     * @param[in] num_threads The greatest number of threads that will be used to execute the transform.
+     *
+     * @return Size of working space required in bytes.
+     */
+    unsigned int get_working_space_size(unsigned int num_threads) const override;
+
     /** Configure the output transform kernel.
      *
-     * @param[in]  biases              Pointer to the biases tensor.
-     * @param[in]  output_workingspace Pointer to working space for the output tensor in the Winograd domain.
-     * @param[in]  matrix_stride       Output matrix stride, can be computed with winograd::WinogradGEMM<2, 2, 3, 3>::Convolution<float, float>::get_output_matrix_stride()
-     * @param[out] output_nhwc         Pointer to a tensor with NHWC data layout, in the spatial domain.
-     * @param[in]  num_batches         Number of batches in the input tensor.
-     * @param[in]  num_rows            Number of rows in output tensor.
-     * @param[in]  num_cols            Number of columns in output tensor.
-     * @param[in]  num_channels        Number of feature maps in the output tensor.
+     * @param[in]  biases             Pointer to the biases tensor.
+     * @param[in]  transformed_output Pointer to working space for the output tensor in the Winograd domain.
+     * @param[in]  matrix_stride      Output matrix stride, can be computed with winograd::WinogradGEMM<2, 2, 3, 3>::Convolution<float, float>::get_output_matrix_stride()
+     * @param[out] output_nhwc        Pointer to a tensor with NHWC data layout, in the spatial domain.
+     * @param[in]  num_batches        Number of batches in the input tensor.
+     * @param[in]  num_rows           Number of rows in output tensor.
+     * @param[in]  num_cols           Number of columns in output tensor.
+     * @param[in]  num_channels       Number of feature maps in the output tensor.
+     * @param[in]  workspace          Tensor to be used as the working space during the computation.
      */
     void configure(
         const ITensor *biases,
-        const ITensor *output_workingspace,
+        const ITensor *transformed_output,
         const int      matrix_stride,
         ITensor       *output_nhwc,
         const int      num_batches,
         const int      num_rows,
         const int      num_cols,
-        const int      num_channels) override;
+        const int      num_channels,
+        ITensor       *workspace) override;
 
     void run(const Window &window, const ThreadInfo &info) override;
 
     /** Static function to check if given info will lead to a valid configuration of @ref NEWinogradLayerTransformOutputKernel
      *
-     * @param[in]  input         Source tensor with shape [C, N, 16, batches] or [C, N, 36, batches]. Data types supported: F32.
-     * @param[in]  bias          Biases tensor. Shared biases supported. Biases are 1D tensor with dimensions [OFM]. It can be a nullptr. Data type supported: as @p input
-     * @param[out] output        Destination tensor with shape [output_convolved_dims.width, output_convolved_dims.height, C, batches]. Data type supported: same as @p input
-     * @param[in]  winograd_info Contains Winograd's information described in @ref WinogradInfo
+     * @param[in] input         Source tensor info with shape [C, N, 16, batches] or [C, N, 36, batches]. Data types supported: F32.
+     * @param[in] bias          Biases tensor info. Shared biases supported. Biases are 1D tensor with dimensions [OFM]. It can be a nullptr. Data type supported: as @p input
+     * @param[in] output        Destination tensor info with shape [output_convolved_dims.width, output_convolved_dims.height, C, batches]. Data type supported: same as @p input
+     * @param[in] winograd_info Contains Winograd's information described in @ref WinogradInfo
      *
      * @return a status
      */
     static Status validate(const ITensorInfo *input, const ITensorInfo *bias, const ITensorInfo *output, const WinogradInfo &winograd_info);
 
 private:
-    using WinogradBase    = winograd::WinogradGEMM<OutputTileRows, OutputTileCols, KernelRows, KernelCols>;
+    using WinogradBase    = winograd::WinogradGEMM<OutputTileRows, OutputTileCols, KernelRows, KernelCols, winograd::WinogradRoots::Integers>;
     using WinogradConv    = typename WinogradBase::template Convolution<T, T>;
-    using OutputTransform = typename WinogradBase::template OutputTransform<T>;
+    using OutputTransform = typename WinogradBase::template OutputTransform<T, T>;
 
-    const ITensor *_biases;
-    const ITensor *_output_workspace;
-    int            _matrix_stride;
-    int            _matrix_row_stride;
-    ITensor       *_output_nhwc;
-    int            _num_batches;
-    int            _num_rows;
-    int            _num_cols;
-    int            _num_channels;
+    std::unique_ptr<OutputTransform> _transform{ nullptr };
+    const ITensor                   *_biases;
+    const ITensor                   *_transformed_output;
+    ITensor                         *_workspace;
+    int                              _matrix_stride;
+    int                              _matrix_row_stride;
+    ITensor                         *_output_nhwc;
+    int                              _num_batches;
+    int                              _num_rows;
+    int                              _num_cols;
+    int                              _num_channels;
 };
 
 /** Interface for the NEON kernel to perform Winograd weights transform. */
@@ -482,15 +544,16 @@
     bool is_parallelisable() const override;
 
 private:
-    using WinogradBase     = winograd::WinogradGEMM<OutputTileRows, OutputTileCols, KernelRows, KernelCols>;
+    using WinogradBase     = winograd::WinogradGEMM<OutputTileRows, OutputTileCols, KernelRows, KernelCols, winograd::WinogradRoots::Integers>;
     using WinogradConv     = typename WinogradBase::template Convolution<T, T>;
-    using WeightsTransform = typename WinogradBase::template WeightsTransform<T>;
+    using WeightsTransform = typename WinogradBase::template WeightsTransform<T, T>;
 
-    const ITensor *_weights_hwio;
-    ITensor       *_output;
-    int            _matrix_stride;
-    int            _num_output_channels;
-    int            _num_input_channels;
+    std::unique_ptr<WeightsTransform> _transform{ nullptr };
+    const ITensor                    *_weights_hwio;
+    ITensor                          *_output;
+    int                               _matrix_stride;
+    int                               _num_output_channels;
+    int                               _num_input_channels;
 };
 
 /** NEON kernel to perform Winograd. */
@@ -499,7 +562,7 @@
 {
 public:
     /** Winograd base kernel */
-    using WinogradBase = winograd::WinogradGEMM<OutputTileRows, OutputTileCols, KernelRows, KernelCols>;
+    using WinogradBase = winograd::WinogradGEMM<OutputTileRows, OutputTileCols, KernelRows, KernelCols, winograd::WinogradRoots::Integers>;
     /** Winograd convolution kernel */
 
     using WinogradConv = typename WinogradBase::template Convolution<TIn, TOut>;