blob: d7eb936d7fe6af4351bef213d24bae1e9e7d7d39 [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>
XNNPACK Teamb455b122019-09-27 18:10:33 -070023#include <xnnpack/params.h>
24#include <xnnpack/requantization.h>
25
26
27class VAddMicrokernelTester {
28 public:
29 enum class Variant {
30 Native,
31 Scalar,
32 };
33
34 inline VAddMicrokernelTester& n(size_t n) {
35 assert(n != 0);
36 this->n_ = n;
37 return *this;
38 }
39
40 inline size_t n() const {
41 return this->n_;
42 }
43
44 inline VAddMicrokernelTester& inplace_a(bool inplace_a) {
45 this->inplace_a_ = inplace_a;
46 return *this;
47 }
48
49 inline bool inplace_a() const {
50 return this->inplace_a_;
51 }
52
53 inline VAddMicrokernelTester& inplace_b(bool inplace_b) {
54 this->inplace_b_ = inplace_b;
55 return *this;
56 }
57
58 inline bool inplace_b() const {
59 return this->inplace_b_;
60 }
61
62 inline VAddMicrokernelTester& a_scale(float a_scale) {
63 assert(a_scale > 0.0f);
64 assert(std::isnormal(a_scale));
65 this->a_scale_ = a_scale;
66 return *this;
67 }
68
69 inline float a_scale() const {
70 return this->a_scale_;
71 }
72
73 inline VAddMicrokernelTester& a_zero_point(uint8_t a_zero_point) {
74 this->a_zero_point_ = a_zero_point;
75 return *this;
76 }
77
78 inline uint8_t a_zero_point() const {
79 return this->a_zero_point_;
80 }
81
82 inline VAddMicrokernelTester& b_scale(float b_scale) {
83 assert(b_scale > 0.0f);
84 assert(std::isnormal(b_scale));
85 this->b_scale_ = b_scale;
86 return *this;
87 }
88
89 inline float b_scale() const {
90 return this->b_scale_;
91 }
92
93 inline VAddMicrokernelTester& b_zero_point(uint8_t b_zero_point) {
94 this->b_zero_point_ = b_zero_point;
95 return *this;
96 }
97
98 inline uint8_t b_zero_point() const {
99 return this->b_zero_point_;
100 }
101
102 inline VAddMicrokernelTester& y_scale(float y_scale) {
103 assert(y_scale > 0.0f);
104 assert(std::isnormal(y_scale));
105 this->y_scale_ = y_scale;
106 return *this;
107 }
108
109 inline float y_scale() const {
110 return this->y_scale_;
111 }
112
113 inline VAddMicrokernelTester& y_zero_point(uint8_t y_zero_point) {
114 this->y_zero_point_ = y_zero_point;
115 return *this;
116 }
117
118 inline uint8_t y_zero_point() const {
119 return this->y_zero_point_;
120 }
121
122 inline VAddMicrokernelTester& qmin(uint8_t qmin) {
123 this->qmin_ = qmin;
124 return *this;
125 }
126
127 inline uint8_t qmin() const {
128 return this->qmin_;
129 }
130
131 inline VAddMicrokernelTester& qmax(uint8_t qmax) {
132 this->qmax_ = qmax;
133 return *this;
134 }
135
136 inline uint8_t qmax() const {
137 return this->qmax_;
138 }
139
140 inline VAddMicrokernelTester& iterations(size_t iterations) {
141 this->iterations_ = iterations;
142 return *this;
143 }
144
145 inline size_t iterations() const {
146 return this->iterations_;
147 }
148
149 void Test(xnn_q8_vadd_ukernel_function vadd, Variant variant = Variant::Native) const {
150 std::random_device random_device;
151 auto rng = std::mt19937(random_device());
152 auto u8rng = std::bind(std::uniform_int_distribution<uint8_t>(), rng);
153
154 std::vector<uint8_t> a(n() + XNN_EXTRA_BYTES / sizeof(uint8_t));
155 std::vector<uint8_t> b(n() + XNN_EXTRA_BYTES / sizeof(uint8_t));
156 std::vector<uint8_t> y(n() + (inplace_a() || inplace_b() ? XNN_EXTRA_BYTES / sizeof(uint8_t) : 0));
157 std::vector<float> y_fp(n());
158 std::vector<uint8_t> y_ref(n());
159 for (size_t iteration = 0; iteration < iterations(); iteration++) {
160 std::generate(a.begin(), a.end(), std::ref(u8rng));
161 std::generate(b.begin(), b.end(), std::ref(u8rng));
162 if (inplace_a() || inplace_b()) {
163 std::generate(y.begin(), y.end(), std::ref(u8rng));
164 } else {
165 std::fill(y.begin(), y.end(), 0xA5);
166 }
167 const uint8_t* a_data = inplace_a() ? y.data() : a.data();
168 const uint8_t* b_data = inplace_b() ? y.data() : b.data();
169
170 // Prepare quantization parameters.
171 xnn_q8_add_params quantization_params = { };
172 switch (variant) {
173 case Variant::Native:
Marat Dukhaneeaa7bd2019-10-25 17:31:25 -0700174 quantization_params = xnn_init_q8_add_params(
XNNPACK Teamb455b122019-09-27 18:10:33 -0700175 a_zero_point(), b_zero_point(), y_zero_point(),
176 a_scale() / y_scale(), b_scale() / y_scale(),
177 qmin(), qmax());
178 break;
179 case Variant::Scalar:
Marat Dukhaneeaa7bd2019-10-25 17:31:25 -0700180 quantization_params = xnn_init_scalar_q8_add_params(
XNNPACK Teamb455b122019-09-27 18:10:33 -0700181 a_zero_point(), b_zero_point(), y_zero_point(),
182 a_scale() / y_scale(), b_scale() / y_scale(),
183 qmin(), qmax());
184 break;
185 }
186 const xnn_q8_add_params scalar_quantization_params =
Marat Dukhaneeaa7bd2019-10-25 17:31:25 -0700187 xnn_init_scalar_q8_add_params(
XNNPACK Teamb455b122019-09-27 18:10:33 -0700188 a_zero_point(), b_zero_point(), y_zero_point(),
189 a_scale() / y_scale(), b_scale() / y_scale(),
190 qmin(), qmax());
191
192 // Compute reference results.
193 for (size_t i = 0; i < n(); i++) {
194 y_fp[i] = float(y_zero_point()) +
195 float(int32_t(a_data[i]) - int32_t(a_zero_point())) * (a_scale() / y_scale()) +
196 float(int32_t(b_data[i]) - int32_t(b_zero_point())) * (b_scale() / y_scale());
197 y_fp[i] = std::min<float>(y_fp[i], float(qmax()));
198 y_fp[i] = std::max<float>(y_fp[i], float(qmin()));
199 y_ref[i] = xnn_add_quantize(a_data[i], b_data[i], scalar_quantization_params);
200 }
201
202 // Call optimized micro-kernel.
203 vadd(n(), a_data, b_data, y.data(), &quantization_params);
204
205 // Verify results.
206 for (size_t i = 0; i < n(); i++) {
207 ASSERT_LE(uint32_t(y[i]), uint32_t(qmax()))
208 << "at " << i << ", n = " << n();
209 ASSERT_GE(uint32_t(y[i]), uint32_t(qmin()))
210 << "at " << i << ", n = " << n();
211 ASSERT_NEAR(float(int32_t(y[i])), y_fp[i], 0.6f)
212 << "at " << i << ", n = " << n();
213 ASSERT_EQ(uint32_t(y_ref[i]), uint32_t(y[i]))
214 << "at " << i << ", n = " << n();
215 }
216 }
217 }
218
XNNPACK Teamb455b122019-09-27 18:10:33 -0700219 private:
220 size_t n_{1};
221 bool inplace_a_{false};
222 bool inplace_b_{false};
223 float a_scale_{0.75f};
224 float b_scale_{1.25f};
225 float y_scale_{0.96875f};
226 uint8_t a_zero_point_{121};
227 uint8_t b_zero_point_{127};
228 uint8_t y_zero_point_{133};
229 uint8_t qmin_{0};
230 uint8_t qmax_{255};
231 size_t iterations_{15};
232};