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/CPP/algorithm.h" 10 #include "src/__support/FPUtil/FPBits.h" 11 #include "src/__support/macros/config.h" 12 #include "test/src/math/performance_testing/Timer.h" 13 14 #include <fstream> 15 16 namespace LIBC_NAMESPACE_DECL { 17 namespace testing { 18 19 template <typename T> class SingleInputSingleOutputPerf { 20 using FPBits = fputil::FPBits<T>; 21 using StorageType = typename FPBits::StorageType; 22 static constexpr StorageType UIntMax = 23 cpp::numeric_limits<StorageType>::max(); 24 25 public: 26 typedef T Func(T); 27 runPerfInRange(Func myFunc,Func otherFunc,StorageType startingBit,StorageType endingBit,size_t rounds,std::ofstream & log)28 static void runPerfInRange(Func myFunc, Func otherFunc, 29 StorageType startingBit, StorageType endingBit, 30 size_t rounds, std::ofstream &log) { 31 size_t n = 10'010'001; 32 if (sizeof(StorageType) <= sizeof(size_t)) 33 n = cpp::min(n, static_cast<size_t>(endingBit - startingBit)); 34 35 auto runner = [=](Func func) { 36 StorageType step = (endingBit - startingBit) / n; 37 if (step == 0) 38 step = 1; 39 [[maybe_unused]] volatile T result; 40 for (size_t i = 0; i < rounds; i++) { 41 for (StorageType bits = startingBit; bits < endingBit; bits += step) { 42 T x = FPBits(bits).get_val(); 43 result = func(x); 44 } 45 } 46 }; 47 48 Timer timer; 49 timer.start(); 50 runner(myFunc); 51 timer.stop(); 52 53 double myAverage = static_cast<double>(timer.nanoseconds()) / n / rounds; 54 log << "-- My function --\n"; 55 log << " Total time : " << timer.nanoseconds() << " ns \n"; 56 log << " Average runtime : " << myAverage << " ns/op \n"; 57 log << " Ops per second : " 58 << static_cast<uint64_t>(1'000'000'000.0 / myAverage) << " op/s \n"; 59 60 timer.start(); 61 runner(otherFunc); 62 timer.stop(); 63 64 double otherAverage = static_cast<double>(timer.nanoseconds()) / n / rounds; 65 log << "-- Other function --\n"; 66 log << " Total time : " << timer.nanoseconds() << " ns \n"; 67 log << " Average runtime : " << otherAverage << " ns/op \n"; 68 log << " Ops per second : " 69 << static_cast<uint64_t>(1'000'000'000.0 / otherAverage) << " op/s \n"; 70 71 log << "-- Average runtime ratio --\n"; 72 log << " Mine / Other's : " << myAverage / otherAverage << " \n"; 73 } 74 runPerf(Func myFunc,Func otherFunc,size_t rounds,const char * logFile)75 static void runPerf(Func myFunc, Func otherFunc, size_t rounds, 76 const char *logFile) { 77 std::ofstream log(logFile); 78 log << " Performance tests with inputs in denormal range:\n"; 79 runPerfInRange(myFunc, otherFunc, /* startingBit= */ StorageType(0), 80 /* endingBit= */ FPBits::max_subnormal().uintval(), rounds, 81 log); 82 log << "\n Performance tests with inputs in normal range:\n"; 83 runPerfInRange(myFunc, otherFunc, 84 /* startingBit= */ FPBits::min_normal().uintval(), 85 /* endingBit= */ FPBits::max_normal().uintval(), rounds, 86 log); 87 } 88 }; 89 90 } // namespace testing 91 } // namespace LIBC_NAMESPACE_DECL 92 93 #define SINGLE_INPUT_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename) \ 94 int main() { \ 95 LIBC_NAMESPACE::testing::SingleInputSingleOutputPerf<T>::runPerf( \ 96 &myFunc, &otherFunc, 1, filename); \ 97 return 0; \ 98 } 99 100 #define SINGLE_INPUT_SINGLE_OUTPUT_PERF_EX(T, myFunc, otherFunc, rounds, \ 101 filename) \ 102 { \ 103 LIBC_NAMESPACE::testing::SingleInputSingleOutputPerf<T>::runPerf( \ 104 &myFunc, &otherFunc, rounds, filename); \ 105 } 106