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 FilterbankSubtractMicrokernelTester { 24 public: 25 batch(size_t batch)26 inline FilterbankSubtractMicrokernelTester& batch(size_t batch) { 27 assert(batch != 0); 28 this->batch_ = batch; 29 return *this; 30 } 31 batch()32 inline size_t batch() const { 33 return this->batch_; 34 } 35 inplace(bool inplace)36 inline FilterbankSubtractMicrokernelTester& inplace(bool inplace) { 37 this->inplace_ = inplace; 38 return *this; 39 } 40 inplace()41 inline bool inplace() const { 42 return this->inplace_; 43 } 44 iterations(size_t iterations)45 inline FilterbankSubtractMicrokernelTester& iterations(size_t iterations) { 46 this->iterations_ = iterations; 47 return *this; 48 } 49 iterations()50 inline size_t iterations() const { 51 return this->iterations_; 52 } 53 Test(xnn_u32_filterbank_subtract_ukernel_function filterbank_subtract)54 void Test(xnn_u32_filterbank_subtract_ukernel_function filterbank_subtract) const { 55 std::random_device random_device; 56 auto rng = std::mt19937(random_device()); 57 auto u32rng = std::bind(std::uniform_int_distribution<uint32_t>(), std::ref(rng)); 58 const uint32_t smoothing = 655; 59 const uint32_t alternate_smoothing = 655; 60 const uint32_t one_minus_smoothing = 15729; 61 const uint32_t alternate_one_minus_smoothing = 15729; 62 const uint32_t min_signal_remaining = 819; 63 const uint32_t smoothing_bits = 0; 64 const uint32_t spectral_subtraction_bits = 14; 65 66 std::vector<uint32_t, AlignedAllocator<uint32_t, 64>> x(batch() + XNN_EXTRA_BYTES / sizeof(uint32_t)); 67 std::vector<uint32_t, AlignedAllocator<uint32_t, 64>> noise(batch() + XNN_EXTRA_BYTES / sizeof(uint32_t)); 68 std::vector<uint32_t, AlignedAllocator<uint32_t, 64>> noise_ref(batch() + XNN_EXTRA_BYTES / sizeof(uint32_t)); 69 std::vector<uint32_t, AlignedAllocator<uint32_t, 64>> y(batch() + (inplace() ? XNN_EXTRA_BYTES / sizeof(uint32_t) : 0)); 70 std::vector<uint32_t, AlignedAllocator<uint32_t, 64>> y_ref(batch()); 71 const uint32_t* x_data = inplace() ? y.data() : x.data(); 72 73 for (size_t iteration = 0; iteration < iterations(); iteration++) { 74 std::generate(x.begin(), x.end(), std::ref(u32rng)); 75 std::iota(noise.begin(), noise.end(), 0); 76 std::iota(noise_ref.begin(), noise_ref.end(), 0); 77 std::generate(y.begin(), y.end(), std::ref(u32rng)); 78 std::generate(y_ref.begin(), y_ref.end(), std::ref(u32rng)); 79 80 for (size_t n = 0; n < batch(); n += 2) { 81 const uint32_t vinput0 = x_data[n + 0]; 82 const uint32_t vinput1 = x_data[n + 1]; 83 84 uint32_t vnoise_estimate0 = noise_ref[n + 0]; 85 uint32_t vnoise_estimate1 = noise_ref[n + 1]; 86 87 // Scale up signa for smoothing filter computation. 88 const uint32_t vsignal_scaled_up0 = vinput0 << smoothing_bits; 89 const uint32_t vsignal_scaled_up1 = vinput1 << smoothing_bits; 90 91 vnoise_estimate0 = (((uint64_t) (vsignal_scaled_up0) * smoothing) + 92 ((uint64_t) (vnoise_estimate0) * one_minus_smoothing)) >> spectral_subtraction_bits; 93 vnoise_estimate1 = (((uint64_t) (vsignal_scaled_up1) * alternate_smoothing) + 94 ((uint64_t) (vnoise_estimate1) * alternate_one_minus_smoothing)) >> spectral_subtraction_bits; 95 96 noise_ref[n + 0] = vnoise_estimate0; 97 noise_ref[n + 1] = vnoise_estimate1; 98 99 // Make sure that we can't get a negative value for the signal - estimate. 100 const uint32_t estimate_scaled_up0 = std::min(vnoise_estimate0, vsignal_scaled_up0); 101 const uint32_t estimate_scaled_up1 = std::min(vnoise_estimate1, vsignal_scaled_up1); 102 const uint32_t vsubtracted0 = (vsignal_scaled_up0 - estimate_scaled_up0) >> smoothing_bits; 103 const uint32_t vsubtracted1 = (vsignal_scaled_up1 - estimate_scaled_up1) >> smoothing_bits; 104 105 const uint32_t vfloor0 = ((uint64_t) (vinput0) * min_signal_remaining) >> spectral_subtraction_bits; 106 const uint32_t vfloor1 = ((uint64_t) (vinput1) * min_signal_remaining) >> spectral_subtraction_bits; 107 const uint32_t vout0 = std::max(vsubtracted0, vfloor0); 108 const uint32_t vout1 = std::max(vsubtracted1, vfloor1); 109 110 y_ref[n + 0] = vout0; 111 y_ref[n + 1] = vout1; 112 } 113 114 // Call optimized micro-kernel. 115 filterbank_subtract(batch(), x_data, 116 smoothing, alternate_smoothing, one_minus_smoothing, alternate_one_minus_smoothing, 117 min_signal_remaining, smoothing_bits, spectral_subtraction_bits, 118 noise.data(), y.data()); 119 120 // Verify results. 121 for (size_t n = 0; n < batch(); n++) { 122 ASSERT_EQ(y[n], y_ref[n]) 123 << "at n " << n << " / " << batch(); 124 ASSERT_EQ(noise[n], noise_ref[n]) 125 << "at n " << n << " / " << batch(); 126 } 127 } 128 } 129 130 private: 131 size_t batch_{48}; 132 bool inplace_{false}; 133 size_t iterations_{15}; 134 }; 135