blob: f69c88324bce7ed54d0e36bc07ec69ea4a4dbe7a [file] [log] [blame]
Gian Marco Iodice06b184a2017-08-29 16:05:25 +01001/*
Michalis Spyroua4f378d2019-04-26 14:54:54 +01002 * Copyright (c) 2017-2019 ARM Limited.
Gian Marco Iodice06b184a2017-08-29 16:05:25 +01003 *
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#include "arm_compute/core/NEON/kernels/NEMinMaxLayerKernel.h"
25
26#include "arm_compute/core/Coordinates.h"
27#include "arm_compute/core/Error.h"
28#include "arm_compute/core/Helpers.h"
29#include "arm_compute/core/IAccessWindow.h"
30#include "arm_compute/core/ITensor.h"
31#include "arm_compute/core/TensorInfo.h"
32#include "arm_compute/core/Types.h"
33#include "arm_compute/core/Validate.h"
34#include "arm_compute/core/Window.h"
Alex Gilday60954c62018-03-05 16:22:48 +000035#include "arm_compute/core/utils/misc/ShapeCalculator.h"
Gian Marco Iodice06b184a2017-08-29 16:05:25 +010036
37#include <algorithm>
38#include <arm_neon.h>
39#include <climits>
40#include <cstddef>
41
Alex Gilday60954c62018-03-05 16:22:48 +000042using namespace arm_compute::misc::shape_calculator;
43
Gian Marco Iodice06b184a2017-08-29 16:05:25 +010044namespace arm_compute
45{
Alex Gilday60954c62018-03-05 16:22:48 +000046namespace
47{
48Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output)
49{
50 ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, output);
51 ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32);
52 ARM_COMPUTE_RETURN_ERROR_ON(input->num_dimensions() < 3);
53
54 if(output->tensor_shape().total_size() > 0)
55 {
56 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, output);
57
58 TensorShape output_shape = compute_min_max_shape(input);
59
60 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(output->tensor_shape(), output_shape);
61 }
62
63 return Status{};
64}
65
66std::tuple<Status, Window> validate_and_configure_window(ITensorInfo *input, ITensorInfo *output)
67{
68 TensorShape output_shape = compute_min_max_shape(input);
69
70 // Output auto initialization if not yet initialized
Vidhya Sudhan Loganathan7485d5a2018-07-04 09:34:00 +010071 auto_init_if_empty(*output, output_shape, 1, input->data_type());
Alex Gilday60954c62018-03-05 16:22:48 +000072
73 constexpr unsigned int num_elems_processed_per_iteration = 1;
74
75 // Configure kernel window
76 Window win = calculate_max_window(*input, Steps(num_elems_processed_per_iteration));
77 AccessWindowHorizontal input_access(input, 0, num_elems_processed_per_iteration);
78 AccessWindowHorizontal output_access(output, 0, 2);
79
80 bool window_changed = update_window_and_padding(win, input_access, output_access);
81
82 output_access.set_valid_region(win, ValidRegion(Coordinates(), output->tensor_shape()));
83
84 Status err = (window_changed) ? ARM_COMPUTE_CREATE_ERROR(ErrorCode::RUNTIME_ERROR, "Insufficient Padding!") : Status{};
85 return std::make_tuple(err, win);
86}
87} // namespace
88
Gian Marco Iodice06b184a2017-08-29 16:05:25 +010089NEMinMaxLayerKernel::NEMinMaxLayerKernel()
90 : _input(nullptr), _output(nullptr), _mtx()
91{
92}
93
94void NEMinMaxLayerKernel::configure(const ITensor *input, ITensor *output)
95{
Alex Gilday60954c62018-03-05 16:22:48 +000096 ARM_COMPUTE_ERROR_ON_NULLPTR(input, output);
97 ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input->info(), output->info()));
Gian Marco Iodice06b184a2017-08-29 16:05:25 +010098
99 _input = input;
100 _output = output;
101
Alex Gilday60954c62018-03-05 16:22:48 +0000102 auto win_config = validate_and_configure_window(input->info(), output->info());
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100103
Alex Gilday60954c62018-03-05 16:22:48 +0000104 ARM_COMPUTE_ERROR_THROW_ON(std::get<0>(win_config));
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100105
Alex Gilday60954c62018-03-05 16:22:48 +0000106 INEKernel::configure(std::get<1>(win_config));
107}
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100108
Alex Gilday60954c62018-03-05 16:22:48 +0000109Status NEMinMaxLayerKernel::validate(const ITensorInfo *input, const ITensorInfo *output)
110{
111 ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, output));
112 ARM_COMPUTE_RETURN_ON_ERROR(std::get<0>(validate_and_configure_window(input->clone().get(), output->clone().get())));
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100113
Alex Gilday60954c62018-03-05 16:22:48 +0000114 return Status{};
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100115}
116
117void NEMinMaxLayerKernel::run(const Window &window, const ThreadInfo &info)
118{
119 ARM_COMPUTE_UNUSED(info);
120 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
121 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
122
123 const int x_start = window.x().start();
124 const int x_end = window.x().end();
125
126 Window window_output;
127 window_output.use_tensor_dimensions(_output->info()->tensor_shape());
128 window_output.set(Window::DimX, Window::Dimension(0, 1, 1));
129
130 // Handle X dimension manually to split into two loops
131 // First one will use vector operations, second one processes the left over pixels
132 Window window_input(window);
133 window_input.set(Window::DimX, Window::Dimension(0, 1, 1));
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100134 window_input.set(3, Window::Dimension(0, 1, 1));
135
136 Iterator input(_input, window_input);
137 Iterator output(_output, window_output);
138
139 execute_window_loop(window_output, [&](const Coordinates & id_batch)
140 {
141 float32x2_t carry_min = vdup_n_f32(std::numeric_limits<float>::max());
142 float32x2_t carry_max = vdup_n_f32(std::numeric_limits<float>::lowest());
143
144 float carry_min_scalar = std::numeric_limits<float>::max();
145 float carry_max_scalar = std::numeric_limits<float>::lowest();
146
Michalis Spyroua4f378d2019-04-26 14:54:54 +0100147 execute_window_loop(window_input, [&](const Coordinates &)
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100148 {
149 int x = x_start;
Kohei Takahashicedb78f2018-08-23 10:23:52 +0900150 const auto in_ptr = reinterpret_cast<const float *>(input.ptr() + id_batch[1] * _input->info()->strides_in_bytes()[3]);
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100151
152 // Vector loop
153 for(; x <= x_end - 8; x += 8)
154 {
155 const float32x4x2_t pixels = vld2q_f32(in_ptr + x);
156 const float32x4_t tmp_min1 = vminq_f32(pixels.val[0], pixels.val[1]);
157 const float32x4_t tmp_max1 = vmaxq_f32(pixels.val[0], pixels.val[1]);
158 const float32x2_t tmp_min2 = vmin_f32(vget_high_f32(tmp_min1), vget_low_f32(tmp_min1));
159 const float32x2_t tmp_max2 = vmax_f32(vget_high_f32(tmp_max1), vget_low_f32(tmp_max1));
160 carry_min = vmin_f32(tmp_min2, carry_min);
161 carry_max = vmax_f32(tmp_max2, carry_max);
162 }
163
164 // Process leftover pixels
165 for(; x < x_end; ++x)
166 {
167 const float pixel = in_ptr[x];
168 carry_min_scalar = std::min(pixel, carry_min_scalar);
169 carry_max_scalar = std::max(pixel, carry_max_scalar);
170 }
171 },
172 input);
173
174 // Reduce result
175 carry_min = vpmin_f32(carry_min, carry_min);
176 carry_max = vpmax_f32(carry_max, carry_max);
177 carry_min = vpmin_f32(carry_min, carry_min);
178 carry_max = vpmax_f32(carry_max, carry_max);
179
180 // Extract max/min values
181 const float min_i = std::min(vget_lane_f32(carry_min, 0), carry_min_scalar);
182 const float max_i = std::max(vget_lane_f32(carry_max, 0), carry_max_scalar);
183
Kohei Takahashicedb78f2018-08-23 10:23:52 +0900184 auto out_ptr = reinterpret_cast<float *>(output.ptr());
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100185
186 // Perform reduction of local min/max values
187 update_min_max(out_ptr, min_i, max_i);
188 },
189 output);
190}
191
192void NEMinMaxLayerKernel::reset()
193{
194 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
195
196 float32x2_t reset_values = vdup_n_f32(0.0f);
197 reset_values = vset_lane_f32(std::numeric_limits<float>::max(), reset_values, 0);
Isabella Gottardi7e1944d2018-03-12 16:39:05 +0000198 reset_values = vset_lane_f32(std::numeric_limits<float>::lowest(), reset_values, 1);
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100199
200 Window window_output;
201 window_output.use_tensor_dimensions(_output->info()->tensor_shape());
202 window_output.set(Window::DimX, Window::Dimension(0, 1, 1));
203
204 Iterator output(_output, window_output);
205
Michalis Spyroua4f378d2019-04-26 14:54:54 +0100206 execute_window_loop(window_output, [&](const Coordinates &)
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100207 {
Kohei Takahashicedb78f2018-08-23 10:23:52 +0900208 vst1_f32(reinterpret_cast<float *>(output.ptr()), reset_values);
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100209 },
210 output);
211}
212
213void NEMinMaxLayerKernel::update_min_max(float *out_ptr, float min, float max)
214{
Georgios Pinitase874ef92019-09-09 17:40:33 +0100215 arm_compute::lock_guard<Mutex> lock(_mtx);
Gian Marco Iodice06b184a2017-08-29 16:05:25 +0100216
217 const float32x2_t old_min = vld1_dup_f32(out_ptr);
218 const float32x2_t old_max = vld1_dup_f32(out_ptr + 1);
219 const float32x2_t new_min = vmin_f32(vdup_n_f32(min), old_min);
220 const float32x2_t new_max = vmax_f32(vdup_n_f32(max), old_max);
221
222 vst1_f32(out_ptr, vzip_f32(new_min, new_max).val[0]);
223}
Pablo Tello9e40cf72017-09-15 16:14:55 +0100224} // namespace arm_compute