blob: 1124095598557992ec66f0fd54ea86e75f65202d [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>
18#include <random>
19#include <vector>
20
21#include <xnnpack.h>
Marat Dukhaneeaa7bd2019-10-25 17:31:25 -070022#include <xnnpack/params-init.h>
Frank Barcharde0601b52019-10-25 17:43:34 -070023#include <xnnpack/params.h>
XNNPACK Teamb455b122019-09-27 18:10:33 -070024
25
26class MaxPoolMicrokernelTester {
27 public:
28 enum class Variant {
29 Native,
30 Scalar,
31 };
32
Marat Dukhan329da642019-11-19 21:44:39 -080033 inline MaxPoolMicrokernelTester& output_pixels(size_t output_pixels) {
34 assert(output_pixels != 0);
35 this->output_pixels_ = output_pixels;
XNNPACK Teamb455b122019-09-27 18:10:33 -070036 return *this;
37 }
38
Marat Dukhan329da642019-11-19 21:44:39 -080039 inline size_t output_pixels() const {
40 return this->output_pixels_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070041 }
42
Marat Dukhan329da642019-11-19 21:44:39 -080043 inline MaxPoolMicrokernelTester& step(size_t step) {
44 assert(step != 0);
45 this->step_ = step;
XNNPACK Teamb455b122019-09-27 18:10:33 -070046 return *this;
47 }
48
Marat Dukhan329da642019-11-19 21:44:39 -080049 inline size_t step() const {
50 return this->step_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070051 }
52
Marat Dukhan329da642019-11-19 21:44:39 -080053 inline MaxPoolMicrokernelTester& input_offset(size_t input_offset) {
54 assert(input_offset != 0);
55 this->input_offset_ = input_offset;
XNNPACK Teamb455b122019-09-27 18:10:33 -070056 return *this;
57 }
58
Marat Dukhan329da642019-11-19 21:44:39 -080059 inline size_t input_offset() const {
60 return this->input_offset_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070061 }
62
Marat Dukhan329da642019-11-19 21:44:39 -080063 inline MaxPoolMicrokernelTester& pooling_elements(size_t pooling_elements) {
64 assert(pooling_elements != 0);
65 this->pooling_elements_ = pooling_elements;
XNNPACK Teamb455b122019-09-27 18:10:33 -070066 return *this;
67 }
68
Marat Dukhan329da642019-11-19 21:44:39 -080069 inline size_t pooling_elements() const {
70 return this->pooling_elements_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070071 }
72
Marat Dukhan329da642019-11-19 21:44:39 -080073 inline size_t packed_pooling_elements() const {
74 if (pooling_elements() <= primary_pooling_tile()) {
75 return primary_pooling_tile();
XNNPACK Teamb455b122019-09-27 18:10:33 -070076 } else {
Marat Dukhan329da642019-11-19 21:44:39 -080077 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 -070078 }
79 }
80
Marat Dukhan329da642019-11-19 21:44:39 -080081 inline MaxPoolMicrokernelTester& pooling_tile(size_t primary_tile, size_t incremental_tile) {
82 assert(primary_tile != 0);
83 this->primary_pooling_tile_ = primary_tile;
84 this->incremental_pooling_tile_ = incremental_tile;
XNNPACK Teamb455b122019-09-27 18:10:33 -070085 return *this;
86 }
87
Marat Dukhan329da642019-11-19 21:44:39 -080088 inline MaxPoolMicrokernelTester& primary_pooling_tile(size_t primary_pooling_tile) {
89 assert(primary_pooling_tile != 0);
90 this->primary_pooling_tile_ = primary_pooling_tile;
XNNPACK Teamb455b122019-09-27 18:10:33 -070091 return *this;
92 }
93
Marat Dukhan329da642019-11-19 21:44:39 -080094 inline size_t primary_pooling_tile() const {
95 return this->primary_pooling_tile_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070096 }
97
Marat Dukhan329da642019-11-19 21:44:39 -080098 inline MaxPoolMicrokernelTester& incremental_pooling_tile(size_t incremental_pooling_tile) {
99 assert(incremental_pooling_tile != 0);
100 this->incremental_pooling_tile_ = incremental_pooling_tile;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700101 return *this;
102 }
103
Marat Dukhan329da642019-11-19 21:44:39 -0800104 inline size_t incremental_pooling_tile() const {
105 return this->incremental_pooling_tile_;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700106 }
107
Marat Dukhan329da642019-11-19 21:44:39 -0800108 inline MaxPoolMicrokernelTester& channels(size_t channels) {
109 assert(channels != 0);
110 this->channels_ = channels;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700111 return *this;
112 }
113
Marat Dukhan329da642019-11-19 21:44:39 -0800114 inline size_t channels() const {
115 return this->channels_;
116 }
117
118 inline MaxPoolMicrokernelTester& output_stride(size_t output_stride) {
119 assert(output_stride != 0);
120 this->output_stride_ = output_stride;
121 return *this;
122 }
123
124 inline size_t output_stride() const {
125 if (this->output_stride_ == 0) {
126 return channels();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700127 } else {
Marat Dukhan329da642019-11-19 21:44:39 -0800128 assert(this->output_stride_ >= channels());
129 return this->output_stride_;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700130 }
131 }
132
133 inline MaxPoolMicrokernelTester& qmin(uint8_t qmin) {
134 this->qmin_ = qmin;
135 return *this;
136 }
137
138 inline uint8_t qmin() const {
139 return this->qmin_;
140 }
141
142 inline MaxPoolMicrokernelTester& qmax(uint8_t qmax) {
143 this->qmax_ = qmax;
144 return *this;
145 }
146
147 inline uint8_t qmax() const {
148 return this->qmax_;
149 }
150
151 inline MaxPoolMicrokernelTester& iterations(size_t iterations) {
152 this->iterations_ = iterations;
153 return *this;
154 }
155
156 inline size_t iterations() const {
157 return this->iterations_;
158 }
159
160 void Test(xnn_u8_maxpool_ukernel_function maxpool, Variant variant = Variant::Native) const {
161 std::random_device random_device;
162 auto rng = std::mt19937(random_device());
163 auto u8rng = std::bind(std::uniform_int_distribution<uint8_t>(), rng);
164
Marat Dukhan329da642019-11-19 21:44:39 -0800165 std::vector<const uint8_t*> indirect_input((output_pixels() - 1) * step() + packed_pooling_elements());
166 std::vector<uint8_t> input(XNN_EXTRA_BYTES / sizeof(uint8_t) +
167 indirect_input.size() * channels());
168 std::vector<uint8_t> output(XNN_EXTRA_BYTES / sizeof(uint8_t) +
169 (output_pixels() - 1) * output_stride() + channels());
170 std::vector<uint8_t> output_ref(output_pixels() * channels());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700171 for (size_t iteration = 0; iteration < iterations(); iteration++) {
Marat Dukhan329da642019-11-19 21:44:39 -0800172 do {
173 std::generate(input.begin(), input.end(), std::ref(u8rng));
174 } while (input.size() > 1 && *std::max_element(input.cbegin(), input.cend()) == *std::min_element(input.cbegin(), input.cend()));
175 std::fill(output.begin(), output.end(), 0xA5);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700176
Marat Dukhan329da642019-11-19 21:44:39 -0800177 for (size_t i = 0; i < (output_pixels() - 1) * step() + pooling_elements(); i++) {
178 indirect_input[i] = input.data() + i * channels() - input_offset();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700179 }
Marat Dukhan329da642019-11-19 21:44:39 -0800180 std::shuffle(indirect_input.begin(),
181 indirect_input.begin() + (output_pixels() - 1) * step() + pooling_elements(), rng);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700182
183 // Prepare output parameters.
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700184 xnn_u8_minmax_params minmax_params = { };
XNNPACK Teamb455b122019-09-27 18:10:33 -0700185 switch (variant) {
186 case Variant::Native:
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700187 minmax_params = xnn_init_u8_minmax_params(qmin(), qmax());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700188 break;
189 case Variant::Scalar:
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700190 minmax_params = xnn_init_scalar_u8_minmax_params(qmin(), qmax());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700191 break;
192 }
193
194 // Compute reference results.
Marat Dukhan329da642019-11-19 21:44:39 -0800195 for (size_t x = 0; x < output_pixels(); x++) {
196 for (size_t c = 0; c < channels(); c++) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700197 uint8_t max_value = 0;
Marat Dukhan329da642019-11-19 21:44:39 -0800198 for (size_t p = 0; p < pooling_elements(); p++) {
199 max_value = std::max(max_value, indirect_input[x * step() + p][c + input_offset()]);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700200 }
201 max_value = std::min(max_value, qmax());
202 max_value = std::max(max_value, qmin());
Marat Dukhan329da642019-11-19 21:44:39 -0800203 output_ref[x * channels() + c] = max_value;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700204 }
205 }
206
207 // Call optimized micro-kernel.
Marat Dukhan329da642019-11-19 21:44:39 -0800208 maxpool(output_pixels(), pooling_elements(), channels(),
209 indirect_input.data(), input_offset() * sizeof(uint8_t), output.data(),
210 (step() - packed_pooling_elements()) * sizeof(void*),
211 (output_stride() - channels()) * sizeof(uint8_t),
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700212 &minmax_params);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700213
214 // Verify results.
Marat Dukhan329da642019-11-19 21:44:39 -0800215 for (size_t x = 0; x < output_pixels(); x++) {
216 for (size_t c = 0; c < channels(); c++) {
217 ASSERT_GE(uint32_t(output[x * output_stride() + c]), uint32_t(qmin()))
218 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
219 << ", pooling elements = " << pooling_elements() << ", step = " << step()
220 << ", input offset = " << input_offset();
221 ASSERT_LE(uint32_t(output[x * output_stride() + c]), uint32_t(qmax()))
222 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
223 << ", pooling elements = " << pooling_elements() << ", step = " << step()
224 << ", input offset = " << input_offset();
225 ASSERT_EQ(uint32_t(output_ref[x * channels() + c]), uint32_t(output[x * output_stride() + c]))
226 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
227 << ", pooling elements = " << pooling_elements() << ", step = " << step()
228 << ", input offset = " << input_offset();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700229 }
230 }
231 }
232 }
233
234 void Test(xnn_f32_maxpool_ukernel_function maxpool, Variant variant = Variant::Native) const {
235 std::random_device random_device;
236 auto rng = std::mt19937(random_device());
237 auto f32rng = std::bind(std::uniform_real_distribution<float>(0.0f, 1.0f), rng);
238
Marat Dukhan329da642019-11-19 21:44:39 -0800239 std::vector<const float*> indirect_input((output_pixels() - 1) * step() + packed_pooling_elements());
240 std::vector<float> input(XNN_EXTRA_BYTES / sizeof(float) +
241 ((output_pixels() - 1) * step() + pooling_elements()) * channels());
242 std::vector<float> output(XNN_EXTRA_BYTES / sizeof(float) +
243 (output_pixels() - 1) * output_stride() + channels());
244 std::vector<float> output_ref(output_pixels() * channels());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700245 for (size_t iteration = 0; iteration < iterations(); iteration++) {
Marat Dukhan329da642019-11-19 21:44:39 -0800246 std::generate(input.begin(), input.end(), std::ref(f32rng));
247 std::fill(output.begin(), output.end(), nanf(""));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700248
Marat Dukhan329da642019-11-19 21:44:39 -0800249 for (size_t i = 0; i < (output_pixels() - 1) * step() + pooling_elements(); i++) {
250 indirect_input[i] = input.data() + i * channels() - input_offset();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700251 }
Marat Dukhan329da642019-11-19 21:44:39 -0800252 std::shuffle(indirect_input.begin(),
253 indirect_input.begin() + (output_pixels() - 1) * step() + pooling_elements(), rng);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700254
255 // Compute reference results, without clamping.
Marat Dukhan329da642019-11-19 21:44:39 -0800256 for (size_t x = 0; x < output_pixels(); x++) {
257 for (size_t c = 0; c < channels(); c++) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700258 float max_value = -std::numeric_limits<float>::infinity();
Marat Dukhan329da642019-11-19 21:44:39 -0800259 for (size_t p = 0; p < pooling_elements(); p++) {
260 max_value = std::max(max_value, indirect_input[x * step() + p][c + input_offset()]);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700261 }
Marat Dukhan329da642019-11-19 21:44:39 -0800262 output_ref[x * channels() + c] = max_value;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700263 }
264 }
265
266 // Compute clamping parameters.
Marat Dukhan329da642019-11-19 21:44:39 -0800267 const float accumulated_min = *std::min_element(output_ref.cbegin(), output_ref.cend());
268 const float accumulated_max = *std::max_element(output_ref.cbegin(), output_ref.cend());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700269 const float accumulated_range = accumulated_max - accumulated_min;
Marat Dukhan329da642019-11-19 21:44:39 -0800270 const float output_min = accumulated_min + float(qmin()) / 255.0f * accumulated_range;
271 const float output_max = accumulated_max - float(255 - qmax()) / 255.0f * accumulated_range;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700272
273
274 // Prepare output parameters.
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700275 xnn_f32_minmax_params minmax_params = { };
XNNPACK Teamb455b122019-09-27 18:10:33 -0700276 switch (variant) {
277 case Variant::Native:
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700278 minmax_params = xnn_init_f32_minmax_params(output_min, output_max);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700279 break;
280 case Variant::Scalar:
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700281 minmax_params = xnn_init_scalar_f32_minmax_params(output_min, output_max);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700282 break;
283 }
284
285 // Clamp reference results.
Marat Dukhan329da642019-11-19 21:44:39 -0800286 for (float& output_value : output_ref) {
287 output_value = std::max(std::min(output_value, output_max), output_min);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700288 }
289
290 // Call optimized micro-kernel.
Marat Dukhan329da642019-11-19 21:44:39 -0800291 maxpool(output_pixels(), pooling_elements(), channels(),
292 indirect_input.data(), input_offset() * sizeof(float), output.data(),
293 (step() - packed_pooling_elements()) * sizeof(void*),
294 (output_stride() - channels()) * sizeof(float),
Marat Dukhaneb09a6b2020-04-08 17:34:32 -0700295 &minmax_params);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700296
297 // Verify results.
Marat Dukhan329da642019-11-19 21:44:39 -0800298 for (size_t x = 0; x < output_pixels(); x++) {
299 for (size_t c = 0; c < channels(); c++) {
300 ASSERT_GE(output[x * output_stride() + c], output_min)
301 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
302 << ", pooling elements = " << pooling_elements() << ", step = " << step()
303 << ", input offset = " << input_offset();
304 ASSERT_LE(output[x * output_stride() + c], output_max)
305 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
306 << ", pooling elements = " << pooling_elements() << ", step = " << step()
307 << ", input offset = " << input_offset();
308 ASSERT_EQ(output_ref[x * channels() + c], output[x * output_stride() + c])
309 << "at pixel " << x << " / " << output_pixels() << ", channel " << c << " / " << channels()
310 << ", pooling elements = " << pooling_elements() << ", step = " << step()
311 << ", input offset = " << input_offset();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700312 }
313 }
314 }
315 }
316
317 private:
Marat Dukhan329da642019-11-19 21:44:39 -0800318 size_t output_pixels_{1};
319 size_t pooling_elements_{1};
320 size_t channels_{1};
321 size_t input_offset_{0};
322 size_t step_{1};
323 size_t primary_pooling_tile_{1};
324 size_t incremental_pooling_tile_{1};
325 size_t output_stride_{0};
XNNPACK Teamb455b122019-09-27 18:10:33 -0700326 uint8_t qmin_{0};
327 uint8_t qmax_{255};
Marat Dukhan329da642019-11-19 21:44:39 -0800328 size_t iterations_{3};
XNNPACK Teamb455b122019-09-27 18:10:33 -0700329};