1 // Copyright 2022 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> 14 #include <cstdlib> 15 #include <random> 16 #include <vector> 17 18 #include <xnnpack.h> 19 #include <xnnpack/aligned-allocator.h> 20 #include <xnnpack/microfnptr.h> 21 22 23 class WindowMicrokernelTester { 24 public: rows(size_t rows)25 inline WindowMicrokernelTester& rows(size_t rows) { 26 assert(rows != 0); 27 this->rows_ = rows; 28 return *this; 29 } 30 rows()31 inline size_t rows() const { 32 return this->rows_; 33 } 34 batch(size_t batch)35 inline WindowMicrokernelTester& batch(size_t batch) { 36 assert(batch != 0); 37 this->batch_ = batch; 38 return *this; 39 } 40 batch()41 inline size_t batch() const { 42 return this->batch_; 43 } 44 shift(uint32_t shift)45 inline WindowMicrokernelTester& shift(uint32_t shift) { 46 assert(shift < 32); 47 this->shift_ = shift; 48 return *this; 49 } 50 shift()51 inline uint32_t shift() const { 52 return this->shift_; 53 } 54 inplace(bool inplace)55 inline WindowMicrokernelTester& inplace(bool inplace) { 56 this->inplace_ = inplace; 57 return *this; 58 } 59 inplace()60 inline bool inplace() const { 61 return this->inplace_; 62 } 63 iterations(size_t iterations)64 inline WindowMicrokernelTester& iterations(size_t iterations) { 65 this->iterations_ = iterations; 66 return *this; 67 } 68 iterations()69 inline size_t iterations() const { 70 return this->iterations_; 71 } 72 Test(xnn_s16_window_ukernel_function window)73 void Test(xnn_s16_window_ukernel_function window) const { 74 std::random_device random_device; 75 auto rng = std::mt19937(random_device()); 76 auto i16rng = std::bind(std::uniform_int_distribution<int16_t>(), std::ref(rng)); 77 78 std::vector<int16_t> x(batch() * rows() + XNN_EXTRA_BYTES / sizeof(int16_t)); 79 std::vector<int16_t, AlignedAllocator<int16_t, 64>> w(batch() + XNN_EXTRA_BYTES / sizeof(int16_t)); 80 std::vector<int16_t> y(batch() * rows() + (inplace() ? XNN_EXTRA_BYTES / sizeof(int16_t) : 0)); 81 std::vector<int16_t> y_ref(batch() * rows()); 82 const int16_t* x_data = inplace() ? y.data() : x.data(); 83 84 for (size_t iteration = 0; iteration < iterations(); iteration++) { 85 std::generate(x.begin(), x.end(), std::ref(i16rng)); 86 std::generate(w.begin(), w.end(), std::ref(i16rng)); 87 std::generate(y.begin(), y.end(), std::ref(i16rng)); 88 std::generate(y_ref.begin(), y_ref.end(), std::ref(i16rng)); 89 90 // Compute reference results. 91 for (size_t m = 0; m < rows(); m++) { 92 for (size_t n = 0; n < batch(); n++) { 93 const int16_t x_value = x_data[m * batch() + n]; 94 int32_t value = ((int32_t) x_value * (int32_t) w[n]) >> shift(); 95 value = std::min(value, (int32_t) std::numeric_limits<int16_t>::max()); 96 value = std::max(value, (int32_t) std::numeric_limits<int16_t>::min()); 97 y_ref[m * batch() + n] = value; 98 } 99 } 100 101 // Call optimized micro-kernel. 102 window(rows(), batch(), x_data, w.data(), y.data(), shift()); 103 104 // Verify results. 105 for (size_t m = 0; m < rows(); m++) { 106 for (size_t n = 0; n < batch(); n++) { 107 ASSERT_EQ(y[m * batch() + n], y_ref[m * batch() + n]) 108 << "at row " << m << " / " << rows() 109 << ", shift " << shift() 110 << ", batch " << n << " / " << batch(); 111 } 112 } 113 } 114 } 115 116 private: 117 size_t rows_{1}; 118 size_t batch_{1}; 119 uint32_t shift_{12}; 120 bool inplace_{false}; 121 size_t iterations_{15}; 122 }; 123