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 25 static void run_perf_in_range(Func myFunc, Func otherFunc, 26 UIntType startingBit, UIntType endingBit, 27 UIntType N, testutils::OutputFileStream &log) { 28 auto runner = [=](Func func) { 29 volatile T result; 30 if (endingBit < startingBit) { 31 return; 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 result = func(x, y); 40 if (endingBit - bitsX < step) { 41 break; 42 } 43 } 44 }; 45 46 Timer timer; 47 timer.start(); 48 runner(myFunc); 49 timer.stop(); 50 51 double my_average = static_cast<double>(timer.nanoseconds()) / N; 52 log << "-- My function --\n"; 53 log << " Total time : " << timer.nanoseconds() << " ns \n"; 54 log << " Average runtime : " << my_average << " ns/op \n"; 55 log << " Ops per second : " 56 << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n"; 57 58 timer.start(); 59 runner(otherFunc); 60 timer.stop(); 61 62 double other_average = static_cast<double>(timer.nanoseconds()) / N; 63 log << "-- Other function --\n"; 64 log << " Total time : " << timer.nanoseconds() << " ns \n"; 65 log << " Average runtime : " << other_average << " ns/op \n"; 66 log << " Ops per second : " 67 << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n"; 68 69 log << "-- Average runtime ratio --\n"; 70 log << " Mine / Other's : " << my_average / other_average << " \n"; 71 } 72 73 static void run_perf(Func myFunc, Func otherFunc, const char *logFile) { 74 testutils::OutputFileStream log(logFile); 75 log << " Performance tests with inputs in denormal range:\n"; 76 run_perf_in_range(myFunc, otherFunc, /* startingBit= */ UIntType(0), 77 /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log); 78 log << "\n Performance tests with inputs in normal range:\n"; 79 run_perf_in_range(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL, 80 /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log); 81 log << "\n Performance tests with inputs in normal range with exponents " 82 "close to each other:\n"; 83 run_perf_in_range( 84 myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(), 85 /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log); 86 } 87 }; 88 89 } // namespace testing 90 } // namespace __llvm_libc 91 92 #define BINARY_OP_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename) \ 93 int main() { \ 94 __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_diff( \ 95 &myFunc, &otherFunc, filename); \ 96 return 0; \ 97 } 98 99 #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \ 100 int main() { \ 101 __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_perf( \ 102 &myFunc, &otherFunc, filename); \ 103 return 0; \ 104 } 105