blob: 97661087d0fc62d79184b9d71a16e0564f67d61b [file] [log] [blame]
XNNPACK Teamb455b122019-09-27 18:10:33 -07001// Copyright (c) Facebook, Inc. and its affiliates.
2// All rights reserved.
3//
4// Copyright 2019 Google LLC
5//
6// This source code is licensed under the BSD-style license found in the
7// LICENSE file in the root directory of this source tree.
8
9#pragma once
10
11#include <gtest/gtest.h>
12
13#include <algorithm>
14#include <cassert>
15#include <cstddef>
16#include <cstdlib>
17#include <functional>
Marat Dukhan5ce30d92020-04-14 03:31:26 -070018#include <limits>
XNNPACK Teamb455b122019-09-27 18:10:33 -070019#include <random>
20#include <vector>
21
22#include <xnnpack.h>
Marat Dukhaneeaa7bd2019-10-25 17:31:25 -070023#include <xnnpack/params-init.h>
Frank Barcharde0601b52019-10-25 17:43:34 -070024#include <xnnpack/params.h>
XNNPACK Teamb455b122019-09-27 18:10:33 -070025
26
27class MaxPoolMicrokernelTester {
28 public:
29 enum class Variant {
30 Native,
31 Scalar,
32 };
33
Marat Dukhan329da642019-11-19 21:44:39 -080034 inline MaxPoolMicrokernelTester& output_pixels(size_t output_pixels) {
35 assert(output_pixels != 0);
36 this->output_pixels_ = output_pixels;
XNNPACK Teamb455b122019-09-27 18:10:33 -070037 return *this;
38 }
39
Marat Dukhan329da642019-11-19 21:44:39 -080040 inline size_t output_pixels() const {
41 return this->output_pixels_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070042 }
43
Marat Dukhan329da642019-11-19 21:44:39 -080044 inline MaxPoolMicrokernelTester& step(size_t step) {
45 assert(step != 0);
46 this->step_ = step;
XNNPACK Teamb455b122019-09-27 18:10:33 -070047 return *this;
48 }
49
Marat Dukhan329da642019-11-19 21:44:39 -080050 inline size_t step() const {
51 return this->step_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070052 }
53
Marat Dukhan329da642019-11-19 21:44:39 -080054 inline MaxPoolMicrokernelTester& input_offset(size_t input_offset) {
55 assert(input_offset != 0);
56 this->input_offset_ = input_offset;
XNNPACK Teamb455b122019-09-27 18:10:33 -070057 return *this;
58 }
59
Marat Dukhan329da642019-11-19 21:44:39 -080060 inline size_t input_offset() const {
61 return this->input_offset_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070062 }
63
Marat Dukhan329da642019-11-19 21:44:39 -080064 inline MaxPoolMicrokernelTester& pooling_elements(size_t pooling_elements) {
65 assert(pooling_elements != 0);
66 this->pooling_elements_ = pooling_elements;
XNNPACK Teamb455b122019-09-27 18:10:33 -070067 return *this;
68 }
69
Marat Dukhan329da642019-11-19 21:44:39 -080070 inline size_t pooling_elements() const {
71 return this->pooling_elements_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070072 }
73
Marat Dukhan329da642019-11-19 21:44:39 -080074 inline size_t packed_pooling_elements() const {
75 if (pooling_elements() <= primary_pooling_tile()) {
76 return primary_pooling_tile();
XNNPACK Teamb455b122019-09-27 18:10:33 -070077 } else {
Marat Dukhan329da642019-11-19 21:44:39 -080078 return (pooling_elements() - primary_pooling_tile()) % incremental_pooling_tile() == 0 ? pooling_elements() : ((pooling_elements() - primary_pooling_tile()) / incremental_pooling_tile() + 1) * incremental_pooling_tile() + primary_pooling_tile();
XNNPACK Teamb455b122019-09-27 18:10:33 -070079 }
80 }
81
Marat Dukhan329da642019-11-19 21:44:39 -080082 inline MaxPoolMicrokernelTester& pooling_tile(size_t primary_tile, size_t incremental_tile) {
83 assert(primary_tile != 0);
84 this->primary_pooling_tile_ = primary_tile;
85 this->incremental_pooling_tile_ = incremental_tile;
XNNPACK Teamb455b122019-09-27 18:10:33 -070086 return *this;
87 }
88
Marat Dukhan329da642019-11-19 21:44:39 -080089 inline MaxPoolMicrokernelTester& primary_pooling_tile(size_t primary_pooling_tile) {
90 assert(primary_pooling_tile != 0);
91 this->primary_pooling_tile_ = primary_pooling_tile;
XNNPACK Teamb455b122019-09-27 18:10:33 -070092 return *this;
93 }
94
Marat Dukhan329da642019-11-19 21:44:39 -080095 inline size_t primary_pooling_tile() const {
96 return this->primary_pooling_tile_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070097 }
98
Marat Dukhan329da642019-11-19 21:44:39 -080099 inline MaxPoolMicrokernelTester& incremental_pooling_tile(size_t incremental_pooling_tile) {
100 assert(incremental_pooling_tile != 0);
101 this->incremental_pooling_tile_ = incremental_pooling_tile;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700102 return *this;
103 }
104
Marat Dukhan329da642019-11-19 21:44:39 -0800105 inline size_t incremental_pooling_tile() const {
106 return this->incremental_pooling_tile_;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700107 }
108
Marat Dukhan329da642019-11-19 21:44:39 -0800109 inline MaxPoolMicrokernelTester& channels(size_t channels) {
110 assert(channels != 0);
111 this->channels_ = channels;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700112 return *this;
113 }
114
Marat Dukhan329da642019-11-19 21:44:39 -0800115 inline size_t channels() const {
116 return this->channels_;
117 }
118
119 inline MaxPoolMicrokernelTester& output_stride(size_t output_stride) {
120 assert(output_stride != 0);
121 this->output_stride_ = output_stride;
122 return *this;
123 }
124
125 inline size_t output_stride() const {
126 if (this->output_stride_ == 0) {
127 return channels();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700128 } else {
Marat Dukhan329da642019-11-19 21:44:39 -0800129 assert(this->output_stride_ >= channels());
130 return this->output_stride_;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700131 }
132 }
133
134 inline MaxPoolMicrokernelTester& qmin(uint8_t qmin) {
135 this->qmin_ = qmin;
136 return *this;
137 }
138
139 inline uint8_t qmin() const {
140 return this->qmin_;
141 }
142
143 inline MaxPoolMicrokernelTester& qmax(uint8_t qmax) {
144 this->qmax_ = qmax;
145 return *this;
146 }
147
148 inline uint8_t qmax() const {
149 return this->qmax_;
150 }
151
152 inline MaxPoolMicrokernelTester& iterations(size_t iterations) {
153 this->iterations_ = iterations;
154 return *this;
155 }
156
157 inline size_t iterations() const {
158 return this->iterations_;
159 }
160
161 void Test(xnn_u8_maxpool_ukernel_function maxpool, Variant variant = Variant::Native) const {
162 std::random_device random_device;
163 auto rng = std::mt19937(random_device());
Marat Dukhan5ce30d92020-04-14 03:31:26 -0700164 auto u8rng = std::bind(std::uniform_int_distribution<uint32_t>(0, std::numeric_limits<uint8_t>::max()), rng);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700165
Marat Dukhan329da642019-11-19 21:44:39 -0800166 std::vector<const uint8_t*> indirect_input((output_pixels() - 1) * step() + packed_pooling_elements());
167 std::vector<uint8_t> input(XNN_EXTRA_BYTES / sizeof(uint8_t) +
168 indirect_input.size() * channels());
169 std::vector<uint8_t> output(XNN_EXTRA_BYTES / sizeof(uint8_t) +
170 (output_pixels() - 1) * output_stride() + channels());
171 std::vector<uint8_t> output_ref(output_pixels() * channels());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700172 for (size_t iteration = 0; iteration < iterations(); iteration++) {
Marat Dukhan329da642019-11-19 21:44:39 -0800173 do {
174 std::generate(input.begin(), input.end(), std::ref(u8rng));
175 } while (input.size() > 1 && *std::max_element(input.cbegin(), input.cend()) == *std::min_element(input.cbegin(), input.cend()));
176 std::fill(output.begin(), output.end(), 0xA5);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700177
Marat Dukhan329da642019-11-19 21:44:39 -0800178 for (size_t i = 0; i < (output_pixels() - 1) * step() + pooling_elements(); i++) {
179 indirect_input[i] = input.data() + i * channels() - input_offset();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700180 }
Marat Dukhan329da642019-11-19 21:44:39 -0800181 std::shuffle(indirect_input.begin(),
182 indirect_input.begin() + (output_pixels() - 1) * step() + pooling_elements(), rng);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700183
184 // Prepare output parameters.
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700185 xnn_u8_minmax_params minmax_params = { };
XNNPACK Teamb455b122019-09-27 18:10:33 -0700186 switch (variant) {
187 case Variant::Native:
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700188 minmax_params = xnn_init_u8_minmax_params(qmin(), qmax());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700189 break;
190 case Variant::Scalar:
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700191 minmax_params = xnn_init_scalar_u8_minmax_params(qmin(), qmax());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700192 break;
193 }
194
195 // Compute reference results.
Marat Dukhan329da642019-11-19 21:44:39 -0800196 for (size_t x = 0; x < output_pixels(); x++) {
197 for (size_t c = 0; c < channels(); c++) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700198 uint8_t max_value = 0;
Marat Dukhan329da642019-11-19 21:44:39 -0800199 for (size_t p = 0; p < pooling_elements(); p++) {
200 max_value = std::max(max_value, indirect_input[x * step() + p][c + input_offset()]);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700201 }
202 max_value = std::min(max_value, qmax());
203 max_value = std::max(max_value, qmin());
Marat Dukhan329da642019-11-19 21:44:39 -0800204 output_ref[x * channels() + c] = max_value;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700205 }
206 }
207
208 // Call optimized micro-kernel.
Marat Dukhan329da642019-11-19 21:44:39 -0800209 maxpool(output_pixels(), pooling_elements(), channels(),
210 indirect_input.data(), input_offset() * sizeof(uint8_t), output.data(),
211 (step() - packed_pooling_elements()) * sizeof(void*),
212 (output_stride() - channels()) * sizeof(uint8_t),
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700213 &minmax_params);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700214
215 // Verify results.
Marat Dukhan329da642019-11-19 21:44:39 -0800216 for (size_t x = 0; x < output_pixels(); x++) {
217 for (size_t c = 0; c < channels(); c++) {
218 ASSERT_GE(uint32_t(output[x * output_stride() + c]), uint32_t(qmin()))
219 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
220 << ", pooling elements = " << pooling_elements() << ", step = " << step()
221 << ", input offset = " << input_offset();
222 ASSERT_LE(uint32_t(output[x * output_stride() + c]), uint32_t(qmax()))
223 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
224 << ", pooling elements = " << pooling_elements() << ", step = " << step()
225 << ", input offset = " << input_offset();
226 ASSERT_EQ(uint32_t(output_ref[x * channels() + c]), uint32_t(output[x * output_stride() + c]))
227 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
228 << ", pooling elements = " << pooling_elements() << ", step = " << step()
229 << ", input offset = " << input_offset();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700230 }
231 }
232 }
233 }
234
235 void Test(xnn_f32_maxpool_ukernel_function maxpool, Variant variant = Variant::Native) const {
236 std::random_device random_device;
237 auto rng = std::mt19937(random_device());
238 auto f32rng = std::bind(std::uniform_real_distribution<float>(0.0f, 1.0f), rng);
239
Marat Dukhan329da642019-11-19 21:44:39 -0800240 std::vector<const float*> indirect_input((output_pixels() - 1) * step() + packed_pooling_elements());
241 std::vector<float> input(XNN_EXTRA_BYTES / sizeof(float) +
242 ((output_pixels() - 1) * step() + pooling_elements()) * channels());
243 std::vector<float> output(XNN_EXTRA_BYTES / sizeof(float) +
244 (output_pixels() - 1) * output_stride() + channels());
245 std::vector<float> output_ref(output_pixels() * channels());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700246 for (size_t iteration = 0; iteration < iterations(); iteration++) {
Marat Dukhan329da642019-11-19 21:44:39 -0800247 std::generate(input.begin(), input.end(), std::ref(f32rng));
248 std::fill(output.begin(), output.end(), nanf(""));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700249
Marat Dukhan329da642019-11-19 21:44:39 -0800250 for (size_t i = 0; i < (output_pixels() - 1) * step() + pooling_elements(); i++) {
251 indirect_input[i] = input.data() + i * channels() - input_offset();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700252 }
Marat Dukhan329da642019-11-19 21:44:39 -0800253 std::shuffle(indirect_input.begin(),
254 indirect_input.begin() + (output_pixels() - 1) * step() + pooling_elements(), rng);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700255
256 // Compute reference results, without clamping.
Marat Dukhan329da642019-11-19 21:44:39 -0800257 for (size_t x = 0; x < output_pixels(); x++) {
258 for (size_t c = 0; c < channels(); c++) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700259 float max_value = -std::numeric_limits<float>::infinity();
Marat Dukhan329da642019-11-19 21:44:39 -0800260 for (size_t p = 0; p < pooling_elements(); p++) {
261 max_value = std::max(max_value, indirect_input[x * step() + p][c + input_offset()]);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700262 }
Marat Dukhan329da642019-11-19 21:44:39 -0800263 output_ref[x * channels() + c] = max_value;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700264 }
265 }
266
267 // Compute clamping parameters.
Marat Dukhan329da642019-11-19 21:44:39 -0800268 const float accumulated_min = *std::min_element(output_ref.cbegin(), output_ref.cend());
269 const float accumulated_max = *std::max_element(output_ref.cbegin(), output_ref.cend());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700270 const float accumulated_range = accumulated_max - accumulated_min;
Marat Dukhan329da642019-11-19 21:44:39 -0800271 const float output_min = accumulated_min + float(qmin()) / 255.0f * accumulated_range;
272 const float output_max = accumulated_max - float(255 - qmax()) / 255.0f * accumulated_range;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700273
274
275 // Prepare output parameters.
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700276 xnn_f32_minmax_params minmax_params = { };
XNNPACK Teamb455b122019-09-27 18:10:33 -0700277 switch (variant) {
278 case Variant::Native:
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700279 minmax_params = xnn_init_f32_minmax_params(output_min, output_max);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700280 break;
281 case Variant::Scalar:
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700282 minmax_params = xnn_init_scalar_f32_minmax_params(output_min, output_max);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700283 break;
284 }
285
286 // Clamp reference results.
Marat Dukhan329da642019-11-19 21:44:39 -0800287 for (float& output_value : output_ref) {
288 output_value = std::max(std::min(output_value, output_max), output_min);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700289 }
290
291 // Call optimized micro-kernel.
Marat Dukhan329da642019-11-19 21:44:39 -0800292 maxpool(output_pixels(), pooling_elements(), channels(),
293 indirect_input.data(), input_offset() * sizeof(float), output.data(),
294 (step() - packed_pooling_elements()) * sizeof(void*),
295 (output_stride() - channels()) * sizeof(float),
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700296 &minmax_params);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700297
298 // Verify results.
Marat Dukhan329da642019-11-19 21:44:39 -0800299 for (size_t x = 0; x < output_pixels(); x++) {
300 for (size_t c = 0; c < channels(); c++) {
301 ASSERT_GE(output[x * output_stride() + c], output_min)
302 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
303 << ", pooling elements = " << pooling_elements() << ", step = " << step()
304 << ", input offset = " << input_offset();
305 ASSERT_LE(output[x * output_stride() + c], output_max)
306 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
307 << ", pooling elements = " << pooling_elements() << ", step = " << step()
308 << ", input offset = " << input_offset();
309 ASSERT_EQ(output_ref[x * channels() + c], output[x * output_stride() + c])
310 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
311 << ", pooling elements = " << pooling_elements() << ", step = " << step()
312 << ", input offset = " << input_offset();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700313 }
314 }
315 }
316 }
317
318 private:
Marat Dukhan329da642019-11-19 21:44:39 -0800319 size_t output_pixels_{1};
320 size_t pooling_elements_{1};
321 size_t channels_{1};
322 size_t input_offset_{0};
323 size_t step_{1};
324 size_t primary_pooling_tile_{1};
325 size_t incremental_pooling_tile_{1};
326 size_t output_stride_{0};
XNNPACK Teamb455b122019-09-27 18:10:33 -0700327 uint8_t qmin_{0};
328 uint8_t qmax_{255};
Marat Dukhan329da642019-11-19 21:44:39 -0800329 size_t iterations_{3};
XNNPACK Teamb455b122019-09-27 18:10:33 -0700330};