blob: 493b2991dcad07f3b9897aa15dbadc108f2bb163 [file] [log] [blame]
Georgios Pinitas4074c992018-01-30 18:13:46 +00001/*
Georgios Pinitas1a57ad12019-01-09 16:11:51 +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 <algorithm>
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000034#include <cstdint>
Georgios Pinitas4074c992018-01-30 18:13:46 +000035#include "arm_compute/core/NEON/kernels/convolution/depthwise/depthwise.hpp"
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000036#include "arm_compute/core/NEON/kernels/convolution/common/padding.hpp"
Georgios Pinitas4074c992018-01-30 18:13:46 +000037#include "arm_compute/core/NEON/kernels/convolution/common/utils.hpp"
38
39#pragma once
40
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000041#define MEMBERFN(TOUT) template <\
42 unsigned int OutputTileRows, unsigned int OutputTileColumns,\
43 unsigned int KernelRows, unsigned int KernelColumns,\
44 unsigned int StrideRows, unsigned int StrideColumns,\
45 typename TIn, typename TBias, typename TOut,\
46 typename Derived\
47> TOUT DepthwiseConvolutionBase<\
48 OutputTileRows, OutputTileColumns,\
49 KernelRows, KernelColumns,\
50 StrideRows, StrideColumns,\
51 TIn, TBias, TOut, Derived\
52>
53
54using namespace neon_convolution_kernels;
55
Georgios Pinitas4074c992018-01-30 18:13:46 +000056namespace depthwise
57{
58
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000059template <unsigned int KernelRows, unsigned int KernelColumns, size_t WeightSize, size_t BiasSize>
60struct PackParameters
61{
62 static void execute(
63 unsigned int n_channels,
64 void *buffer,
65 const void *weights,
66 unsigned int weight_row_stride,
67 unsigned int weight_col_stride,
68 const void *biases
69 );
70};
71
Georgios Pinitasbe0ae932018-03-13 13:08:12 +000072const unsigned int CHANNEL_BLOCK = 16;
73
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000074MEMBERFN(int)::get_output_size(
Georgios Pinitas1a57ad12019-01-09 16:11:51 +000075 const int dim_size, const unsigned int padding_before, const unsigned int padding_after
76)
77{
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000078 return iceildiv(dim_size + padding_before + padding_after - KernelRows + 1, StrideRows);
Georgios Pinitas1a57ad12019-01-09 16:11:51 +000079}
Georgios Pinitas4074c992018-01-30 18:13:46 +000080
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000081MEMBERFN(int)::output_size(
82 const int dim_size, const unsigned int padding_before, const unsigned int padding_after
83) const
Georgios Pinitas1a57ad12019-01-09 16:11:51 +000084{
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000085 return get_output_size(dim_size, padding_before, padding_after);
Georgios Pinitas1a57ad12019-01-09 16:11:51 +000086}
87
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000088MEMBERFN()::DepthwiseConvolutionBase(
89 const int n_batches,
90 const int n_input_rows,
91 const int n_input_cols,
Georgios Pinitas1a57ad12019-01-09 16:11:51 +000092 const int n_channels,
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000093 ActivationFunction activation,
Georgios Pinitas1a57ad12019-01-09 16:11:51 +000094 const unsigned int padding_top,
95 const unsigned int padding_left,
96 const unsigned int padding_bottom,
Georgios Pinitas47d39dc2019-03-11 14:03:23 +000097 const unsigned int padding_right
98) : _input(nullptr), _output(nullptr),
99 _packed_parameters(nullptr),
100 _working_space(nullptr),
Georgios Pinitas4074c992018-01-30 18:13:46 +0000101 _n_batches(n_batches),
102 _n_input_rows(n_input_rows),
103 _n_input_cols(n_input_cols),
104 _n_channels(n_channels),
Georgios Pinitas1a57ad12019-01-09 16:11:51 +0000105 _n_output_rows(get_output_size(n_input_rows, padding_top, padding_bottom)),
106 _n_output_cols(get_output_size(n_input_cols, padding_left, padding_right)),
Georgios Pinitas4074c992018-01-30 18:13:46 +0000107 _n_tile_rows(iceildiv(_n_output_rows, output_tile_rows)),
108 _n_tile_cols(iceildiv(_n_output_cols, output_tile_cols)),
Georgios Pinitas1a57ad12019-01-09 16:11:51 +0000109 _padding_top(padding_top),
110 _padding_left(padding_left),
111 _padding_bottom(padding_bottom),
112 _padding_right(padding_right),
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000113 _activation(activation),
114 _input_col_stride(0), _input_row_stride(0), _input_batch_stride(0),
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100115 _output_col_stride(0), _output_row_stride(0), _output_batch_stride(0)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000116{
117}
118
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000119MEMBERFN(void)::set_input(const void* const inptr)
120{
121 set_input(inptr, _n_channels);
122}
Georgios Pinitas4074c992018-01-30 18:13:46 +0000123
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000124MEMBERFN(void)::set_input(const void* const inptr, const int ld_col)
125{
126 set_input(inptr, _n_input_cols * ld_col, ld_col);
127}
128
129MEMBERFN(void)::set_input(const void* const inptr, const int ld_row, const int ld_col)
130{
131 set_input(inptr, _n_input_rows * ld_row, ld_row, ld_col);
132}
133
134MEMBERFN(void)::set_input(const void* const inptr, const int ld_batch, const int ld_row, const int ld_col)
135{
136 _input = static_cast<const TIn *>(inptr);
137 _input_batch_stride = ld_batch;
138 _input_row_stride = ld_row;
139 _input_col_stride = ld_col;
140}
141
142MEMBERFN(void)::set_output(void* const outptr)
143{
144 set_output(outptr, _n_channels);
145}
146
147MEMBERFN(void)::set_output(void* const outptr, const int ld_col)
148{
149 set_output(outptr, _n_output_cols * ld_col, ld_col);
150}
151
152MEMBERFN(void)::set_output(void* const outptr, const int ld_row, const int ld_col)
153{
154 set_output(outptr, _n_output_rows * ld_row, ld_row, ld_col);
155}
156
157MEMBERFN(void)::set_output(void* const outptr, const int ld_batch, const int ld_row, const int ld_col)
158{
159 _output = static_cast<TOut *>(outptr);
160 _output_batch_stride = ld_batch;
161 _output_row_stride = ld_row;
162 _output_col_stride = ld_col;
163}
164
165MEMBERFN(size_t)::get_packed_params_size(void) const
166{
167 return _n_channels * (sizeof(TIn)*KernelRows*KernelColumns + sizeof(TBias));
168}
169
170MEMBERFN(void)::set_packed_params_buffer(void *buffer)
171{
172 _packed_parameters = buffer;
173}
174
175MEMBERFN(void)::pack_params(const void *weights, const void *biases) const
176{
177 static_cast<const Derived *>(this)->pack_params(_packed_parameters, weights, biases);
178}
179
180MEMBERFN(void)::pack_params(void *buffer, const void *weights, const void *biases) const
181{
182 const unsigned int weight_col_stride = _n_channels;
183 const unsigned int weight_row_stride = KernelColumns * weight_col_stride;
184 static_cast<const Derived *>(this)->pack_params(
185 buffer, weights, weight_row_stride, weight_col_stride, biases
186 );
187}
188
189MEMBERFN(void)::pack_params(
190 void * const buffer,
191 const void * const weights,
192 const unsigned int weight_row_stride,
193 const unsigned int weight_col_stride,
194 const void * const biases
195) const
196{
197 static_cast<const Derived *>(this)->_pack_params(
198 buffer, weights, weight_row_stride, weight_col_stride, biases
199 );
200}
201
202MEMBERFN(void)::_pack_params(
203 void * const buffer,
204 const void * const weights,
205 const unsigned int weight_row_stride,
206 const unsigned int weight_col_stride,
207 const void * const biases
208) const
209{
210 // Default implementation
211 PackParameters<KernelRows, KernelColumns, sizeof(TIn), sizeof(TOut)>::execute(
212 _n_channels, buffer, weights, weight_row_stride, weight_col_stride, biases
213 );
214}
215
216MEMBERFN(size_t)::get_working_space_size(const unsigned int nthreads) const
217{
218 return nthreads * (
219 _get_input_working_space_size() + _get_output_working_space_size()
220 );
221}
222
223MEMBERFN(void)::set_working_space(void *buffer)
224{
225 _working_space = buffer;
226}
227
228MEMBERFN(size_t)::_get_input_working_space_size(void) const
229{
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100230 return sizeof(TIn) * _n_channels;
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000231}
232
233MEMBERFN(size_t)::_get_output_working_space_size(void) const
234{
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100235 return sizeof(TOut) * _n_channels;
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000236}
237
238MEMBERFN(void *)::_get_input_working_space(const unsigned int threadid) const
239{
240 return static_cast<uint8_t*>(_working_space) + threadid * (
241 _get_input_working_space_size() + _get_output_working_space_size()
242 );
243}
244
245MEMBERFN(void *)::_get_output_working_space(const unsigned int threadid) const
246{
247 return static_cast<uint8_t*>(_get_input_working_space(threadid)) + _get_input_working_space_size();
248}
249
250MEMBERFN(unsigned int)::get_window() const
Georgios Pinitas4074c992018-01-30 18:13:46 +0000251{
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000252 // Parallelise over blocks of channels.
253 return iceildiv(_n_channels, CHANNEL_BLOCK);
Georgios Pinitas4074c992018-01-30 18:13:46 +0000254}
255
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000256MEMBERFN(void)::run(
Georgios Pinitas4074c992018-01-30 18:13:46 +0000257 const unsigned int start,
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000258 const unsigned int stop,
259 const unsigned int threadid
Georgios Pinitas4074c992018-01-30 18:13:46 +0000260)
261{
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100262 // Clear the input padding buffer
263 TIn *buf = static_cast<TIn *>(_get_input_working_space(threadid));
264 const TIn pad_value = static_cast<Derived *>(this)->_input_padding_value();
265 for (int n = 0; n < _n_channels; n++)
266 {
267 buf[n] = pad_value;
268 }
269
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000270 // Parallelise over blocks of channels
271 const auto start_channel = CHANNEL_BLOCK * start;
272 const auto stop_channel = std::min<unsigned int>(_n_channels, CHANNEL_BLOCK * stop);
Georgios Pinitas4074c992018-01-30 18:13:46 +0000273
274 // Compute top and bottom padding for input and output
Georgios Pinitas1a57ad12019-01-09 16:11:51 +0000275 const int input_pad_top = _padding_top;
276 const int input_pad_left = _padding_left;
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000277 constexpr int tile_overlap = kernel_rows - stride_rows;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000278
279 // Perform the convolution by calling `process_tile_row` for each tile row in
280 // each batch.
281 for (int batch = 0; batch < _n_batches; batch++)
282 {
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000283 const TIn* const inptr_batch = _input + batch*_input_batch_stride;
284 TOut* const outptr_batch = _output + batch*_output_batch_stride;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000285
286 // Loop over rows of tiles
287 for (int tile_i = 0; tile_i < _n_tile_rows; tile_i++)
288 {
289 // Pointer to the row
290 const int input_row_offset = (tile_i == 0) ? 0 : input_pad_top;
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000291 const TIn* const inptr_row = (inptr_batch + ((inner_tile_rows - tile_overlap)*tile_i - input_row_offset)*_input_row_stride);
292 TOut* const outptr_row = outptr_batch + output_tile_rows * tile_i * _output_row_stride;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000293
294 // Input padding (top + bottom) for the row
295 const int input_row_top = tile_i*(inner_tile_rows - tile_overlap) - input_pad_top;
296 const int input_row_bottom = input_row_top + inner_tile_rows;
297 const int input_row_pad_top = (tile_i == 0) ? input_pad_top : 0;
298 const int input_row_pad_bottom = std::max(0, input_row_bottom - _n_input_rows);
299
300 // Output padding (bottom) for the row
301 const int output_row_bottom = (tile_i + 1)*output_tile_rows;
302 const int output_row_pad_bottom = std::max(0, output_row_bottom - _n_output_rows);
303
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000304 // Get the offset into the packed parameters
305 const auto params_ptr = static_cast<const uint8_t*>(_packed_parameters) +
306 start_channel*(sizeof(TIn)*KernelRows*KernelColumns + sizeof(TBias));
307
Georgios Pinitas4074c992018-01-30 18:13:46 +0000308 // Process the row
309 process_tile_row(
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000310 threadid,
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000311 stop_channel - start_channel,
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000312 params_ptr,
313 inptr_row + start_channel,
314 outptr_row + start_channel,
Georgios Pinitas4074c992018-01-30 18:13:46 +0000315 input_row_pad_top, input_pad_left, input_row_pad_bottom,
316 output_row_pad_bottom,
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000317 _n_tile_cols, _n_input_cols, _n_output_cols
Georgios Pinitas4074c992018-01-30 18:13:46 +0000318 );
319 }
320 }
321}
322
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000323MEMBERFN(void)::process_tile_row(
324 const unsigned int threadid,
Georgios Pinitas4074c992018-01-30 18:13:46 +0000325 const int n_channels,
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000326 const void* const packed_params,
Georgios Pinitas4074c992018-01-30 18:13:46 +0000327 const TIn* const inptr,
Georgios Pinitas4074c992018-01-30 18:13:46 +0000328 TOut* const outptr,
Georgios Pinitas4074c992018-01-30 18:13:46 +0000329 const int row_pad_in_top,
330 const int row_pad_in_left,
331 const int row_pad_in_bottom,
332 const int row_pad_out_bottom,
333 const int n_tiles,
334 const int n_input_cols,
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000335 const int n_output_cols
Georgios Pinitas4074c992018-01-30 18:13:46 +0000336)
337{
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000338 constexpr int tile_overlap = kernel_cols - stride_cols;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000339
340 // Loop over columns of tiles
341 for (int tile_j = 0; tile_j < n_tiles; tile_j++)
342 {
343 // Input padding (left + right) for the tile
344 const int t_pad_in_left = (tile_j == 0) ? row_pad_in_left : 0;
345 const int t_in_start = tile_j*(inner_tile_cols - tile_overlap) - row_pad_in_left;
346 const int t_in_end = t_in_start + inner_tile_cols;
347 const int t_pad_in_right = std::max(0, t_in_end - n_input_cols);
348
349 // Output padding (right) for the tile
350 const int t_out_end = (tile_j + 1) * output_tile_cols;
351 const int t_pad_out_right = std::max(0, t_out_end - n_output_cols);
352
353 // Get pointers into the inputs and outputs
354 const int col_offset = (tile_j == 0) ? 0 : row_pad_in_left;
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000355 const TIn* const inptr_col = (inptr + ((inner_tile_cols - tile_overlap)*tile_j - col_offset)*_input_col_stride);
356 TOut* const outptr_col = outptr + tile_j * output_tile_cols * _output_col_stride;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000357
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000358 // Process just this tile
359 process_tile(
360 threadid, n_channels, packed_params, inptr_col, outptr_col,
361 row_pad_in_top, t_pad_in_left, row_pad_in_bottom, t_pad_in_right, // Input paddings
362 row_pad_out_bottom, t_pad_out_right // Output paddings
Georgios Pinitas4074c992018-01-30 18:13:46 +0000363 );
364 }
365}
366
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000367MEMBERFN(TIn)::_input_padding_value(void) const
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000368{
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000369 return static_cast<TIn>(0);
370}
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000371
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000372MEMBERFN(void)::process_tile(
373 const unsigned int threadid,
Georgios Pinitas4074c992018-01-30 18:13:46 +0000374 const int n_channels,
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000375 const void* const packed_params,
376 const TIn* const inptr,
377 TOut* const outptr,
378 const int pad_in_top,
379 const int pad_in_left,
380 const int pad_in_bottom,
381 const int pad_in_right,
382 const int pad_out_bottom,
383 const int pad_out_right
Georgios Pinitas4074c992018-01-30 18:13:46 +0000384)
385{
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100386 Derived * dthis = static_cast<Derived *>(this);
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000387 const bool pad_input = pad_in_top || pad_in_left || pad_in_bottom || pad_in_right;
388 const bool pad_output = pad_out_bottom || pad_out_right;
Georgios Pinitasbe0ae932018-03-13 13:08:12 +0000389
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100390 if (!pad_input && !pad_output)
Georgios Pinitas4074c992018-01-30 18:13:46 +0000391 {
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100392 switch(_activation)
393 {
394 case ActivationFunction::ReLU:
395 dthis->template execute_tile<ActivationFunction::ReLU>(
396 n_channels, packed_params,
397 inptr, _input_row_stride, _input_col_stride,
398 outptr, _output_row_stride, _output_col_stride
399 );
400 break;
401 case ActivationFunction::ReLU6:
402 dthis->template execute_tile<ActivationFunction::ReLU6>(
403 n_channels, packed_params,
404 inptr, _input_row_stride, _input_col_stride,
405 outptr, _output_row_stride, _output_col_stride
406 );
407 break;
408 default:
409 dthis->template execute_tile<ActivationFunction::None>(
410 n_channels, packed_params,
411 inptr, _input_row_stride, _input_col_stride,
412 outptr, _output_row_stride, _output_col_stride
413 );
414 break;
415 }
Georgios Pinitas4074c992018-01-30 18:13:46 +0000416 }
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100417 else
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000418 {
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100419 // Create arrays of input and output pointers, pointing padded elements to
420 // the working space padding buffers provided.
421 const TIn *inptrs[inner_tile_rows][inner_tile_cols];
422 for (int i = 0; i < inner_tile_rows; i++)
423 {
424 for (int j = 0; j < inner_tile_cols; j++)
425 {
426 if (i < pad_in_top || (inner_tile_rows - pad_in_bottom) <= i ||
427 j < pad_in_left || (inner_tile_cols - pad_in_right) <= j)
428 {
429 // Padded input
430 inptrs[i][j] = static_cast<const TIn *>(_get_input_working_space(threadid));
431 }
432 else
433 {
434 inptrs[i][j] = inptr + (i - pad_in_top)*_input_row_stride + (j - pad_in_left)*_input_col_stride;
435 }
436 }
437 }
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000438
Georgios Pinitasa4bba9c2019-04-02 15:27:52 +0100439 TOut *outptrs[output_tile_rows][output_tile_cols];
440 for (int i = 0; i < output_tile_rows; i++)
441 {
442 for (int j = 0; j < output_tile_cols; j++)
443 {
444 if (i < (output_tile_rows - pad_out_bottom) &&
445 j < (output_tile_cols - pad_out_right))
446 {
447 outptrs[i][j] = outptr + i*_output_row_stride + j*_output_col_stride;
448 }
449 else
450 {
451 outptrs[i][j] = static_cast<TOut *>(_get_output_working_space(threadid));
452 }
453 }
454 }
455
456 switch(_activation)
457 {
458 case ActivationFunction::ReLU:
459 dthis->template execute_tile<ActivationFunction::ReLU>(
460 n_channels, packed_params, inptrs, outptrs
461 );
462 break;
463 case ActivationFunction::ReLU6:
464 dthis->template execute_tile<ActivationFunction::ReLU6>(
465 n_channels, packed_params, inptrs, outptrs
466 );
467 break;
468 default:
469 dthis->template execute_tile<ActivationFunction::None>(
470 n_channels, packed_params, inptrs, outptrs
471 );
472 break;
473 }
Georgios Pinitas47d39dc2019-03-11 14:03:23 +0000474 }
475}
476
477MEMBERFN(int)::n_channels(void) const
478{
479 return _n_channels;
Georgios Pinitas4074c992018-01-30 18:13:46 +0000480}
481
Georgios Pinitas4074c992018-01-30 18:13:46 +0000482} // namespace depthwise