blob: ddd02357d6cf7e4744e6a1ef9189ba760024b6a1 [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>
XNNPACK Teamb455b122019-09-27 18:10:33 -070024#include <xnnpack/params.h>
25#include <xnnpack/requantization.h>
26
27
28class VAddMicrokernelTester {
29 public:
Marat Dukhand9f3ad42020-08-10 12:30:58 -070030 inline VAddMicrokernelTester& batch_size(size_t batch_size) {
31 assert(batch_size != 0);
32 this->batch_size_ = batch_size;
XNNPACK Teamb455b122019-09-27 18:10:33 -070033 return *this;
34 }
35
Marat Dukhand9f3ad42020-08-10 12:30:58 -070036 inline size_t batch_size() const {
37 return this->batch_size_;
XNNPACK Teamb455b122019-09-27 18:10:33 -070038 }
39
40 inline VAddMicrokernelTester& inplace_a(bool inplace_a) {
41 this->inplace_a_ = inplace_a;
42 return *this;
43 }
44
45 inline bool inplace_a() const {
46 return this->inplace_a_;
47 }
48
49 inline VAddMicrokernelTester& inplace_b(bool inplace_b) {
50 this->inplace_b_ = inplace_b;
51 return *this;
52 }
53
54 inline bool inplace_b() const {
55 return this->inplace_b_;
56 }
57
58 inline VAddMicrokernelTester& a_scale(float a_scale) {
59 assert(a_scale > 0.0f);
60 assert(std::isnormal(a_scale));
61 this->a_scale_ = a_scale;
62 return *this;
63 }
64
65 inline float a_scale() const {
66 return this->a_scale_;
67 }
68
69 inline VAddMicrokernelTester& a_zero_point(uint8_t a_zero_point) {
70 this->a_zero_point_ = a_zero_point;
71 return *this;
72 }
73
74 inline uint8_t a_zero_point() const {
75 return this->a_zero_point_;
76 }
77
78 inline VAddMicrokernelTester& b_scale(float b_scale) {
79 assert(b_scale > 0.0f);
80 assert(std::isnormal(b_scale));
81 this->b_scale_ = b_scale;
82 return *this;
83 }
84
85 inline float b_scale() const {
86 return this->b_scale_;
87 }
88
89 inline VAddMicrokernelTester& b_zero_point(uint8_t b_zero_point) {
90 this->b_zero_point_ = b_zero_point;
91 return *this;
92 }
93
94 inline uint8_t b_zero_point() const {
95 return this->b_zero_point_;
96 }
97
98 inline VAddMicrokernelTester& y_scale(float y_scale) {
99 assert(y_scale > 0.0f);
100 assert(std::isnormal(y_scale));
101 this->y_scale_ = y_scale;
102 return *this;
103 }
104
105 inline float y_scale() const {
106 return this->y_scale_;
107 }
108
109 inline VAddMicrokernelTester& y_zero_point(uint8_t y_zero_point) {
110 this->y_zero_point_ = y_zero_point;
111 return *this;
112 }
113
114 inline uint8_t y_zero_point() const {
115 return this->y_zero_point_;
116 }
117
118 inline VAddMicrokernelTester& qmin(uint8_t qmin) {
119 this->qmin_ = qmin;
120 return *this;
121 }
122
123 inline uint8_t qmin() const {
124 return this->qmin_;
125 }
126
127 inline VAddMicrokernelTester& qmax(uint8_t qmax) {
128 this->qmax_ = qmax;
129 return *this;
130 }
131
132 inline uint8_t qmax() const {
133 return this->qmax_;
134 }
135
136 inline VAddMicrokernelTester& iterations(size_t iterations) {
137 this->iterations_ = iterations;
138 return *this;
139 }
140
141 inline size_t iterations() const {
142 return this->iterations_;
143 }
144
Marat Dukhan64287252021-09-07 16:20:03 -0700145 void Test(xnn_qu8_vaddsub_minmax_ukernel_function vadd_minmax, xnn_init_qu8_addsub_minmax_params_fn init_params) const {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700146 std::random_device random_device;
147 auto rng = std::mt19937(random_device());
Marat Dukhan5ce30d92020-04-14 03:31:26 -0700148 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 -0700149
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700150 std::vector<uint8_t> a(batch_size() + XNN_EXTRA_BYTES / sizeof(uint8_t));
151 std::vector<uint8_t> b(batch_size() + XNN_EXTRA_BYTES / sizeof(uint8_t));
152 std::vector<uint8_t> y(batch_size() + (inplace_a() || inplace_b() ? XNN_EXTRA_BYTES / sizeof(uint8_t) : 0));
153 std::vector<float> y_fp(batch_size());
154 std::vector<uint8_t> y_ref(batch_size());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700155 for (size_t iteration = 0; iteration < iterations(); iteration++) {
156 std::generate(a.begin(), a.end(), std::ref(u8rng));
157 std::generate(b.begin(), b.end(), std::ref(u8rng));
158 if (inplace_a() || inplace_b()) {
159 std::generate(y.begin(), y.end(), std::ref(u8rng));
160 } else {
161 std::fill(y.begin(), y.end(), 0xA5);
162 }
163 const uint8_t* a_data = inplace_a() ? y.data() : a.data();
164 const uint8_t* b_data = inplace_b() ? y.data() : b.data();
165
Frank Barchard9f3a8432020-06-02 13:59:35 -0700166 // Prepare parameters.
Marat Dukhan64287252021-09-07 16:20:03 -0700167 xnn_qu8_addsub_minmax_params quantization_params;
Marat Dukhan6e0fc392021-07-19 18:38:24 -0700168 init_params(
169 &quantization_params,
170 a_zero_point(), b_zero_point(), y_zero_point(),
171 a_scale() / y_scale(), b_scale() / y_scale(),
172 qmin(), qmax());
Marat Dukhan64287252021-09-07 16:20:03 -0700173 xnn_qu8_addsub_minmax_params scalar_quantization_params;
Marat Dukhan6e0fc392021-07-19 18:38:24 -0700174 xnn_init_qu8_add_minmax_scalar_params(
Marat Dukhanf56f4c42021-05-17 01:47:20 -0700175 &scalar_quantization_params,
176 a_zero_point(), b_zero_point(), y_zero_point(),
177 a_scale() / y_scale(), b_scale() / y_scale(),
178 qmin(), qmax());
XNNPACK Teamb455b122019-09-27 18:10:33 -0700179
180 // Compute reference results.
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700181 for (size_t i = 0; i < batch_size(); i++) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700182 y_fp[i] = float(y_zero_point()) +
183 float(int32_t(a_data[i]) - int32_t(a_zero_point())) * (a_scale() / y_scale()) +
184 float(int32_t(b_data[i]) - int32_t(b_zero_point())) * (b_scale() / y_scale());
185 y_fp[i] = std::min<float>(y_fp[i], float(qmax()));
186 y_fp[i] = std::max<float>(y_fp[i], float(qmin()));
Marat Dukhan5b69f8b2020-07-24 15:26:48 -0700187 y_ref[i] = xnn_qu8_quantize_add(a_data[i], b_data[i], scalar_quantization_params);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700188 }
189
190 // Call optimized micro-kernel.
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700191 vadd_minmax(batch_size(), a_data, b_data, y.data(), &quantization_params);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700192
193 // Verify results.
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700194 for (size_t i = 0; i < batch_size(); i++) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700195 ASSERT_LE(uint32_t(y[i]), uint32_t(qmax()))
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700196 << "at element " << i << " / " << batch_size();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700197 ASSERT_GE(uint32_t(y[i]), uint32_t(qmin()))
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700198 << "at element " << i << " / " << batch_size();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700199 ASSERT_NEAR(float(int32_t(y[i])), y_fp[i], 0.6f)
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700200 << "at element " << i << " / " << batch_size();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700201 ASSERT_EQ(uint32_t(y_ref[i]), uint32_t(y[i]))
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700202 << "at element " << i << " / " << batch_size();
203 }
204 }
205 }
206
Marat Dukhan64287252021-09-07 16:20:03 -0700207 void Test(xnn_qs8_vaddsub_minmax_ukernel_function vadd_minmax, xnn_init_qs8_addsub_minmax_params_fn init_params) const {
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700208 std::random_device random_device;
209 auto rng = std::mt19937(random_device());
210 auto i8rng = std::bind(
211 std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()), rng);
212
213 std::vector<int8_t> a(batch_size() + XNN_EXTRA_BYTES / sizeof(int8_t));
214 std::vector<int8_t> b(batch_size() + XNN_EXTRA_BYTES / sizeof(int8_t));
215 std::vector<int8_t> y(batch_size() + (inplace_a() || inplace_b() ? XNN_EXTRA_BYTES / sizeof(int8_t) : 0));
216 std::vector<float> y_fp(batch_size());
217 std::vector<int8_t> y_ref(batch_size());
218 for (size_t iteration = 0; iteration < iterations(); iteration++) {
219 std::generate(a.begin(), a.end(), std::ref(i8rng));
220 std::generate(b.begin(), b.end(), std::ref(i8rng));
221 if (inplace_a() || inplace_b()) {
222 std::generate(y.begin(), y.end(), std::ref(i8rng));
223 } else {
224 std::fill(y.begin(), y.end(), 0xA5);
225 }
226 const int8_t* a_data = inplace_a() ? y.data() : a.data();
227 const int8_t* b_data = inplace_b() ? y.data() : b.data();
228
229 // Prepare parameters.
Marat Dukhan64287252021-09-07 16:20:03 -0700230 xnn_qs8_addsub_minmax_params quantization_params;
Marat Dukhan6e0fc392021-07-19 18:38:24 -0700231 init_params(
232 &quantization_params,
233 int8_t(a_zero_point() - 0x80), int8_t(b_zero_point() - 0x80), int8_t(y_zero_point() - 0x80),
234 a_scale() / y_scale(), b_scale() / y_scale(),
235 int8_t(qmin() - 0x80), int8_t(qmax() - 0x80));
Marat Dukhan64287252021-09-07 16:20:03 -0700236 xnn_qs8_addsub_minmax_params scalar_quantization_params;
Marat Dukhan6e0fc392021-07-19 18:38:24 -0700237 xnn_init_qs8_add_minmax_scalar_params(
Marat Dukhanf56f4c42021-05-17 01:47:20 -0700238 &scalar_quantization_params,
239 int8_t(a_zero_point() - 0x80), int8_t(b_zero_point() - 0x80), int8_t(y_zero_point() - 0x80),
240 a_scale() / y_scale(), b_scale() / y_scale(),
241 int8_t(qmin() - 0x80), int8_t(qmax() - 0x80));
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700242
243 // Compute reference results.
244 for (size_t i = 0; i < batch_size(); i++) {
245 y_fp[i] = float(int32_t(y_zero_point() - 0x80)) +
246 float(int32_t(a_data[i]) - int32_t(a_zero_point() - 0x80)) * (a_scale() / y_scale()) +
247 float(int32_t(b_data[i]) - int32_t(b_zero_point() - 0x80)) * (b_scale() / y_scale());
248 y_fp[i] = std::min<float>(y_fp[i], float(int32_t(qmax() - 0x80)));
249 y_fp[i] = std::max<float>(y_fp[i], float(int32_t(qmin() - 0x80)));
250 y_ref[i] = xnn_qs8_quantize_add(a_data[i], b_data[i], scalar_quantization_params);
251 }
252
253 // Call optimized micro-kernel.
254 vadd_minmax(batch_size(), a_data, b_data, y.data(), &quantization_params);
255
256 // Verify results.
257 for (size_t i = 0; i < batch_size(); i++) {
258 ASSERT_LE(int32_t(y[i]), int32_t(qmax() - 0x80))
259 << "at element " << i << " / " << batch_size();
260 ASSERT_GE(int32_t(y[i]), int32_t(qmin() - 0x80))
261 << "at element " << i << " / " << batch_size();
262 ASSERT_EQ(int32_t(y_ref[i]), int32_t(y[i]))
263 << "at element " << i << " / " << batch_size();
264 ASSERT_NEAR(float(int32_t(y[i])), y_fp[i], 0.6f)
265 << "at element " << i << " / " << batch_size();
XNNPACK Teamb455b122019-09-27 18:10:33 -0700266 }
267 }
268 }
269
XNNPACK Teamb455b122019-09-27 18:10:33 -0700270 private:
Marat Dukhand9f3ad42020-08-10 12:30:58 -0700271 size_t batch_size_{1};
XNNPACK Teamb455b122019-09-27 18:10:33 -0700272 bool inplace_a_{false};
273 bool inplace_b_{false};
274 float a_scale_{0.75f};
275 float b_scale_{1.25f};
276 float y_scale_{0.96875f};
277 uint8_t a_zero_point_{121};
278 uint8_t b_zero_point_{127};
279 uint8_t y_zero_point_{133};
280 uint8_t qmin_{0};
281 uint8_t qmax_{255};
282 size_t iterations_{15};
283};