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 FilterbankAccumulateMicrokernelTester { 24 public: rows(size_t rows)25 inline FilterbankAccumulateMicrokernelTester& 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 iterations(size_t iterations)35 inline FilterbankAccumulateMicrokernelTester& iterations(size_t iterations) { 36 this->iterations_ = iterations; 37 return *this; 38 } 39 iterations()40 inline size_t iterations() const { 41 return this->iterations_; 42 } 43 Test(xnn_u32_filterbank_accumulate_ukernel_function filterbank_accumulate)44 void Test(xnn_u32_filterbank_accumulate_ukernel_function filterbank_accumulate) const { 45 std::random_device random_device; 46 auto rng = std::mt19937(random_device()); 47 auto u8rng = std::bind(std::uniform_int_distribution<uint16_t>(1, 10), std::ref(rng)); 48 auto u16rng = std::bind(std::uniform_int_distribution<uint16_t>(), std::ref(rng)); 49 auto u32rng = std::bind(std::uniform_int_distribution<uint32_t>(), std::ref(rng)); 50 51 std::vector<uint8_t> filterbank_widths(rows() + 1); 52 std::vector<uint64_t> output(rows()); 53 std::vector<uint64_t> output_ref(rows()); 54 55 for (size_t iteration = 0; iteration < iterations(); iteration++) { 56 std::generate(filterbank_widths.begin(), filterbank_widths.end(), std::ref(u8rng)); 57 const size_t num_channels = std::accumulate(filterbank_widths.cbegin(), filterbank_widths.cend(), 0); 58 59 std::vector<uint32_t> input(num_channels); 60 std::vector<uint16_t> weights(num_channels * 2); 61 std::generate(input.begin(), input.end(), std::ref(u32rng)); 62 std::generate(weights.begin(), weights.end(), std::ref(u16rng)); 63 std::fill(output.begin(), output.end(), UINT64_C(0xCAFEB0BADEADBEAF)); 64 65 uint64_t weight_accumulator = 0; 66 uint64_t unweight_accumulator = 0; 67 size_t i = 0; 68 for (size_t m = 0; m <= rows(); m++) { 69 const size_t weight_width = filterbank_widths[m]; 70 for (size_t n = 0; n < weight_width; n++) { 71 weight_accumulator += uint64_t(input[i]) * uint64_t(weights[i * 2]); 72 unweight_accumulator += uint64_t(input[i]) * uint64_t(weights[i * 2 + 1]); 73 i += 1; 74 } 75 if (m != 0) { 76 output_ref[m - 1] = weight_accumulator; 77 } 78 weight_accumulator = unweight_accumulator; 79 unweight_accumulator = 0; 80 } 81 82 // Call optimized micro-kernel. 83 filterbank_accumulate(rows(), input.data(), filterbank_widths.data(), weights.data(), output.data()); 84 85 // Verify results. 86 for (size_t m = 0; m < rows(); m++) { 87 ASSERT_EQ(output[m], output_ref[m]) 88 << "at row " << m << " / " << rows(); 89 } 90 } 91 } 92 93 private: 94 size_t rows_{1}; 95 size_t iterations_{15}; 96 }; 97