blob: abfc4aa7169a257ae60e557c10729ccc41cb5bb5 [file] [log] [blame]
Marat Dukhan35dacfb2019-11-07 19:18:16 -08001// Copyright 2019 Google LLC
2//
3// This source code is licensed under the BSD-style license found in the
4// LICENSE file in the root directory of this source tree.
5
6#pragma once
7
8#include <gtest/gtest.h>
9
10#include <algorithm>
11#include <cassert>
12#include <cmath>
13#include <cstddef>
Marat Dukhan9fab3f92019-11-08 14:55:19 -080014#include <cstdint>
Marat Dukhan35dacfb2019-11-07 19:18:16 -080015#include <functional>
16#include <random>
17#include <vector>
18
19#include <xnnpack.h>
20#include <xnnpack/AlignedAllocator.h>
21#include <xnnpack/params.h>
22
23
24class BilinearMicrokernelTester {
25 public:
26 inline BilinearMicrokernelTester& pixels(uint32_t pixels) {
27 assert(pixels >= 1);
28 this->pixels_ = pixels;
29 return *this;
30 }
31
32 inline uint32_t pixels() const {
33 return this->pixels_;
34 }
35
36 inline BilinearMicrokernelTester& channels(uint32_t channels) {
37 assert(channels >= 1);
38 this->channels_ = channels;
39 return *this;
40 }
41
42 inline uint32_t channels() const {
43 return this->channels_;
44 }
45
Marat Dukhan9fab3f92019-11-08 14:55:19 -080046 inline BilinearMicrokernelTester& input_offset(uint32_t input_offset) {
47 this->input_offset_ = input_offset;
48 return *this;
49 }
50
51 inline uint32_t input_offset() const {
52 return this->input_offset_;
53 }
54
Marat Dukhan35dacfb2019-11-07 19:18:16 -080055 inline BilinearMicrokernelTester& output_stride(uint32_t output_stride) {
56 assert(output_stride != 0);
57 this->output_stride_ = output_stride;
58 return *this;
59 }
60
61 inline uint32_t output_stride() const {
62 if (this->output_stride_ == 0) {
63 return channels();
64 } else {
65 assert(this->output_stride_ >= channels());
66 return this->output_stride_;
67 }
68 }
69
70 inline BilinearMicrokernelTester& iterations(size_t iterations) {
71 this->iterations_ = iterations;
72 return *this;
73 }
74
75 inline size_t iterations() const {
76 return this->iterations_;
77 }
78
79 void Test(xnn_f32_bilinear_ukernel_function bilinear) const {
80 std::random_device random_device;
81 auto rng = std::mt19937(random_device());
82 auto f32rng = std::bind(std::uniform_real_distribution<float>(0.0f, 1.0f), rng);
83
84 std::vector<const float*> indirection(pixels() * 4);
85 std::vector<float> input(XNN_EXTRA_BYTES / sizeof(float) + indirection.size() * channels());
Marat Dukhan9594db02019-12-05 14:32:37 -080086 std::vector<float, AlignedAllocator<float, 64>> packed_weights(pixels() * 2);
Marat Dukhan35dacfb2019-11-07 19:18:16 -080087 std::vector<float> output((pixels() - 1) * output_stride() + channels());
88 std::vector<float> output_ref(pixels() * channels());
89
90 for (size_t iteration = 0; iteration < iterations(); iteration++) {
91 std::generate(input.begin(), input.end(), std::ref(f32rng));
92 std::generate(packed_weights.begin(), packed_weights.end(), std::ref(f32rng));
93 std::fill(output.begin(), output.end(), nanf(""));
94
95 for (size_t i = 0; i < indirection.size(); i++) {
Marat Dukhan9fab3f92019-11-08 14:55:19 -080096 indirection[i] = input.data() + i * channels() - input_offset();
Marat Dukhan35dacfb2019-11-07 19:18:16 -080097 }
98 std::shuffle(indirection.begin(), indirection.end(), rng);
99
100 // Compute reference results.
101 for (size_t i = 0; i < pixels(); i++) {
102 for (size_t c = 0; c < channels(); c++) {
103 const float alpha_h = packed_weights[i * 2 + 0];
104 const float alpha_v = packed_weights[i * 2 + 1];
105 output_ref[i * channels() + c] =
Marat Dukhan9fab3f92019-11-08 14:55:19 -0800106 indirection[i * 4 + 0][c + input_offset()] * (1.0f - alpha_h) * (1.0f - alpha_v) +
107 indirection[i * 4 + 1][c + input_offset()] * alpha_h * (1.0f - alpha_v) +
108 indirection[i * 4 + 2][c + input_offset()] * (1.0f - alpha_h) * alpha_v +
109 indirection[i * 4 + 3][c + input_offset()] * alpha_h * alpha_v;
Marat Dukhan35dacfb2019-11-07 19:18:16 -0800110 }
111 }
112
113 // Call optimized micro-kernel.
114 bilinear(
115 pixels(), channels() * sizeof(float),
Marat Dukhan9fab3f92019-11-08 14:55:19 -0800116 indirection.data(), input_offset() * sizeof(float),
117 packed_weights.data(), output.data(),
Marat Dukhan35dacfb2019-11-07 19:18:16 -0800118 (output_stride() - channels()) * sizeof(float));
119
120 // Verify results.
121 for (size_t i = 0; i < pixels(); i++) {
122 for (size_t c = 0; c < channels(); c++) {
123 ASSERT_NEAR(
124 output_ref[i * channels() + c],
125 output[i * output_stride() + c],
126 std::abs(output_ref[i * channels() + c]) * 1.0e-5)
127 << "i = " << i << ", channel = " << c;
128 }
129 }
130 }
131 }
132
133 private:
134 uint32_t channels_{1};
135 uint32_t pixels_{1};
136 uint32_t output_stride_{0};
Marat Dukhan9fab3f92019-11-08 14:55:19 -0800137 uint32_t input_offset_{0};
Marat Dukhan35dacfb2019-11-07 19:18:16 -0800138 size_t iterations_{3};
139};