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 SingleInputSingleOutputDiff { 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); 24 25 static void runDiff(Func myFunc, Func otherFunc, const char *logFile) { 26 UIntType diffCount = 0; 27 testutils::OutputFileStream log(logFile); 28 log << "Starting diff for values from 0 to " << UIntMax << '\n' 29 << "Only differing results will be logged.\n\n"; 30 for (UIntType bits = 0;; ++bits) { 31 T x = T(FPBits(bits)); 32 T myResult = myFunc(x); 33 T otherResult = otherFunc(x); 34 UIntType myBits = FPBits(myResult).uintval(); 35 UIntType otherBits = FPBits(otherResult).uintval(); 36 if (myBits != otherBits) { 37 ++diffCount; 38 log << " Input: " << bits << " (" << x << ")\n" 39 << " My result: " << myBits << " (" << myResult << ")\n" 40 << "Other result: " << otherBits << " (" << otherResult << ")\n" 41 << '\n'; 42 } 43 if (bits == UIntMax) 44 break; 45 } 46 log << "Total number of differing results: " << diffCount << '\n'; 47 } 48 49 static void runPerfInRange(Func myFunc, Func otherFunc, UIntType startingBit, 50 UIntType endingBit, 51 testutils::OutputFileStream &log) { 52 auto runner = [=](Func func) { 53 volatile T result; 54 for (UIntType bits = startingBit;; ++bits) { 55 T x = T(FPBits(bits)); 56 result = func(x); 57 if (bits == endingBit) 58 break; 59 } 60 }; 61 62 Timer timer; 63 timer.start(); 64 runner(myFunc); 65 timer.stop(); 66 67 UIntType numberOfRuns = endingBit - startingBit + 1; 68 double myAverage = static_cast<double>(timer.nanoseconds()) / numberOfRuns; 69 log << "-- My function --\n"; 70 log << " Total time : " << timer.nanoseconds() << " ns \n"; 71 log << " Average runtime : " << myAverage << " ns/op \n"; 72 log << " Ops per second : " 73 << static_cast<uint64_t>(1'000'000'000.0 / myAverage) << " op/s \n"; 74 75 timer.start(); 76 runner(otherFunc); 77 timer.stop(); 78 79 double otherAverage = 80 static_cast<double>(timer.nanoseconds()) / numberOfRuns; 81 log << "-- Other function --\n"; 82 log << " Total time : " << timer.nanoseconds() << " ns \n"; 83 log << " Average runtime : " << otherAverage << " ns/op \n"; 84 log << " Ops per second : " 85 << static_cast<uint64_t>(1'000'000'000.0 / otherAverage) << " op/s \n"; 86 87 log << "-- Average runtime ratio --\n"; 88 log << " Mine / Other's : " << myAverage / otherAverage << " \n"; 89 } 90 91 static void runPerf(Func myFunc, Func otherFunc, const char *logFile) { 92 testutils::OutputFileStream log(logFile); 93 log << " Performance tests with inputs in denormal range:\n"; 94 runPerfInRange(myFunc, otherFunc, /* startingBit= */ UIntType(0), 95 /* endingBit= */ FPBits::MAX_SUBNORMAL, log); 96 log << "\n Performance tests with inputs in normal range:\n"; 97 runPerfInRange(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL, 98 /* endingBit= */ FPBits::MAX_NORMAL, log); 99 } 100 }; 101 102 } // namespace testing 103 } // namespace __llvm_libc 104 105 #define SINGLE_INPUT_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename) \ 106 int main() { \ 107 __llvm_libc::testing::SingleInputSingleOutputDiff<T>::runDiff( \ 108 &myFunc, &otherFunc, filename); \ 109 return 0; \ 110 } 111 112 #define SINGLE_INPUT_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \ 113 int main() { \ 114 __llvm_libc::testing::SingleInputSingleOutputDiff<T>::runPerf( \ 115 &myFunc, &otherFunc, filename); \ 116 return 0; \ 117 } 118