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 "utils/testutils/StreamWrapper.h" 11 #include "utils/testutils/Timer.h" 12 13 namespace __llvm_libc { 14 namespace testing { 15 16 template <typename T> class BinaryOpSingleOutputDiff { 17 using FPBits = fputil::FPBits<T>; 18 using UIntType = typename FPBits::UIntType; 19 static constexpr UIntType MSBIT = UIntType(1) << (8 * sizeof(UIntType) - 1); 20 static constexpr UIntType UINTMAX = (MSBIT - 1) + MSBIT; 21 22 public: 23 typedef T Func(T, T); 24 run_diff_in_range(Func myFunc,Func otherFunc,UIntType startingBit,UIntType endingBit,UIntType N,testutils::OutputFileStream & log)25 static uint64_t run_diff_in_range(Func myFunc, Func otherFunc, 26 UIntType startingBit, UIntType endingBit, 27 UIntType N, 28 testutils::OutputFileStream &log) { 29 uint64_t result = 0; 30 if (endingBit < startingBit) { 31 return result; 32 } 33 34 UIntType step = (endingBit - startingBit) / N; 35 for (UIntType bitsX = startingBit, bitsY = endingBit;; 36 bitsX += step, bitsY -= step) { 37 T x = T(FPBits(bitsX)); 38 T y = T(FPBits(bitsY)); 39 FPBits myBits = FPBits(myFunc(x, y)); 40 FPBits otherBits = FPBits(otherFunc(x, y)); 41 if (myBits.uintval() != otherBits.uintval()) { 42 result++; 43 log << " Input: " << bitsX << ", " << bitsY << " (" << x << ", " 44 << y << ")\n" 45 << " My result: " << myBits.uintval() << " (" << myBits.get_val() 46 << ")\n" 47 << "Other result: " << otherBits.uintval() << " (" 48 << otherBits.get_val() << ")\n" 49 << '\n'; 50 } 51 52 if (endingBit - bitsX < step) { 53 break; 54 } 55 } 56 return result; 57 } 58 run_perf_in_range(Func myFunc,Func otherFunc,UIntType startingBit,UIntType endingBit,UIntType N,testutils::OutputFileStream & log)59 static void run_perf_in_range(Func myFunc, Func otherFunc, 60 UIntType startingBit, UIntType endingBit, 61 UIntType N, testutils::OutputFileStream &log) { 62 auto runner = [=](Func func) { 63 volatile T result; 64 if (endingBit < startingBit) { 65 return; 66 } 67 68 UIntType step = (endingBit - startingBit) / N; 69 for (UIntType bitsX = startingBit, bitsY = endingBit;; 70 bitsX += step, bitsY -= step) { 71 T x = T(FPBits(bitsX)); 72 T y = T(FPBits(bitsY)); 73 result = func(x, y); 74 if (endingBit - bitsX < step) { 75 break; 76 } 77 } 78 }; 79 80 Timer timer; 81 timer.start(); 82 runner(myFunc); 83 timer.stop(); 84 85 double my_average = static_cast<double>(timer.nanoseconds()) / N; 86 log << "-- My function --\n"; 87 log << " Total time : " << timer.nanoseconds() << " ns \n"; 88 log << " Average runtime : " << my_average << " ns/op \n"; 89 log << " Ops per second : " 90 << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n"; 91 92 timer.start(); 93 runner(otherFunc); 94 timer.stop(); 95 96 double other_average = static_cast<double>(timer.nanoseconds()) / N; 97 log << "-- Other function --\n"; 98 log << " Total time : " << timer.nanoseconds() << " ns \n"; 99 log << " Average runtime : " << other_average << " ns/op \n"; 100 log << " Ops per second : " 101 << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n"; 102 103 log << "-- Average runtime ratio --\n"; 104 log << " Mine / Other's : " << my_average / other_average << " \n"; 105 } 106 run_perf(Func myFunc,Func otherFunc,const char * logFile)107 static void run_perf(Func myFunc, Func otherFunc, const char *logFile) { 108 testutils::OutputFileStream log(logFile); 109 log << " Performance tests with inputs in denormal range:\n"; 110 run_perf_in_range(myFunc, otherFunc, /* startingBit= */ UIntType(0), 111 /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log); 112 log << "\n Performance tests with inputs in normal range:\n"; 113 run_perf_in_range(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL, 114 /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log); 115 log << "\n Performance tests with inputs in normal range with exponents " 116 "close to each other:\n"; 117 run_perf_in_range( 118 myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(), 119 /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log); 120 } 121 run_diff(Func myFunc,Func otherFunc,const char * logFile)122 static void run_diff(Func myFunc, Func otherFunc, const char *logFile) { 123 uint64_t diffCount = 0; 124 testutils::OutputFileStream log(logFile); 125 log << " Diff tests with inputs in denormal range:\n"; 126 diffCount += run_diff_in_range( 127 myFunc, otherFunc, /* startingBit= */ UIntType(0), 128 /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log); 129 log << "\n Diff tests with inputs in normal range:\n"; 130 diffCount += run_diff_in_range( 131 myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL, 132 /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log); 133 log << "\n Diff tests with inputs in normal range with exponents " 134 "close to each other:\n"; 135 diffCount += run_diff_in_range( 136 myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(), 137 /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log); 138 139 log << "Total number of differing results: " << diffCount << '\n'; 140 } 141 }; 142 143 } // namespace testing 144 } // namespace __llvm_libc 145 146 #define BINARY_OP_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename) \ 147 int main() { \ 148 __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_diff( \ 149 &myFunc, &otherFunc, filename); \ 150 return 0; \ 151 } 152 153 #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \ 154 int main() { \ 155 __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_perf( \ 156 &myFunc, &otherFunc, filename); \ 157 return 0; \ 158 } 159