blob: 10d110feb881c73c43f03fd1ecbca56557f6ccff [file] [log] [blame]
Georgios Pinitas4074c992018-01-30 18:13:46 +00001/*
Georgios Pinitas47d39dc2019-03-11 14:03:23 +00002 * Copyright (c) 2018-2019 ARM Limited.
Georgios Pinitas4074c992018-01-30 18:13:46 +00003 *
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
25/*
26 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
27 *
28 * NOTE: Header to be included by implementation files only.
29 *
30 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
31 */
32
33#include "arm_compute/core/NEON/kernels/convolution/common/arm.hpp"
34#include "arm_compute/core/NEON/kernels/convolution/depthwise/impl_base.hpp"
35
36#pragma once
37
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000038using namespace neon_convolution_kernels;
39
Georgios Pinitas4074c992018-01-30 18:13:46 +000040namespace depthwise
41{
Georgios Pinitas4074c992018-01-30 18:13:46 +000042
Georgios Pinitas4074c992018-01-30 18:13:46 +000043template <
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000044 unsigned int OutputTileRows, unsigned int OutputTileCols,
45 unsigned int KernelRows, unsigned int KernelCols,
46 unsigned int StrideRows, unsigned int StrideCols
Georgios Pinitas4074c992018-01-30 18:13:46 +000047>
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000048DepthwiseConvolution<
49 OutputTileRows, OutputTileCols,
50 KernelRows, KernelCols, StrideRows, StrideCols,
51 float, float, float
52>::DepthwiseConvolution(
53 int n_batches, int n_input_rows, int n_input_cols, int n_channels,
54 ActivationFunction activation,
55 unsigned int padding_top,
56 unsigned int padding_left,
57 unsigned int padding_bottom,
58 unsigned int padding_right
59) : Base(
60 n_batches, n_input_rows, n_input_cols, n_channels, activation,
61 padding_top, padding_left, padding_bottom, padding_right
62 )
63{
64}
65
66
67template <
68 unsigned int OutputTileRows, unsigned int OutputTileCols,
69 unsigned int KernelRows, unsigned int KernelCols,
70 unsigned int StrideRows, unsigned int StrideCols
71>
72template <ActivationFunction Activation>
73void DepthwiseConvolution<
74 OutputTileRows, OutputTileCols,
75 KernelRows, KernelCols, StrideRows, StrideCols,
76 float, float, float
77>::execute_tile(
78 int n_channels,
79 const void *weights_biases_ptr,
80 const float *input,
81 const unsigned int in_row_stride,
82 const unsigned int in_col_stride,
83 float *output,
84 const unsigned int out_row_stride,
85 const unsigned int out_col_stride
Georgios Pinitas4074c992018-01-30 18:13:46 +000086)
87{
Georgios Pinitas4074c992018-01-30 18:13:46 +000088 // Instantiate pointers
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000089 const float* __restrict__ inptr_base = input;
90 float* __restrict__ outptr_base = output;
91 const float* __restrict__ params = static_cast<const float*>(weights_biases_ptr);
Georgios Pinitas4074c992018-01-30 18:13:46 +000092
93 // Perform the depthwise convolution
94 int channels_remaining = n_channels;
95#ifdef __aarch64__
96 for (; channels_remaining >= 4; channels_remaining -= 4)
97 {
98 // Load input tile
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000099 float32x4_t u[Base::inner_tile_rows][Base::inner_tile_cols];
100 for (int i = 0; i < Base::inner_tile_rows; i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000101 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000102 const float* const inptr_row = inptr_base + i*in_row_stride;
103 for (int j = 0; j < Base::inner_tile_cols; j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000104 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000105 u[i][j] = vld1q_f32(inptr_row + j*in_col_stride);
Georgios Pinitas4074c992018-01-30 18:13:46 +0000106 }
107 }
108 inptr_base += 4;
109
110 // Load weights tile
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000111 float32x4_t vbias = vld1q_f32(params);
112 params += 4;
113
114 float32x4_t w[KernelRows][KernelCols];
115 for (unsigned int i = 0; i < KernelRows; i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000116 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000117 for (unsigned int j = 0; j < KernelCols; j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000118 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000119 w[i][j] = vld1q_f32(params);
120 params += 4;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000121 }
122 }
Georgios Pinitas4074c992018-01-30 18:13:46 +0000123
124 // Perform the convolution
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000125 float32x4_t v[OutputTileRows][OutputTileCols];
126 for (unsigned int out_i = 0; out_i < OutputTileRows; out_i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000127 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000128 for (unsigned int out_j = 0; out_j < OutputTileCols; out_j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000129 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000130 v[out_i][out_j] = vbias;
131
Georgios Pinitas4074c992018-01-30 18:13:46 +0000132 // Base co-ordinate
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000133 const int base_i = out_i * StrideRows;
134 const int base_j = out_j * StrideCols;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000135
136 // Fill the accumulator
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000137 for (unsigned int in_i = 0; in_i < KernelRows; in_i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000138 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000139 const unsigned int i = base_i + in_i;
140 for (unsigned int in_j = 0; in_j < KernelCols; in_j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000141 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000142 const unsigned int j = base_j + in_j;
143
144 // v[out_i][out_j] += w[in_i][in_j] * u[i][j];
145 v[out_i][out_j] = vmlaq_f32(v[out_i][out_j], w[in_i][in_j], u[i][j]);
Georgios Pinitas4074c992018-01-30 18:13:46 +0000146 }
147 }
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000148
149 // Apply the activation function
150 if (Activation == ActivationFunction::ReLU ||
151 Activation == ActivationFunction::ReLU6)
152 {
153 v[out_i][out_j] = vmaxq_f32(v[out_i][out_j], vdupq_n_f32(0.0f));
154 }
155 if (Activation == ActivationFunction::ReLU6)
156 {
157 v[out_i][out_j] = vminq_f32(v[out_i][out_j], vdupq_n_f32(6.0f));
158 }
Georgios Pinitas4074c992018-01-30 18:13:46 +0000159 }
160 }
161
162 // Store the output tile
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000163 for (unsigned int i = 0; i < OutputTileRows; i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000164 {
165 float* const outptr_row = outptr_base + i*out_row_stride;
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000166 for (unsigned int j = 0; j < OutputTileCols; j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000167 {
168 vst1q_f32(outptr_row + j*out_col_stride, v[i][j]);
169 }
170 }
171 outptr_base += 4;
172 }
173#endif // __aarch64__
174 for (; channels_remaining; channels_remaining--)
175 {
176 // Load input tile
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000177 float u[Base::inner_tile_rows][Base::inner_tile_cols];
178 for (int i = 0; i < Base::inner_tile_rows; i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000179 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000180 const float* const inptr_row = inptr_base + i*in_row_stride;
181 for (int j = 0; j < Base::inner_tile_cols; j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000182 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000183 u[i][j] = *(inptr_row + j*in_col_stride);
Georgios Pinitas4074c992018-01-30 18:13:46 +0000184 }
185 }
186 inptr_base++;
187
188 // Load weights tile
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000189 float bias = *(params++);
190 float w[KernelRows][KernelCols];
191 for (unsigned int i = 0; i < KernelRows; i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000192 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000193 for (unsigned int j = 0; j < KernelCols; j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000194 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000195 w[i][j] = *(params++);
Georgios Pinitas4074c992018-01-30 18:13:46 +0000196 }
197 }
Georgios Pinitas4074c992018-01-30 18:13:46 +0000198
199 // Perform the convolution
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000200 float v[OutputTileRows][OutputTileCols];
201 for (unsigned int out_i = 0; out_i < OutputTileRows; out_i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000202 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000203 for (unsigned int out_j = 0; out_j < OutputTileCols; out_j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000204 {
205 // Clear the accumulator
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000206 v[out_i][out_j] = bias;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000207
208 // Base co-ordinate
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000209 const int base_i = out_i * StrideRows;
210 const int base_j = out_j * StrideCols;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000211
212 // Fill the accumulator
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000213 for (unsigned int in_i = 0; in_i < KernelRows; in_i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000214 {
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000215 const unsigned int i = base_i + in_i;
216 for (unsigned int in_j = 0; in_j < KernelCols; in_j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000217 {
218 const int j = base_j + in_j;
219 v[out_i][out_j] += w[in_i][in_j] * u[i][j];
220 }
221 }
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000222
223 // Apply the activation function
224 if (Activation == ActivationFunction::ReLU ||
225 Activation == ActivationFunction::ReLU6)
226 {
227 v[out_i][out_j] = std::max(0.0f, v[out_i][out_j]);
228 }
229 if (Activation == ActivationFunction::ReLU6)
230 {
231 v[out_i][out_j] = std::min(6.0f, v[out_i][out_j]);
232 }
Georgios Pinitas4074c992018-01-30 18:13:46 +0000233 }
234 }
235
236 // Store the output tile
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000237 for (unsigned int i = 0; i < OutputTileRows; i++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000238 {
239 float* const outptr_row = outptr_base + i*out_row_stride;
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000240 for (unsigned int j = 0; j < OutputTileCols; j++)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000241 {
242 *(outptr_row + j*out_col_stride) = v[i][j];
243 }
244 }
245 outptr_base++;
246 }
247}
248
249} // namespace depthwise