1 // 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 <chrono> 12 #include <cstddef> 13 #include <cstdlib> 14 #include <functional> 15 #include <random> 16 #include <vector> 17 18 #include <xnnpack.h> 19 #include <xnnpack/params.h> 20 21 22 class VScaleExpMinusMaxMicrokernelTester { 23 public: elements(size_t elements)24 inline VScaleExpMinusMaxMicrokernelTester& elements(size_t elements) { 25 assert(elements != 0); 26 this->elements_ = elements; 27 return *this; 28 } 29 elements()30 inline size_t elements() const { 31 return this->elements_; 32 } 33 scale(float scale)34 inline VScaleExpMinusMaxMicrokernelTester& scale(float scale) { 35 assert(std::isfinite(scale)); 36 assert(scale > 0); 37 this->scale_ = scale; 38 return *this; 39 } 40 scale()41 inline float scale() const { 42 return this->scale_; 43 } 44 iterations(size_t iterations)45 inline VScaleExpMinusMaxMicrokernelTester& 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_f32_vscaleexpminusmax_ukernel_function vscaleexpminusmax)54 void Test(xnn_f32_vscaleexpminusmax_ukernel_function vscaleexpminusmax) const { 55 std::random_device random_device; 56 auto rng = std::mt19937(random_device()); 57 // Choose such range that expf(x[i]) overflows, but expf(x[i] - x_max) doesn't. 58 // However, the range is still narrow enough that double-precision exp doesn't overflow. 59 auto f32rng = std::bind(std::uniform_real_distribution<float>(90.0f, 100.0f), rng); 60 61 std::vector<float> x(elements() + XNN_EXTRA_BYTES / sizeof(float)); 62 std::vector<float> y(elements()); 63 std::vector<double> y_ref(elements()); 64 for (size_t iteration = 0; iteration < iterations(); iteration++) { 65 std::generate(x.begin(), x.end(), std::ref(f32rng)); 66 67 // Compute reference results. 68 const float x_max = *std::max_element(x.begin(), x.begin() + elements()); 69 for (size_t i = 0; i < elements(); i++) { 70 y_ref[i] = double(scale()) * exp(double(x[i]) - double(x_max)); 71 } 72 73 // Call optimized micro-kernel. 74 vscaleexpminusmax(elements() * sizeof(float), x.data(), y.data(), scale(), x_max); 75 76 // Verify results. 77 for (size_t i = 0; i < elements(); i++) { 78 ASSERT_NEAR(y_ref[i], y[i], std::abs(y_ref[i]) * 1.0e-6) 79 << "elements = " << elements() << ", scale = " << scale() << ", x_max = " << x_max; 80 } 81 } 82 } 83 84 private: 85 size_t elements_{1}; 86 float scale_{1.0f}; 87 size_t iterations_{15}; 88 }; 89