1 //===-- Common utility class for differential analysis --------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "src/__support/FPUtil/FPBits.h" 10 #include "test/src/math/performance_testing/Timer.h" 11 12 #include <cstddef> 13 #include <fstream> 14 15 namespace LIBC_NAMESPACE { 16 namespace testing { 17 18 template <typename T> class BinaryOpSingleOutputPerf { 19 using FPBits = fputil::FPBits<T>; 20 using StorageType = typename FPBits::StorageType; 21 static constexpr StorageType UIntMax = 22 cpp::numeric_limits<StorageType>::max(); 23 24 public: 25 typedef T Func(T, T); 26 run_perf_in_range(Func myFunc,Func otherFunc,StorageType startingBit,StorageType endingBit,size_t N,size_t rounds,std::ofstream & log)27 static void run_perf_in_range(Func myFunc, Func otherFunc, 28 StorageType startingBit, StorageType endingBit, 29 size_t N, size_t rounds, std::ofstream &log) { 30 if (endingBit - startingBit < N) 31 N = endingBit - startingBit; 32 33 auto runner = [=](Func func) { 34 volatile T result; 35 if (endingBit < startingBit) { 36 return; 37 } 38 39 StorageType step = (endingBit - startingBit) / N; 40 for (size_t i = 0; i < rounds; i++) { 41 for (StorageType bitsX = startingBit, bitsY = endingBit;; 42 bitsX += step, bitsY -= step) { 43 T x = FPBits(bitsX).get_val(); 44 T y = FPBits(bitsY).get_val(); 45 result = func(x, y); 46 if (endingBit - bitsX < step) { 47 break; 48 } 49 } 50 } 51 }; 52 53 Timer timer; 54 timer.start(); 55 runner(myFunc); 56 timer.stop(); 57 58 double my_average = static_cast<double>(timer.nanoseconds()) / N / rounds; 59 log << "-- My function --\n"; 60 log << " Total time : " << timer.nanoseconds() << " ns \n"; 61 log << " Average runtime : " << my_average << " ns/op \n"; 62 log << " Ops per second : " 63 << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n"; 64 65 timer.start(); 66 runner(otherFunc); 67 timer.stop(); 68 69 double other_average = 70 static_cast<double>(timer.nanoseconds()) / N / rounds; 71 log << "-- Other function --\n"; 72 log << " Total time : " << timer.nanoseconds() << " ns \n"; 73 log << " Average runtime : " << other_average << " ns/op \n"; 74 log << " Ops per second : " 75 << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n"; 76 77 log << "-- Average runtime ratio --\n"; 78 log << " Mine / Other's : " << my_average / other_average << " \n"; 79 } 80 run_perf(Func myFunc,Func otherFunc,int rounds,const char * logFile)81 static void run_perf(Func myFunc, Func otherFunc, int rounds, 82 const char *logFile) { 83 std::ofstream log(logFile); 84 log << " Performance tests with inputs in denormal range:\n"; 85 run_perf_in_range(myFunc, otherFunc, /* startingBit= */ StorageType(0), 86 /* endingBit= */ FPBits::max_subnormal().uintval(), 87 1'000'001, rounds, log); 88 log << "\n Performance tests with inputs in normal range:\n"; 89 run_perf_in_range(myFunc, otherFunc, 90 /* startingBit= */ FPBits::min_normal().uintval(), 91 /* endingBit= */ FPBits::max_normal().uintval(), 92 1'000'001, rounds, log); 93 log << "\n Performance tests with inputs in normal range with exponents " 94 "close to each other:\n"; 95 run_perf_in_range(myFunc, otherFunc, 96 /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(), 97 /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 98 1'000'001, rounds, log); 99 } 100 run_diff(Func myFunc,Func otherFunc,const char * logFile)101 static void run_diff(Func myFunc, Func otherFunc, const char *logFile) { 102 uint64_t diffCount = 0; 103 std::ofstream log(logFile); 104 log << " Diff tests with inputs in denormal range:\n"; 105 diffCount += run_diff_in_range( 106 myFunc, otherFunc, /* startingBit= */ StorageType(0), 107 /* endingBit= */ FPBits::max_subnormal().uintval(), 1'000'001, log); 108 log << "\n Diff tests with inputs in normal range:\n"; 109 diffCount += run_diff_in_range( 110 myFunc, otherFunc, 111 /* startingBit= */ FPBits::min_normal().uintval(), 112 /* endingBit= */ FPBits::max_normal().uintval(), 100'000'001, log); 113 log << "\n Diff tests with inputs in normal range with exponents " 114 "close to each other:\n"; 115 diffCount += run_diff_in_range( 116 myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(), 117 /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log); 118 119 log << "Total number of differing results: " << diffCount << '\n'; 120 } 121 }; 122 123 } // namespace testing 124 } // namespace LIBC_NAMESPACE 125 126 #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \ 127 int main() { \ 128 LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf<T>::run_perf( \ 129 &myFunc, &otherFunc, 1, filename); \ 130 return 0; \ 131 } 132 133 #define BINARY_OP_SINGLE_OUTPUT_PERF_EX(T, myFunc, otherFunc, rounds, \ 134 filename) \ 135 { \ 136 LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf<T>::run_perf( \ 137 &myFunc, &otherFunc, rounds, filename); \ 138 LIBC_NAMESPACE::testing::BinaryOpSingleOutputPerf<T>::run_perf( \ 139 &myFunc, &otherFunc, rounds, filename); \ 140 } 141